From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=0.2 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED, T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.4 Received: (qmail 17915 invoked from network); 26 Jun 2022 19:35:36 -0000 Received: from 9front.inri.net (168.235.81.73) by inbox.vuxu.org with ESMTPUTF8; 26 Jun 2022 19:35:36 -0000 Received: from mail.posixcafe.org ([45.76.19.58]) by 9front; Sun Jun 26 15:34:10 -0400 2022 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=posixcafe.org; s=20200506; t=1656272046; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=xqxGnP7KJfuMo70WAaNlkERK4+I26VLzvkXu/yQIEw0=; b=WGroewVRxhEIGwLCQeAg2JD8boct82c040LVMSPJ5UWBSkTkXuK2oDw0b9BIQfesgSh8hb yEamVnKvxcg9M3W1tftCX1vVbU2AD8BSxV0HIJEEqwSpM+zF8Lz4S6yW1catvYDht4thtJ rOu+I/EGKJKGzFnsrYnydy0pakHe284= Received: from [192.168.168.200] (161-97-228-135.lpcnextlight.net [161.97.228.135]) by mail.posixcafe.org (OpenSMTPD) with ESMTPSA id 11322381 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO) for <9front@9front.org>; Sun, 26 Jun 2022 14:34:06 -0500 (CDT) Message-ID: <24f24e43-8731-5414-8bd2-e20e3f4d1988@posixcafe.org> Date: Sun, 26 Jun 2022 13:34:02 -0600 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.10.0 Content-Language: en-US To: 9front@9front.org From: Jacob Moody Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit List-ID: <9front.9front.org> List-Help: X-Glyph: ➈ X-Bullshit: optimized structured wrapper software Subject: [9front] [PATCH] aux/9pcon: assert mode Reply-To: 9front@9front.org Precedence: bulk I recalled 9pcon existed this weekend, looking at it again I realized how close this was to being very useful for scripting tests for 9p filesystems. This adds an -a flag, which allows R* messages to be given as commands to do assertions on what is returned from the server. As an example, the following 9pcon 'script' will ensure a serve rspeaks 9P2000. Tversion 8192 9P2000 Rversion 8192 9P2000 I also added a couple more builtin functions that I think help with using 9pcon for this use case. Namely crude macros and sourcing external files. Thanks, moody diff 5ee86cf824c5591aa92118c0cd9d71b005e789d0 uncommitted --- a//sys/src/cmd/aux/9pcon.c +++ b//sys/src/cmd/aux/9pcon.c @@ -5,6 +5,8 @@ #include uint messagesize = 65536; /* just a buffer size */ +int aflag; +int srvfd; void usage(void) @@ -37,12 +39,14 @@ } } +static int rendez; + void watch(int fd) { int n; uchar *buf; - Fcall f; + Fcall f, *p; buf = malloc(messagesize); if(buf == nil) @@ -49,10 +53,18 @@ sysfatal("out of memory"); while((n = read9pmsg(fd, buf, messagesize)) > 0){ + memset(&f, 0, sizeof f); if(convM2S(buf, n, &f) != n){ print("convM2S: %r\n"); continue; } + if(aflag){ + p = malloc(sizeof *p); + if(p == nil) + sysfatal("out of memory"); + memmove(p, &f, sizeof f); + rendezvous(&rendez, p); + } print("\t<- %F\n", &f); } if(n == 0) @@ -62,7 +74,7 @@ } char* -tversion(Fcall *f, int, char **argv) +version(Fcall *f, int, char **argv) { f->msize = strtol(argv[0], 0, 0); if(f->msize > messagesize) @@ -81,6 +93,58 @@ } char* +strtoqid(char *s, Qid *q) +{ + char *dot; + int state; + char buf[1024]; + char *p; + + state = 0; + p = buf; + for(dot = s; *dot; dot++){ + assert(p - buf < sizeof buf); + switch(*dot){ + case '{': + continue; + default: + *p++ = *dot; + break; + case '}': + case ',': + *p = '\0'; + switch(state){ + case 0: + q->path = strtoull(buf, 0, 0); + break; + case 1: + q->vers = strtoul(buf, 0, 0); + break; + case 2: + if(buf[0] == 'f' || strcmp("QTFILE", buf) == 0) + q->type = QTFILE; + else if(buf[0] == 'd' || strcmp("QTDIR", buf) == 0) + q->type = QTDIR; + else + q->type = (uchar)strtol(buf, 0, 0); + break; + } + p = buf; + state++; + } + } + if(state != 3) + return "malformed qid"; + return nil; +} + +char* +rauth(Fcall *f, int, char **argv) +{ + return strtoqid(argv[0], &f->aqid); +} + +char* tflush(Fcall *f, int, char **argv) { f->oldtag = strtol(argv[0], 0, 0); @@ -98,6 +162,12 @@ } char* +rattach(Fcall *f, int, char **argv) +{ + return strtoqid(argv[0], &f->qid); +} + +char* twalk(Fcall *f, int argc, char **argv) { int i; @@ -115,6 +185,24 @@ } char* +rwalk(Fcall *f, int argc, char **argv) +{ + int i; + char *e; + + if(argc >= MAXWELEM) + return "too many names"; + + f->nwqid = argc; + for(i = 0; i < argc; i++){ + e = strtoqid(argv[i], &f->wqid[i]); + if(e != nil) + return e; + } + return nil; +} + +char* topen(Fcall *f, int, char **argv) { f->fid = strtol(argv[0], 0, 0); @@ -123,6 +211,13 @@ } char* +ropen(Fcall *f, int, char **argv) +{ + f->iounit = strtol(argv[1], 0, 0); + return strtoqid(argv[0], &f->qid); +} + +char* tcreate(Fcall *f, int, char **argv) { f->fid = strtol(argv[0], 0, 0); @@ -142,6 +237,14 @@ } char* +rread(Fcall *f, int, char **argv) +{ + f->data = argv[0]; + f->count = strlen(argv[0]); + return nil; +} + +char* twrite(Fcall *f, int, char **argv) { f->fid = strtol(argv[0], 0, 0); @@ -152,6 +255,13 @@ } char* +rwrite(Fcall *f, int, char **argv) +{ + f->count = strtol(argv[0], 0, 0); + return nil; +} + +char* tclunk(Fcall *f, int, char **argv) { f->fid = strtol(argv[0], 0, 0); @@ -194,16 +304,21 @@ static uchar buf[DIRMAX]; Dir d; + //We function as Rstat as well + if(f->type == Twstat){ + f->fid = strtol(argv[0], 0, 0); + argv++; + } + memset(&d, 0, sizeof d); nulldir(&d); - d.name = argv[1]; - d.uid = argv[2]; - d.gid = argv[3]; - d.mode = xstrtoul(argv[4]); - d.mtime = xstrtoul(argv[5]); - d.length = xstrtoull(argv[6]); + d.name = argv[0]; + d.uid = argv[1]; + d.gid = argv[2]; + d.mode = xstrtoul(argv[3]); + d.mtime = xstrtoul(argv[4]); + d.length = xstrtoull(argv[5]); - f->fid = strtol(argv[0], 0, 0); f->stat = buf; f->nstat = convD2M(&d, buf, sizeof buf); if(f->nstat < BIT16SZ) @@ -212,6 +327,21 @@ return nil; } +char* +nop(Fcall*, int, char**) +{ + /* Rwstat,Rremove,Rclunk,Rflush */ + return nil; +} + +enum{ + Xsource = Tmax+1, + Xdef, + Xend, + + Xnexttag, +}; + int taggen; char* @@ -224,6 +354,72 @@ return buf; } +char* shell9p(int); + +char* +source(Fcall*, int, char **argv) +{ + int fd; + char *e; + + fd = open(argv[0], OREAD); + if(fd < 0) + return smprint("^could not open %s: %r", argv[0]); + e = shell9p(fd); + close(fd); + return e; +} + +typedef struct Func Func; +struct Func { + Func *link; + char *name; + char *lines[128]; + int n; +}; + +Func *globals; +Func *local; + +char* +funcdef(Fcall*, int, char **argv) +{ + if(local != nil) + return smprint("^can not define func %s; %s not terminated", argv[0], local->name); + local = mallocz(sizeof *local, 1); + if(local == nil) + return "!out of memory"; + local->name = strdup(argv[0]); + return nil; +} + +char* +funcend(Fcall*, int, char**) +{ + Func **l; + Func *p; + int i; + + if(local == nil) + return "?no function defined"; + l = &globals; + for(p = globals; p != nil; p = p->link){ + if(strcmp(local->name, p->name) == 0) + break; + l = &p->link; + } + if(p != nil){ + for(i=0; in; i++) + free(p->lines[i]); + free(p->name); + free(p); + } + *l = local; + local = nil; + + return nil; +} + typedef struct Cmd Cmd; struct Cmd { char *name; @@ -234,87 +430,184 @@ }; Cmd msg9p[] = { - "Tversion", Tversion, 2, "messagesize version", tversion, + "Tversion", Tversion, 2, "messagesize version", version, + "Rversion", Rversion, 2, "messagesize version", version, + "Tauth", Tauth, 3, "afid uname aname", tauth, + "Rauth", Rauth, 1, "aqid", rauth, + "Tflush", Tflush, 1, "oldtag", tflush, + "Rflush", Rflush, 0, "", nop, + "Tattach", Tattach, 4, "fid afid uname aname", tattach, + "Rattach", Rattach, 1, "qid", rattach, + "Twalk", Twalk, 0, "fid newfid [name...]", twalk, + "Rwalk", Rwalk, 0, "name...", rwalk, + "Topen", Topen, 2, "fid mode", topen, + "Ropen", Ropen, 2, "qid iounit", ropen, + "Tcreate", Tcreate, 4, "fid name perm mode", tcreate, + "Rcreate", Rcreate, 2, "qid iounit", ropen, + "Tread", Tread, 3, "fid offset count", tread, + "Rread", Rread, 1, "data", rread, + "Twrite", Twrite, 3, "fid offset data", twrite, + "Rwrite", Rwrite, 1, "count", rwrite, + "Tclunk", Tclunk, 1, "fid", tclunk, + "Rclunk", Rclunk, 0, "", nop, + "Tremove", Tremove, 1, "fid", tremove, + "Rremove", Rremove, 0, "", nop, + "Tstat", Tstat, 1, "fid", tstat, + "Rstat", Rstat, 6, "name uid gid mode mtime length", twstat, + "Twstat", Twstat, 7, "fid name uid gid mode mtime length", twstat, - "nexttag", 0, 0, "", settag, + "Rwstat", Rwstat, 0, "", nop, + + ".", Xsource, 1, "file", source, + "def", Xdef, 1, "name", funcdef, + "end", Xend, 0, "", funcend, + + "nexttag", Xnexttag, 0, "", settag, }; -void -shell9p(int fd) +char* +run(char *p) { - char *e, *f[10], *p; - uchar *buf; - int i, n, nf; - Biobuf b; - Fcall t; + char *e, *f[10]; + int i, n, nf, n2; + Fcall t, *r; + Func *func; + char *cp; + static uchar *buf = nil; + static uchar *buf2 = nil; - buf = malloc(messagesize); - if(buf == nil){ - fprint(2, "out of memory\n"); - return; + if(buf == nil) + buf = malloc(messagesize); + if(buf2 == nil) + buf2 = malloc(messagesize); + if(buf == nil || buf2 == nil) + return "!out of memory"; + + if(p[0] == '#') + return nil; + if(local != nil && strstr(p, "end") == nil){ + local->lines[local->n++] = strdup(p); + return nil; } + if((nf = tokenize(p, f, nelem(f))) == 0) + return nil; + for(i=0; ilink){ + if(strcmp(func->name, f[0]) != 0) + continue; + for(i = 0; i < func->n; i++){ + cp = strdup(func->lines[i]); + if(e = run(cp)){ + free(cp); + return e; + } + free(cp); + } + return nil; + } + return "?unknown message"; + } - taggen = 0; - Binit(&b, 0, OREAD); + memset(&t, 0, sizeof t); + t.type = msg9p[i].type; + if(t.type == Tversion) + t.tag = NOTAG; + else + t.tag = ++taggen; + if(nf < 1 || (msg9p[i].argc && nf != 1+msg9p[i].argc)) + return smprint("^usage: %s %s", msg9p[i].name, msg9p[i].usage); + + if((e = msg9p[i].fn(&t, nf-1, f+1)) || t.type > Tmax) + return e; + + n = convS2M(&t, buf, messagesize); + if(n <= BIT16SZ) + return "?message too large for buffer"; + + switch(msg9p[i].name[0]){ + case 'R': + if(!aflag) + break; + r = rendezvous(&rendez, nil); + r->tag = t.tag; + n2 = convS2M(r, buf2, messagesize); + if(n != n2 || memcmp(buf, buf2, n) != 0){ + fprint(2, "?mismatch %F != %F\n", r, &t); + return "!assert fail"; + } + free(r); + break; + case 'T': + if(write(srvfd, buf, n) != n) + return "!write fails"; + print("\t-> %F\n", &t); + } + return nil; +} + +char* +shell9p(int fd) +{ + char *e, *p; + Biobuf b; + + Binit(&b, fd, OREAD); while(p = Brdline(&b, '\n')){ p[Blinelen(&b)-1] = '\0'; - if(p[0] == '#') + e = run(p); + if(e == nil) continue; - if((nf = tokenize(p, f, nelem(f))) == 0) - continue; - for(i=0; i %F\n", &t); } + Bterm(&b); + return nil; } void main(int argc, char **argv) { - int fd, pid, cmd, net; + int pid, cmd, net; + char *status; cmd = 0; net = 0; + aflag = 0; + taggen = 0; ARGBEGIN{ + case 'a': + aflag = 1; + break; case 'c': cmd = 1; break; @@ -339,29 +632,30 @@ usage(); if(cmd) - fd = connectcmd(argv[0]); + srvfd = connectcmd(argv[0]); else if(net){ - fd = dial(netmkaddr(argv[0], "net", "9fs"), 0, 0, 0); - if(fd < 0) + srvfd = dial(netmkaddr(argv[0], "net", "9fs"), 0, 0, 0); + if(srvfd < 0) sysfatal("dial: %r"); }else{ - fd = open(argv[0], ORDWR); - if(fd < 0) + srvfd = open(argv[0], ORDWR); + if(srvfd < 0) sysfatal("open: %r"); } + status = nil; switch(pid = rfork(RFPROC|RFMEM)){ case -1: sysfatal("rfork: %r"); break; case 0: - watch(fd); + watch(srvfd); postnote(PNPROC, getppid(), "kill"); break; default: - shell9p(fd); + status = shell9p(0); postnote(PNPROC, pid, "kill"); break; } - exits(nil); + exits(status); }