ref: e259f9d582ce2cb4ee427efe0f366504794b5095
parent: e283e9f2622b7df01c315adf41325cbf1e087727
author: B. Wilson <x@wilsonb.com>
date: Sun Jul 13 09:43:03 EDT 2025
Make text non-blocking and expose server event queue
--- a/ridefs.c
+++ b/ridefs.c
@@ -6,27 +6,57 @@
#include <stdio.h>
#include <json.h>
-
+typedef struct Evfifo Evfifo;
+typedef struct Qfile Qfile;
typedef struct Client Client;
+typedef struct Rmsg Rmsg;
typedef struct Rfid Rfid;
+struct Evfifo {
+ int in, out;
+ int open;
+ int pid; /* event handler */
+};
+
+struct Qfile {
+ RWLock;
+ int fd;
+};
+
struct Client {
Ref;
+ QLock;
+ /* config */
char *addr;
- /* tcp i/o */
- QLock rl, wl;
- Req *rreq, *wreq;
- int rpid, wpid;
- int fd, cfd;
+ /* internal */
+ Evfifo ev;
+ Qfile qtext, tdata, tctl;
+ char *qctl, *qinfo;
+};
- /* buffers */
- char *qinfo, *qctl;
- char *r; /* response */
- int rn, roff;
+enum {
+ RHandshake,
+ THandshake,
+ TIdentify,
+ RIdentify,
+ TExecute,
+ RAppendSessionOutput,
+ RSetPromptType,
+ CMDCOUNT
};
+static char *cmdtab[] = {
+ "SupportedProtocols",
+ "UsingProtocol",
+ "Identify",
+ "ReplyIdentify",
+ "Execute",
+ "AppendSessionOutput",
+ "SetPromptType",
+};
+
enum {
Qroot,
Qrctl,
@@ -33,25 +63,30 @@
Qclone,
Qclient,
Qctl,
- Qdata,
+ Qtext,
Qinfo,
+ Qevent,
QCOUNT
};
static char *nametab[] = {
- "/",
+ ".",
"ctl",
"clone",
nil,
"ctl",
- "data",
+ "text",
"info",
+ "event",
};
struct Rfid {
Ref;
+ RWLock *l;
+ void (*unlock)(RWLock*);
int kind;
int client;
+ int pid;
};
#define VERSION 0
@@ -61,7 +96,6 @@
static char *defport;
static char *user;
static char *qrctl;
-static long bufsz;
static long time0;
static uint nclients;
static int debug;
@@ -103,13 +137,14 @@
int
genqrctl(void){
- snprintf(qrctl, bufsz,
- "version %i\n"
- "bufsz %u\n"
- "nclients %u\n"
- "debug %i\n"
+ if(qrctl)
+ free(qrctl);
+ qrctl = smprint(
+ "version %d\n"
+ "nclients %d\n"
+ "debug %d\n"
"defport %s\n",
- VERSION, bufsz, nclients, debug,defport);
+ VERSION, nclients, debug, defport);
return strlen(qrctl);
}
@@ -119,8 +154,10 @@
Client *c;
c = clientref(client);
- snprintf(c->qctl, bufsz,
- "%i\n"
+ if(c->qctl)
+ free(c->qctl);
+ c->qctl = smprint(
+ "%d\n"
"connect %s\n",
client, c->addr);
@@ -140,10 +177,6 @@
c = &cpool[i];
incref(c);
- c->r = ecalloc(bufsz);
- c->rn = -1;
- c->qctl = ecalloc(bufsz);
- c->qinfo = ecalloc(bufsz);
genqctl(i);
return i;
@@ -154,14 +187,17 @@
Client *c;
c = clientref(i);
- if(c == nil || decref(c))
+ if(decref(c))
return;
-
- if(c->fd) close(c->fd);
- if(c->cfd) close(c->cfd);
+
+ if(c->ev.in) close(c->ev.in);
+ if(c->ev.out) close(c->ev.out);
+ if(c->ev.pid > 0) postnote(PNPROC, c->ev.pid, "kill");
+ if(c->qtext.fd) close(c->qtext.fd);
+ if(c->tdata.fd) close(c->tdata.fd);
+ if(c->tctl.fd) close(c->tctl.fd);
if(c->qctl) free(c->qctl);
if(c->qinfo) free(c->qinfo);
- if(c->r) free(c->r);
memset(c, 0, sizeof(*c));
}
@@ -180,61 +216,135 @@
}
}
+char*
+jsonesc(char *s){
+ char c, *b;
+ int i, sz;
+
+ sz = 32;
+ b = ecalloc(32);
+ for(i = 0, c = s[i]; c != '\0'; c = s[++i]){
+ if(i == sz-7){ sz *= 2; b = realloc(b, sz); }
+ if(c >= 0 && c < 32 || c == '"' || c == '\\')
+ sprintf(&b[i], "\\u%04x", c);
+ else
+ b[i] = c;
+ }
+
+ return b;
+}
+
long
-writemsg(int fd, void *pld, long n){
- char *r;
- int len;
+writecmd(int fd, int cmd, ...){
+ va_list v;
+ char *j, *s;
+ long n;
+ char **strv;
+ int i, strc;
- len = n+8;
- r = ecalloc(len);
- r[0] = 24>>len & 0xff;
- r[1] = 16>>len & 0xff;
- r[2] = 8>>len & 0xff;
- r[3] = len & 0xff;
- r[4] = 'R';
- r[5] = 'I';
- r[6] = 'D';
- r[7] = 'E';
- memcpy(&r[8], pld, n);
- write(fd, r, len);
+ j = nil;
+ strc = 0;
+ strv = nil;
+ va_start(v, cmd);
+ switch(cmd){
+ case THandshake:
+ j = vsmprint("%d", v);
+ break;
+ case TIdentify:
+ j = vsmprint("{\"apiVersion\":%d,\"identity\":%d}", v);
+ break;
+ case TExecute:
+ strc = 1;
+ strv = ecalloc(strc * sizeof(*strv));
+ strv[0] = jsonesc(va_arg(v, char*));
+ j = smprint("{\"text\":\"%s\",\"trace\":%d}", strv[0], va_arg(v, int));
+ break;
+ }
+ va_end(v);
+ switch(cmd){
+ case THandshake: s = smprint("%s=%s", cmdtab[cmd], j); break;
+ default: s = smprint("[\"%s\",%s]", cmdtab[cmd], j); break;
+ }
+
+ n = 8 + strlen(s);
+ fprint(fd, "%c%c%c%cRIDE%s",
+ (char)(24>>n & 0xff),
+ (char)(16>>n & 0xff),
+ (char)(8>>n & 0xff),
+ (char)(n & 0xff),
+ s);
+
+ if(debug)
+ fprint(2, "ridefs: T: %s\n", s);
+ for(i = 0; i < strc; i++)
+ free(strv[i]);
+ if(strv) free(strv);
+ free(j);
+ free(s);
return n;
}
long
-readmsg(int fd, void **pld){
- ulong len;
- int e;
- char buf[9];
+readcmd(int fd, int *cmd, JSON **arg){
+ JSON *j, *v;
+ char *pld, buf[9];
+ long n;
+ int i, ret;
- if(0 > (e = readn(fd, buf, 8)))
- return e;
-
+ if((n = readn(fd, buf, 8)) < 0)
+ return n;
buf[9] = '\0';
- if(0 != (e = strcmp(&buf[4], "RIDE")))
- return e;
+ if((n = strcmp(&buf[4], "RIDE")) != 0)
+ return n;
- len = buf[0]<<24 & 0xff000000;
- len |= buf[1]<<16 & 0x00ff0000;
- len |= buf[2]<<8 & 0x0000ff00;
- len |= buf[3] & 0x000000ff;
- len -= 8; /* len + magic */
- len += 1; /* trailing string null */
- *pld = ecalloc(len);
- if(0 > (e = readn(fd, *pld, len-1))){
- free(*pld);
- return e;
+ n = buf[0]<<24 & 0xff000000;
+ n |= buf[1]<<16 & 0x00ff0000;
+ n |= buf[2]<<8 & 0x0000ff00;
+ n |= buf[3] & 0x000000ff;
+ n -= 8; /* len + magic */
+ pld = ecalloc(n+32); /* XXX: normalization overhead */
+ if((n = readn(fd, pld, n)) < 0)
+ return n;
+
+ /* Normalize bespoke handshake payload to JSON */
+ if((j = jsonparse(pld)) == nil)
+ if(sscanf(pld, "SupportedProtocols=%i", &i) == 1){
+ sprintf(pld, "[\"SupportedProtocols\",%i]", i);
+ j = jsonparse(pld);
}
+ free(pld);
- return len;
+ ret = -1;
+ if(j->t != JSONArray || j->first == nil)
+ goto end;
+ v = j->first->val;
+ if(v->t != JSONString)
+ goto end;
+ for(*cmd = 0; *cmd < CMDCOUNT; (*cmd)++)
+ if(strcmp(v->s, cmdtab[*cmd]) == 0)
+ break;
+ if(*cmd == CMDCOUNT)
+ goto end;
+
+ if(debug)
+ fprint(2, "ridefs: R: %J\n", j);
+ *arg = j->first->next->val;
+ j->first->next->val = nil;
+ ret = n;
+
+ end:
+ jsonfree(j);
+ return ret;
}
-char *
+char*
rideinit(char *addr, int *fd, int *cfd, char **info){
- int i;
+ int i, cmd;
char *b;
- JSON *j, *v;
+ JSON *j;
JSONEl *d;
+ FILE *f;
b = netmkaddr(addr, "tcp", defport);
if(debug)
@@ -242,47 +352,87 @@
if((*fd = dial(b, nil, nil, cfd)) < 0)
return "failed to dial";
- if(0 > readmsg(*fd, &b))
+ if(readcmd(*fd, &cmd, &j) < 0)
return "failed to read handshake";
- if(0 != strcmp(b, "SupportedProtocols=2"))
+ if(cmd != RHandshake || j->n != 2)
return "unrecognized protocol";
- free(b);
-
- b = "UsingProtocol=2";
- if(0 > writemsg(*fd, b, strlen(b)))
+ if(writecmd(*fd, THandshake, 2) < 0)
return "failed to write handshake";
-
- b = "[\"Identify\",{\"apiVersion\":1,\"identity\":1}]";
- if(0 > writemsg(*fd, b, strlen(b)))
+ jsonfree(j);
+
+ if(writecmd(*fd, TIdentify, 1, 1) < 0)
return "failed to send identification message";
+ if(readcmd(*fd, &cmd, &j) < 0 || cmd != RIdentify)
+ return "failed to receive identification reply";
- if(0 > readmsg(*fd, &b))
- return "failed to receive identification message";
- if(nil == (j = jsonparse(b)))
- return "failed to parse identification message";
- free(b);
- if(j == nil || j->t != JSONArray || nil == j->first)
- return "unrecognized reply";
- v = j->first->val;
- if(v->t != JSONString || 0 != strcmp(v->s, "ReplyIdentify"))
- return "unexpected identification reply";
- if(nil == (d = j->first->next) || d->val->t != JSONObject)
- return "malformed identification reply";
-
- for(i = 0, d = d->val->first; d != nil; d = d->next){
+ f = sopenw();
+ for(i = 0, d = j->first; d != nil; d = d->next){
switch(d->val->t){
case JSONBool:
case JSONNumber:
- i += snprintf(*info+i, bufsz-i, "%s %i\n", d->name, d->val->n); break;
+ i += fprintf(f, "%s %lld\n", d->name, d->val->n); break;
case JSONString:
- i += snprintf(*info+i, bufsz-i, "%s %s\n", d->name, d->val->s); break;
+ i += fprintf(f, "%s %s\n", d->name, d->val->s); break;
}
}
-
+ *info = estrdup(f->buf);
+ fclose(f);
jsonfree(j);
+
return nil;
}
+void
+handlemsgs(int in, Evfifo *ev, Qfile *out){
+ JSON *j, *v;
+ char *s;
+ int cmd;
+ vlong off;
+
+ while(1){
+ s = nil;
+ readcmd(in, &cmd, &j);
+ switch(cmd){
+ case RAppendSessionOutput:
+ v = jsonbyname(j, "type");
+ switch((int)v->n){
+ case 11: /* echoed input */
+ case 14: /* input line */
+ goto end;
+ break;
+ }
+ v = jsonbyname(j, "result");
+ s = v->s;
+ break;
+ case RSetPromptType:
+ v = jsonbyname(j, "type");
+ switch((int)v->n){
+ case 0: break; /* no prompt */
+ case 1: s = " "; break; /* normal prompt */
+ case 2: s = "⎕:\n "; break; /* ⎕ input */
+ case 3: break; /* line editor */
+ case 4: s = ""; break; /* ⍞ input */
+ case 5: break; /* unforeseen */
+ }
+ break;
+ }
+
+ if(s == nil)
+ continue;
+
+ off = seek(out->fd, 0, 1);
+ wlock(out);
+ fprint(out->fd, s);
+ wunlock(out);
+
+ if(ev->open > 0)
+ fprint(ev->in, "t%ld %lld\n", strlen(s), off);
+
+ end:
+ jsonfree(j);
+ }
+}
+
long
writeqrctl(char *b, long){
char *s;
@@ -290,8 +440,6 @@
sprintf(sep, " \t\n\f\r\v");
for(s = strtok(b, sep); s != nil; s = strtok(nil, sep)){
- if(strcmp(s, "bufsz") == 0)
- bufsz = strtoul(strtok(nil, sep), nil, 0);
if(strcmp(s, "nclients") == 0)
nclients = strtoul(strtok(nil, sep), nil, 0);
if(strcmp(s, "debug") == 0)
@@ -326,6 +474,7 @@
void
mkdirent(Dir *d, int kind, int client){
Client *c;
+ Dir *dir;
char *nm;
memset(d, 0, sizeof(*d));
@@ -347,22 +496,28 @@
c = clientref(client);
switch(kind){
- case Qrctl: d->mode = 0666; break;
+ case Qrctl: d->mode = 0666; break;
case Qctl:
- case Qdata: d->mode = 0666; break;
+ case Qtext: d->mode = 0666; break;
}
switch(kind){
case Qrctl: d->length = strlen(qrctl); break;
case Qctl: d->length = strlen(c->qctl); break;
- case Qdata: d->length = 0; break;
case Qinfo: d->length = strlen(c->qinfo); break;
+ case Qtext:
+ if(c->qtext.fd){
+ dir = dirfstat(c->qtext.fd);
+ d->length = dir->length;
+ free(dir);
+ } else
+ d->length = 0;
+ break;
}
}
int
genqroot(int i, Dir *d, void*){
- static int n;
int j;
i += Qroot + 1;
@@ -370,12 +525,9 @@
mkdirent(d, i, -1);
} else {
i -= Qclient;
- if(i == 0)
- n = 0;
- for(j = n; j < nclients && cpool[j].ref == 0; j++);
+ for(j = i; j < nclients && cpool[j].ref == 0; j++);
if(j == nclients)
return -1;
- n++;
mkdirent(d, Qclient, j);
}
@@ -385,12 +537,20 @@
int
genqclient(int i, Dir *d, void *aux){
int client;
+ Client *c;
client = (vlong)aux;
+ c = clientref(client);
+
i += Qclient + 1;
- if(i >= QCOUNT)
+ if(i >= Qtext && c->addr == nil) i++;
+ if(i >= Qinfo && c->qinfo == nil) i++;
+ if(i >= Qevent && c->ev.in == 0) i++;
+ if(i == QCOUNT)
return -1;
+
mkdirent(d, i, client);
+
return 0;
}
@@ -397,12 +557,20 @@
static void
fsdestroyfid(Fid *fid){
Rfid *f;
+ Client *c;
- f = fid->aux;
- if(f == nil)
+ if((f = fid->aux) == nil)
return;
- rmclient(f->client);
+ if(c = clientref(f->client))
+ switch(f->kind){
+ case Qevent: c->ev.open -= c->ev.open ? 1 : 0; break;
+ }
+
+ if(c)
+ rmclient(f->client);
+
+
free(f);
}
@@ -438,6 +606,8 @@
char *err;
Rfid *f;
Client *c;
+ char *s;
+ int ppid;
f = r->fid->aux;
c = clientref(f->client);
@@ -444,51 +614,52 @@
err = nil;
switch(f->kind){
case Qclone:
- if((f->client = mkclient()) == -1){
- respond(r, "reached client limit");
- return;
- }
+ if((f->client = mkclient()) < 0)
+ err = "reached client limit";
+ if(err)
+ break;
f->kind = Qctl;
mkqid(&r->ofcall.qid, f->kind, f->client);
r->fid->qid = r->ofcall.qid;
-
- respond(r, nil);
break;
- case Qdata:
- if(nil == c->addr){
- respond(r, "no server set");
- return;
- }
-
- qlock(&c->wl);
- incref(&r->ref);
- c->wreq = r;
- switch(c->wpid = rfork(RFPROC|RFNOWAIT|RFMEM)){
+ case Qtext:
+ ppid = getpid();
+ switch(f->pid = rfork(RFPROC|RFNOWAIT|RFMEM)){
case 0:
- if(!c->fd)
- err = rideinit(c->addr, &c->fd, &c->cfd, &c->qinfo);
- c->wpid = 0;
- c->wreq = nil;
- decref(&r->ref);
+ f->l = &c->qtext;
+ f->unlock = wunlock;
+ wlock(f->l);
+ if(c->tdata.fd == 0){
+ if(err = rideinit(c->addr, &c->tdata.fd, &c->tctl.fd, &c->qinfo))
+ goto done;
+ switch(c->ev.pid = rfork(RFPROC|RFNOWAIT|RFMEM)){
+ case 0:
+ s = smprint("/tmp/ride.%d.%d", ppid, f->client);
+ c->qtext.fd = create(s, ORDWR|ORCLOSE, DMREAD|DMAPPEND);
+ pipe(&c->ev.in);
+ handlemsgs(c->tdata.fd, &c->ev, &c->qtext);
+ free(s);
+ return;
+ case -1: err = "unable to fork message handler"; break;
+ default: break;
+ }
+ }
+
+ done:
+ wunlock(f->l);
respond(r, err);
- qunlock(&c->wl);
exits(nil);
- case -1:
- respond(r, "failed to init ride");
- c->wpid = 0;
- c->wreq = nil;
- decref(&r->ref);
- qunlock(&c->wl);
- break;
- default:
- return;
+ case -1: err = "failed to init ride"; break;
+ default: return;
}
break;
- default:
- respond(r, nil);
+ case Qevent: qlock(c); c->ev.open++; qunlock(c); break;
+ default: break;
}
+
+ respond(r, err);
}
static void
@@ -495,68 +666,43 @@
fsread(Req *r){
Rfid *f;
Client *c;
- char *buf, *err;
- int n, off;
+ char *err;
+ int fd;
- buf = err = nil;
+ err = nil;
f = r->fid->aux;
c = clientref(f->client);
switch(f->kind){
case Qroot: dirread9p(r, genqroot, nil); break;
- case Qrctl: buf = estrdup(qrctl); break;
- case Qclone: err = "read prohibited"; break;
+ case Qrctl: readstr(r, qrctl); break;
case Qclient: dirread9p(r, genqclient, (void*)f->client); break;
- case Qctl: buf = estrdup(c->qctl); break;
- case Qinfo: buf = estrdup(c->qinfo); break;
- case Qdata:
- qlock(&c->rl);
- incref(&r->ref);
- c->rreq = r;
- switch(c->rpid = rfork(RFPROC|RFNOWAIT|RFMEM)){
+ case Qctl: readstr(r, c->qctl); break;
+ case Qinfo: readstr(r, c->qinfo); break;
+ case Qtext:
+ switch(f->pid = rfork(RFPROC|RFNOWAIT|RFMEM)){
case 0:
- off = r->ifcall.offset - c->roff;
- if(c->rn < 0){
- free(c->r);
- c->r = nil;
- c->rn = readmsg(c->fd, &c->r);
- c->roff = r->ifcall.offset;
- off = 0;
- }
+ f->l = &c->qtext;
+ f->unlock = runlock;
+ rlock(f->l);
- n = r->ifcall.count + off < c->rn ?
- r->ifcall.count : c->rn - off;
- if(c->rn == off) {
- r->ofcall.count = 0;
- c->rn = -1;
- } else {
- memmove(r->ofcall.data, c->r + off, n);
- r->ofcall.count = n;
- }
+ fd = dup(c->qtext.fd, -1);
+ seek(fd, r->ifcall.offset, 0);
+ r->ofcall.count = read(fd, r->ofcall.data, r->ifcall.count);
+ close(fd);
- c->rpid = 0;
- c->rreq = nil;
- decref(&r->ref);
- respond(r, err);
- qunlock(&c->rl);
+ runlock(f->l);
+ respond(r, nil);
exits(nil);
- case -1:
- err = "failed to fork read";
- c->rpid = 0;
- c->rreq = nil;
- decref(&r->ref);
- qunlock(&c->rl);
- break;
- default:
- return;
+ case -1: err = "failed to fork read"; break;
+ default: return;
}
break;
+ case Qevent:
+ r->ofcall.count = read(c->ev.out, r->ofcall.data, r->ifcall.count);
+ break;
+ default: err = "read prohibited"; break;
}
- if(buf != nil){
- readstr(r, buf);
- free(buf);
- }
-
respond(r, err);
}
@@ -564,7 +710,7 @@
fswrite(Req *r){
Rfid *f;
Client *c;
- char *d, *err;
+ char *b, *d, *err;
long n;
d = r->ifcall.data;
@@ -574,39 +720,29 @@
err = nil;
switch(f->kind){
- case Qrctl:
- r->ofcall.count = writeqrctl(d, n);
- break;
- case Qctl:
- r->ofcall.count = writeqctl(d, n, f->client);
- break;
- case Qdata:
- qlock(&c->wl);
- incref(&r->ref);
- c->wreq = r;
- switch(c->wpid = rfork(RFPROC|RFNOWAIT|RFMEM)){
+ case Qrctl: r->ofcall.count = writeqrctl(d, n); break;
+ case Qctl: r->ofcall.count = writeqctl(d, n, f->client); break;
+ case Qtext:
+ switch(f->pid = rfork(RFPROC|RFNOWAIT|RFMEM)){
case 0:
- r->ofcall.count = writemsg(c->fd, d, n);
+ f->l = &c->qtext;
+ f->unlock = wunlock;
+ wlock(f->l);
- c->wpid = 0;
- c->wreq = nil;
- decref(&r->ref);
+ r->ofcall.count = write(c->qtext.fd, d, r->ifcall.count);
+ b = ecalloc(r->ifcall.count + 1);
+ memmove(b, d, r->ifcall.count);
+ writecmd(c->tdata.fd, TExecute, b, 0);
+ free(b);
+
+ wunlock(f->l);
respond(r, nil);
- qunlock(&c->wl);
exits(nil);
- case -1:
- err = "failed to fork write";
- c->wpid = 0;
- c->wreq = nil;
- decref(&r->ref);
- qunlock(&c->wl);
- break;
- default:
- return;
+ case -1: err = "failed to fork write"; break;
+ default: return;
}
break;
- default:
- err = "write prohibited";
+ default: err = "write prohibited"; break;
}
respond(r, err);
@@ -616,24 +752,12 @@
fsflush(Req *r){
Req *o;
Rfid *f;
- Client *c;
if(o = r->oldreq)
- if(f = o->fid->aux)
- if(c = clientref(f->client))
- if(o == c->rreq){
- postnote(PNPROC, c->rpid, "interrupt");
- respond(c->rreq, "interrupted");
- c->rpid = 0;
- decref(&c->rreq->ref);
- qunlock(&c->rl);
- } else if (o == c->wreq){
- postnote(PNPROC, c->wpid, "interrupt");
- respond(c->wreq, "interrupted");
- c->wpid = 0;
- c->wreq = nil;
- decref(&c->wreq->ref);
- qunlock(&c->wl);
+ if(f = o->fid->aux){
+ if(f->pid) postnote(PNPROC, f->pid, "interrupt");
+ if(f->l) f->unlock(f->l);
+ respond(o, "interrupted");
}
respond(r, nil);
@@ -653,46 +777,51 @@
fswalk1(Fid *fid, char *name, Qid *qid){
Rfid *f;
Client *c;
- int i, n;
- char *nend;
+ char *s;
+ int n, i;
if(!(fid->qid.type&QTDIR))
return "cannot walk from non-directory";
f = fid->aux;
- n = -1;
if(strcmp(name, "..") == 0){
switch(f->kind){
- case Qroot:
- break;
+ case Qroot: break;
case Qclient:
- rmclient(f->client);
+ f->kind = Qroot;
+ if(f->client > -1) rmclient(f->client);
f->client = -1;
break;
- default:
- if(f->kind > Qclient)
- f->kind = Qclient;
}
} else {
- for(i = f->kind+1; i<QCOUNT; i++){
+ for(i = f->kind+1; i<QCOUNT; i++)
if(nametab[i] && strcmp(name, nametab[i]) == 0)
break;
- if(i == Qclient){
- n = strtol(name, &nend, 10);
- c = clientref(n);
- if(*nend == 0 && c != nil && c->ref != 0){
- incref(c);
- f->client = n;
- break;
- }
- }
+
+ n = strtol(name, &s, 10);
+ if(f->kind == Qroot && i == QCOUNT && *s == 0){
+ i = Qclient;
+ f->client = n;
}
- if(i >= QCOUNT)
- return "directory entry not found";
+
+ c = clientref(f->client);
+ switch(i){
+ case Qclient:
+ if(c == nil || c->ref == 0) return "directory entry not found";
+ incref(c);
+ break;
+ case Qtext: if(c->addr == nil) return "directory entry not found"; break;
+ case Qinfo: if(c->qinfo == nil) return "directory entry not found"; break;
+ case Qevent: if(c->ev.in == 0) return "directory entry not found"; break;
+ case QCOUNT: return "directory entry not found"; break;
+ }
+
f->kind = i;
}
- mkqid(qid, f->kind, n);
+
+ mkqid(qid, f->kind, f->client);
fid->qid = *qid;
+
return nil;
}
@@ -738,7 +867,6 @@
user = getuser();
mtpt = "/mnt/ride";
defport = "4502";
- bufsz = 4096;
time0 = time(0);
nclients = 256;
@@ -751,7 +879,6 @@
}ARGEND
cpool = ecalloc(nclients*sizeof(*cpool));
- qrctl = ecalloc(bufsz);
genqrctl();
JSONfmtinstall();
--- a/test
+++ b/test
@@ -2,9 +2,8 @@
fn fail{ >[1=2] echo $1 >[1=2]; exit $1 }
-~ $1 '' || addr=$1
-~ $addr '' && {
- usage
+argv0=$0
+~ $rideaddr '' && {
fail 'missing server address argument'
}
@@ -40,11 +39,10 @@
fail 'Could not read client ctl'
x=`{grep connect ctl}
- echo 'connect 127.0.0.1' >ctl ||
+ echo 'connect localhost' >ctl ||
fail 'Could not write client ctl'
-
y=`{grep connect ctl}
- ~ $y(2) 127.0.0.1 ||
+ ~ $y(2) localhost ||
fail 'Written and read client ctl values differ'
echo umask $x(2) >ctl
@@ -56,43 +54,29 @@
ls -d $n >[2]/dev/null &&
fail 'Did not clean up closed client'
-
<clone {
cd `{read}
+ echo 'connect '^$rideaddr >ctl
- echo 'connect '^$addr >ctl ||
+ echo >text ||
fail 'Could not connect to server'
- <>data >[1=0] {
+ >text {
~ `{wc -c info} 0 &&
fail 'Did not receive RIDE info'
- cat >/dev/null ||
- fail 'Did not receive initial message'
-
- echo -n '["Execute",{"text":"⍳5\n","trace":0}]' ||
+ # expect events: prompt > response > prompt
+ # XXX: >/dev/null and >/fd/1 mysteriously effect different text
+ <event for(n in 0 1 2) x=`{read} & evpid=$apid
+ echo '⍳5' ||
fail 'Could not send message'
- res='["AppendSessionOutput",{"result":"⍳5\n","type":14,"group":0}]'
- ~ `{cat} $res ||
- fail 'Did not receive response'
+ wait $evpid
+ tx=' ⍳5
+0 1 2 3 4
+ '
+ ~ `''{cat text} $tx ||
+ fail 'Did not receive expected response'
}
cd ..
-}
-
-<clone {
- cd `{read}
- echo 'connect '^$addr >ctl
-
- >data {
- msg='["Execute",{"text":"⍳5\n","trace":0}]'
- echo -n $msg || fail 'Could not send message'
- } & tpid=$apid
-
- <data {
- cat >/dev/null || fail 'Could not receive message'
- } & rpid=$apid
-
- wait $tpid || fail $status
- wait $rpid || fail $status
}
--
⑨