| #include <u.h> |
| #include <libc.h> |
| #include <fcall.h> |
| #include <thread.h> |
| #include <errno.h> |
| |
| #define err err9pserve /* Darwin x86 */ |
| |
| enum |
| { |
| STACK = 32768, |
| NHASH = 31, |
| MAXMSG = 64, /* per connection */ |
| MAXMSGSIZE = 4*1024*1024 |
| }; |
| |
| typedef struct Hash Hash; |
| typedef struct Fid Fid; |
| typedef struct Msg Msg; |
| typedef struct Conn Conn; |
| typedef struct Queue Queue; |
| |
| struct Hash |
| { |
| Hash *next; |
| uint n; |
| void *v; |
| }; |
| |
| struct Fid |
| { |
| int fid; |
| int ref; |
| int cfid; |
| int openfd; |
| int offset; |
| int coffset; |
| int isdir; |
| Fid *next; |
| }; |
| |
| struct Msg |
| { |
| Conn *c; |
| int internal; |
| int sync; |
| int ref; |
| int ctag; |
| int tag; |
| int isopenfd; |
| Fcall tx; |
| Fcall rx; |
| Fid *fid; |
| Fid *newfid; |
| Fid *afid; |
| Msg *oldm; |
| Msg *next; |
| uchar *tpkt; |
| uchar *rpkt; |
| }; |
| |
| struct Conn |
| { |
| int fd; |
| int fdmode; |
| Fid *fdfid; |
| int nmsg; |
| int nfid; |
| Channel *inc; |
| Channel *internal; |
| int inputstalled; |
| char dir[40]; |
| Hash *tag[NHASH]; |
| Hash *fid[NHASH]; |
| Queue *outq; |
| Queue *inq; |
| Channel *outqdead; |
| }; |
| |
| char *xaname; |
| char *addr; |
| int afd; |
| char adir[40]; |
| int isunix; |
| Queue *outq; |
| Queue *inq; |
| int verbose = 0; |
| int logging = 0; |
| int msize = 8192; |
| u32int xafid = NOFID; |
| int attached; |
| int versioned; |
| int noauth; |
| |
| void *gethash(Hash**, uint); |
| int puthash(Hash**, uint, void*); |
| int delhash(Hash**, uint, void*); |
| Msg *mread9p(Ioproc*, int); |
| int mwrite9p(Ioproc*, int, uchar*); |
| uchar *read9ppkt(Ioproc*, int); |
| int write9ppkt(int, uchar*); |
| Msg *msgnew(int); |
| void msgput(Msg*); |
| void msgclear(Msg*); |
| Msg *msgget(int); |
| void msgincref(Msg*); |
| Fid *fidnew(int); |
| void fidput(Fid*); |
| void *emalloc(int); |
| void *erealloc(void*, int); |
| Queue *qalloc(void); |
| int sendq(Queue*, void*); |
| void *recvq(Queue*); |
| void connthread(void*); |
| void connoutthread(void*); |
| void listenthread(void*); |
| void outputthread(void*); |
| void inputthread(void*); |
| void rewritehdr(Fcall*, uchar*); |
| void repack(Fcall*, uchar**); |
| int tlisten(char*, char*); |
| int taccept(int, char*); |
| int iolisten(Ioproc*, char*, char*); |
| int ioaccept(Ioproc*, int, char*); |
| int iorecvfd(Ioproc*, int); |
| int iosendfd(Ioproc*, int, int); |
| void mainproc(void*); |
| int ignorepipe(void*, char*); |
| int timefmt(Fmt*); |
| void dorootstat(void); |
| |
| void |
| usage(void) |
| { |
| fprint(2, "usage: 9pserve [-lnv] [-A aname afid] [-c addr] [-M msize] address\n"); |
| fprint(2, "\treads/writes 9P messages on stdin/stdout\n"); |
| threadexitsall("usage"); |
| } |
| |
| uchar vbuf[128]; |
| extern int _threaddebuglevel; |
| void |
| threadmain(int argc, char **argv) |
| { |
| char *file, *x, *addr; |
| int fd; |
| |
| rfork(RFNOTEG); |
| x = getenv("verbose9pserve"); |
| if(x){ |
| verbose = atoi(x); |
| fprint(2, "verbose9pserve %s => %d\n", x, verbose); |
| } |
| ARGBEGIN{ |
| default: |
| usage(); |
| case 'A': |
| attached = 1; |
| xaname = EARGF(usage()); |
| xafid = atoi(EARGF(usage())); |
| break; |
| case 'M': |
| versioned = 1; |
| msize = atoi(EARGF(usage())); |
| break; |
| case 'c': |
| addr = netmkaddr(EARGF(usage()), "net", "9fs"); |
| if((fd = dial(addr, nil, nil, nil)) < 0) |
| sysfatal("dial %s: %r", addr); |
| dup(fd, 0); |
| dup(fd, 1); |
| if(fd > 1) |
| close(fd); |
| break; |
| case 'n': |
| noauth = 1; |
| break; |
| case 'v': |
| verbose++; |
| break; |
| case 'u': |
| isunix++; |
| break; |
| case 'l': |
| logging++; |
| break; |
| }ARGEND |
| |
| if(attached && !versioned){ |
| fprint(2, "-A must be used with -M\n"); |
| usage(); |
| } |
| |
| if(argc != 1) |
| usage(); |
| addr = argv[0]; |
| |
| fmtinstall('T', timefmt); |
| |
| if((afd = announce(addr, adir)) < 0) |
| sysfatal("announce %s: %r", addr); |
| if(logging){ |
| if(strncmp(addr, "unix!", 5) == 0) |
| addr += 5; |
| file = smprint("%s.log", addr); |
| if(file == nil) |
| sysfatal("smprint log: %r"); |
| if((fd = create(file, OWRITE, 0666)) < 0) |
| sysfatal("create %s: %r", file); |
| dup(fd, 2); |
| if(fd > 2) |
| close(fd); |
| } |
| if(verbose) fprint(2, "%T 9pserve running\n"); |
| proccreate(mainproc, nil, STACK); |
| } |
| |
| void |
| mainproc(void *v) |
| { |
| int n, nn; |
| Fcall f; |
| USED(v); |
| |
| atnotify(ignorepipe, 1); |
| fmtinstall('D', dirfmt); |
| fmtinstall('M', dirmodefmt); |
| fmtinstall('F', fcallfmt); |
| fmtinstall('H', encodefmt); |
| |
| outq = qalloc(); |
| inq = qalloc(); |
| |
| if(!versioned){ |
| f.type = Tversion; |
| f.version = "9P2000"; |
| f.msize = msize; |
| f.tag = NOTAG; |
| n = convS2M(&f, vbuf, sizeof vbuf); |
| if(n <= BIT16SZ) |
| sysfatal("convS2M conversion error"); |
| if(verbose > 1) fprint(2, "%T * <- %F\n", &f); |
| nn = write(1, vbuf, n); |
| if(n != nn) |
| sysfatal("error writing Tversion: %r\n"); |
| n = read9pmsg(0, vbuf, sizeof vbuf); |
| if(n < 0) |
| sysfatal("read9pmsg failure"); |
| if(convM2S(vbuf, n, &f) != n) |
| sysfatal("convM2S failure"); |
| if(f.msize < msize) |
| msize = f.msize; |
| if(verbose > 1) fprint(2, "%T * -> %F\n", &f); |
| } |
| |
| threadcreate(inputthread, nil, STACK); |
| threadcreate(outputthread, nil, STACK); |
| |
| /* if(rootfid) */ |
| /* dorootstat(); */ |
| |
| threadcreate(listenthread, nil, STACK); |
| threadexits(0); |
| } |
| |
| int |
| ignorepipe(void *v, char *s) |
| { |
| USED(v); |
| if(strcmp(s, "sys: write on closed pipe") == 0) |
| return 1; |
| if(strcmp(s, "sys: tstp") == 0) |
| return 1; |
| if(strcmp(s, "sys: window size change") == 0) |
| return 1; |
| fprint(2, "9pserve %s: %T note: %s\n", addr, s); |
| return 0; |
| } |
| |
| void |
| listenthread(void *arg) |
| { |
| Conn *c; |
| Ioproc *io; |
| |
| io = ioproc(); |
| USED(arg); |
| threadsetname("listen %s", adir); |
| for(;;){ |
| c = emalloc(sizeof(Conn)); |
| c->fd = iolisten(io, adir, c->dir); |
| if(c->fd < 0){ |
| if(verbose) fprint(2, "%T listen: %r\n"); |
| close(afd); |
| free(c); |
| return; |
| } |
| c->inc = chancreate(sizeof(void*), 0); |
| c->internal = chancreate(sizeof(void*), 0); |
| c->inq = qalloc(); |
| c->outq = qalloc(); |
| c->outqdead = chancreate(sizeof(void*), 0); |
| if(verbose) fprint(2, "%T incoming call on %s\n", c->dir); |
| threadcreate(connthread, c, STACK); |
| } |
| } |
| |
| void |
| send9pmsg(Msg *m) |
| { |
| int n, nn; |
| |
| n = sizeS2M(&m->rx); |
| m->rpkt = emalloc(n); |
| nn = convS2M(&m->rx, m->rpkt, n); |
| if(nn <= BIT16SZ) |
| sysfatal("convS2M conversion error"); |
| if(nn != n) |
| sysfatal("sizeS2M and convS2M disagree"); |
| sendq(m->c->outq, m); |
| } |
| |
| void |
| sendomsg(Msg *m) |
| { |
| int n, nn; |
| |
| n = sizeS2M(&m->tx); |
| m->tpkt = emalloc(n); |
| nn = convS2M(&m->tx, m->tpkt, n); |
| if(nn <= BIT16SZ) |
| sysfatal("convS2M conversion error"); |
| if(nn != n) |
| sysfatal("sizeS2M and convS2M disagree"); |
| sendq(outq, m); |
| } |
| |
| void |
| err(Msg *m, char *ename) |
| { |
| m->rx.type = Rerror; |
| m->rx.ename = ename; |
| m->rx.tag = m->tx.tag; |
| send9pmsg(m); |
| } |
| |
| char* |
| estrdup(char *s) |
| { |
| char *t; |
| |
| t = emalloc(strlen(s)+1); |
| strcpy(t, s); |
| return t; |
| } |
| |
| void |
| connthread(void *arg) |
| { |
| int i, fd; |
| Conn *c; |
| Hash *h, *hnext; |
| Msg *m, *om, *mm, sync; |
| Fid *f; |
| Ioproc *io; |
| |
| c = arg; |
| threadsetname("conn %s", c->dir); |
| io = ioproc(); |
| fd = ioaccept(io, c->fd, c->dir); |
| if(fd < 0){ |
| if(verbose) fprint(2, "%T accept %s: %r\n", c->dir); |
| goto out; |
| } |
| close(c->fd); |
| c->fd = fd; |
| threadcreate(connoutthread, c, STACK); |
| while((m = mread9p(io, c->fd)) != nil){ |
| if(verbose > 1) fprint(2, "%T fd#%d -> %F\n", c->fd, &m->tx); |
| m->c = c; |
| m->ctag = m->tx.tag; |
| c->nmsg++; |
| if(verbose > 1) fprint(2, "%T fd#%d: new msg %p\n", c->fd, m); |
| if(puthash(c->tag, m->tx.tag, m) < 0){ |
| err(m, "duplicate tag"); |
| continue; |
| } |
| msgincref(m); |
| switch(m->tx.type){ |
| case Tversion: |
| m->rx.tag = m->tx.tag; |
| m->rx.msize = m->tx.msize; |
| if(m->rx.msize > msize) |
| m->rx.msize = msize; |
| m->rx.version = "9P2000"; |
| m->rx.type = Rversion; |
| send9pmsg(m); |
| continue; |
| case Tflush: |
| if((m->oldm = gethash(c->tag, m->tx.oldtag)) == nil){ |
| m->rx.tag = m->tx.tag; |
| m->rx.type = Rflush; |
| send9pmsg(m); |
| continue; |
| } |
| msgincref(m->oldm); |
| break; |
| case Tattach: |
| m->afid = nil; |
| if(m->tx.afid != NOFID |
| && (m->afid = gethash(c->fid, m->tx.afid)) == nil){ |
| err(m, "unknown fid"); |
| continue; |
| } |
| if(m->afid) |
| m->afid->ref++; |
| m->fid = fidnew(m->tx.fid); |
| if(puthash(c->fid, m->tx.fid, m->fid) < 0){ |
| err(m, "duplicate fid"); |
| continue; |
| } |
| m->fid->ref++; |
| if(attached && m->afid==nil){ |
| if(m->tx.aname[0] && strcmp(xaname, m->tx.aname) != 0){ |
| err(m, "invalid attach name"); |
| continue; |
| } |
| m->tx.afid = xafid; |
| m->tx.aname = xaname; |
| m->tx.uname = getuser(); /* what srv.c used */ |
| repack(&m->tx, &m->tpkt); |
| } |
| break; |
| case Twalk: |
| if((m->fid = gethash(c->fid, m->tx.fid)) == nil){ |
| err(m, "unknown fid"); |
| continue; |
| } |
| m->fid->ref++; |
| if(m->tx.newfid == m->tx.fid){ |
| m->fid->ref++; |
| m->newfid = m->fid; |
| }else{ |
| m->newfid = fidnew(m->tx.newfid); |
| if(puthash(c->fid, m->tx.newfid, m->newfid) < 0){ |
| err(m, "duplicate fid"); |
| continue; |
| } |
| m->newfid->ref++; |
| } |
| break; |
| case Tauth: |
| if(attached){ |
| err(m, "authentication not required"); |
| continue; |
| } |
| if(noauth){ |
| err(m, "authentication rejected"); |
| continue; |
| } |
| m->afid = fidnew(m->tx.afid); |
| if(puthash(c->fid, m->tx.afid, m->afid) < 0){ |
| err(m, "duplicate fid"); |
| continue; |
| } |
| m->afid->ref++; |
| break; |
| case Tcreate: |
| if(m->tx.perm&(DMSYMLINK|DMDEVICE|DMNAMEDPIPE|DMSOCKET)){ |
| err(m, "unsupported file type"); |
| continue; |
| } |
| goto caseTopen; |
| case Topenfd: |
| if(m->tx.mode&~(OTRUNC|3)){ |
| err(m, "bad openfd mode"); |
| continue; |
| } |
| m->isopenfd = 1; |
| m->tx.type = Topen; |
| m->tpkt[4] = Topen; |
| /* fall through */ |
| caseTopen: |
| case Topen: |
| case Tclunk: |
| case Tread: |
| case Twrite: |
| case Tremove: |
| case Tstat: |
| case Twstat: |
| if((m->fid = gethash(c->fid, m->tx.fid)) == nil){ |
| err(m, "unknown fid"); |
| continue; |
| } |
| m->fid->ref++; |
| break; |
| } |
| |
| /* have everything - translate and send */ |
| m->c = c; |
| m->ctag = m->tx.tag; |
| m->tx.tag = m->tag; |
| if(m->fid) |
| m->tx.fid = m->fid->fid; |
| if(m->newfid) |
| m->tx.newfid = m->newfid->fid; |
| if(m->afid) |
| m->tx.afid = m->afid->fid; |
| if(m->oldm) |
| m->tx.oldtag = m->oldm->tag; |
| /* reference passes to outq */ |
| sendq(outq, m); |
| while(c->nmsg >= MAXMSG){ |
| c->inputstalled = 1; |
| recvp(c->inc); |
| } |
| } |
| |
| if(verbose) fprint(2, "%T fd#%d eof; flushing conn\n", c->fd); |
| |
| /* flush all outstanding messages */ |
| for(i=0; i<NHASH; i++){ |
| while((h = c->tag[i]) != nil){ |
| om = h->v; |
| msgincref(om); /* for us */ |
| m = msgnew(0); |
| m->internal = 1; |
| m->c = c; |
| c->nmsg++; |
| m->tx.type = Tflush; |
| m->tx.tag = m->tag; |
| m->tx.oldtag = om->tag; |
| m->oldm = om; |
| msgincref(om); |
| msgincref(m); /* for outq */ |
| sendomsg(m); |
| mm = recvp(c->internal); |
| assert(mm == m); |
| msgput(m); /* got from recvp */ |
| msgput(m); /* got from msgnew */ |
| if(delhash(c->tag, om->ctag, om) == 0) |
| msgput(om); /* got from hash table */ |
| msgput(om); /* got from msgincref */ |
| } |
| } |
| |
| /* |
| * outputthread has written all its messages |
| * to the remote connection (because we've gotten all the replies!), |
| * but it might not have gotten a chance to msgput |
| * the very last one. sync up to make sure. |
| */ |
| memset(&sync, 0, sizeof sync); |
| sync.sync = 1; |
| sync.c = c; |
| sendq(outq, &sync); |
| recvp(c->outqdead); |
| |
| /* everything is quiet; can close the local output queue. */ |
| sendq(c->outq, nil); |
| recvp(c->outqdead); |
| |
| /* should be no messages left anywhere. */ |
| assert(c->nmsg == 0); |
| |
| /* clunk all outstanding fids */ |
| for(i=0; i<NHASH; i++){ |
| for(h=c->fid[i]; h; h=hnext){ |
| f = h->v; |
| m = msgnew(0); |
| m->internal = 1; |
| m->c = c; |
| c->nmsg++; |
| m->tx.type = Tclunk; |
| m->tx.tag = m->tag; |
| m->tx.fid = f->fid; |
| m->fid = f; |
| f->ref++; |
| msgincref(m); |
| sendomsg(m); |
| mm = recvp(c->internal); |
| assert(mm == m); |
| msgclear(m); |
| msgput(m); /* got from recvp */ |
| msgput(m); /* got from msgnew */ |
| fidput(f); /* got from hash table */ |
| hnext = h->next; |
| free(h); |
| } |
| } |
| |
| out: |
| closeioproc(io); |
| assert(c->nmsg == 0); |
| assert(c->nfid == 0); |
| close(c->fd); |
| chanfree(c->internal); |
| c->internal = 0; |
| chanfree(c->inc); |
| c->inc = 0; |
| free(c->inq); |
| c->inq = 0; |
| free(c); |
| } |
| |
| static void |
| openfdthread(void *v) |
| { |
| Conn *c; |
| Fid *fid; |
| Msg *m; |
| int n; |
| vlong tot; |
| Ioproc *io; |
| char buf[1024]; |
| |
| c = v; |
| fid = c->fdfid; |
| io = ioproc(); |
| threadsetname("openfd %s", c->fdfid); |
| tot = 0; |
| m = nil; |
| if(c->fdmode == OREAD){ |
| for(;;){ |
| if(verbose) fprint(2, "%T tread..."); |
| m = msgnew(0); |
| m->internal = 1; |
| m->c = c; |
| m->tx.type = Tread; |
| m->tx.count = msize - IOHDRSZ; |
| m->tx.fid = fid->fid; |
| m->tx.tag = m->tag; |
| m->tx.offset = tot; |
| m->fid = fid; |
| fid->ref++; |
| msgincref(m); |
| sendomsg(m); |
| recvp(c->internal); |
| if(m->rx.type == Rerror){ |
| /* fprint(2, "%T read error: %s\n", m->rx.ename); */ |
| break; |
| } |
| if(m->rx.count == 0) |
| break; |
| tot += m->rx.count; |
| if(iowrite(io, c->fd, m->rx.data, m->rx.count) != m->rx.count){ |
| /* fprint(2, "%T pipe write error: %r\n"); */ |
| break; |
| } |
| msgput(m); |
| msgput(m); |
| m = nil; |
| } |
| }else{ |
| for(;;){ |
| if(verbose) fprint(2, "%T twrite..."); |
| n = sizeof buf; |
| if(n > msize) |
| n = msize; |
| if((n=ioread(io, c->fd, buf, n)) <= 0){ |
| if(n < 0) |
| fprint(2, "%T pipe read error: %r\n"); |
| break; |
| } |
| m = msgnew(0); |
| m->internal = 1; |
| m->c = c; |
| m->tx.type = Twrite; |
| m->tx.fid = fid->fid; |
| m->tx.data = buf; |
| m->tx.count = n; |
| m->tx.tag = m->tag; |
| m->tx.offset = tot; |
| m->fid = fid; |
| fid->ref++; |
| msgincref(m); |
| sendomsg(m); |
| recvp(c->internal); |
| if(m->rx.type == Rerror){ |
| /* fprint(2, "%T write error: %s\n", m->rx.ename); */ |
| } |
| tot += n; |
| msgput(m); |
| msgput(m); |
| m = nil; |
| } |
| } |
| if(verbose) fprint(2, "%T eof on %d fid %d\n", c->fd, fid->fid); |
| close(c->fd); |
| closeioproc(io); |
| if(m){ |
| msgput(m); |
| msgput(m); |
| } |
| if(verbose) fprint(2, "%T eof on %d fid %d ref %d\n", c->fd, fid->fid, fid->ref); |
| if(--fid->openfd == 0){ |
| m = msgnew(0); |
| m->internal = 1; |
| m->c = c; |
| m->tx.type = Tclunk; |
| m->tx.tag = m->tag; |
| m->tx.fid = fid->fid; |
| m->fid = fid; |
| fid->ref++; |
| msgincref(m); |
| sendomsg(m); |
| recvp(c->internal); |
| msgput(m); |
| msgput(m); |
| } |
| fidput(fid); |
| c->fdfid = nil; |
| chanfree(c->internal); |
| c->internal = 0; |
| free(c); |
| } |
| |
| int |
| xopenfd(Msg *m) |
| { |
| char errs[ERRMAX]; |
| int n, p[2]; |
| Conn *nc; |
| |
| if(pipe(p) < 0){ |
| rerrstr(errs, sizeof errs); |
| err(m, errs); |
| /* XXX return here? */ |
| } |
| if(verbose) fprint(2, "%T xopen pipe %d %d...", p[0], p[1]); |
| |
| /* now we're committed. */ |
| |
| /* a new connection for this fid */ |
| nc = emalloc(sizeof(Conn)); |
| nc->internal = chancreate(sizeof(void*), 0); |
| |
| /* a ref for us */ |
| nc->fdfid = m->fid; |
| m->fid->ref++; |
| nc->fdfid->openfd++; |
| nc->fdmode = m->tx.mode; |
| nc->fd = p[0]; |
| |
| /* a thread to tend the pipe */ |
| threadcreate(openfdthread, nc, STACK); |
| |
| /* if mode is ORDWR, that openfdthread will write; start a reader */ |
| if((m->tx.mode&3) == ORDWR){ |
| nc = emalloc(sizeof(Conn)); |
| nc->internal = chancreate(sizeof(void*), 0); |
| nc->fdfid = m->fid; |
| m->fid->ref++; |
| nc->fdfid->openfd++; |
| nc->fdmode = OREAD; |
| nc->fd = dup(p[0], -1); |
| threadcreate(openfdthread, nc, STACK); |
| } |
| |
| /* steal fid from other connection */ |
| if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0) |
| fidput(m->fid); |
| |
| /* rewrite as Ropenfd */ |
| m->rx.type = Ropenfd; |
| n = GBIT32(m->rpkt); |
| m->rpkt = erealloc(m->rpkt, n+4); |
| PBIT32(m->rpkt+n, p[1]); |
| n += 4; |
| PBIT32(m->rpkt, n); |
| m->rpkt[4] = Ropenfd; |
| m->rx.unixfd = p[1]; |
| return 0; |
| } |
| |
| void |
| connoutthread(void *arg) |
| { |
| int err; |
| Conn *c; |
| Msg *m, *om; |
| Ioproc *io; |
| |
| c = arg; |
| io = ioproc(); |
| threadsetname("connout %s", c->dir); |
| while((m = recvq(c->outq)) != nil){ |
| err = m->tx.type+1 != m->rx.type; |
| if(!err && m->isopenfd) |
| if(xopenfd(m) < 0) |
| continue; |
| switch(m->tx.type){ |
| case Tflush: |
| om = m->oldm; |
| if(om) |
| if(delhash(om->c->tag, om->ctag, om) == 0) |
| msgput(om); |
| break; |
| case Tclunk: |
| case Tremove: |
| if(m->fid) |
| if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0) |
| fidput(m->fid); |
| break; |
| case Tauth: |
| if(err && m->afid){ |
| if(verbose) fprint(2, "%T auth error\n"); |
| if(delhash(m->c->fid, m->afid->cfid, m->afid) == 0) |
| fidput(m->afid); |
| } |
| break; |
| case Tattach: |
| if(err && m->fid) |
| if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0) |
| fidput(m->fid); |
| break; |
| case Twalk: |
| if(err || m->rx.nwqid < m->tx.nwname) |
| if(m->tx.fid != m->tx.newfid && m->newfid) |
| if(delhash(m->c->fid, m->newfid->cfid, m->newfid) == 0) |
| fidput(m->newfid); |
| break; |
| case Tread: |
| break; |
| case Tstat: |
| break; |
| case Topen: |
| case Tcreate: |
| m->fid->isdir = (m->rx.qid.type & QTDIR); |
| break; |
| } |
| if(delhash(m->c->tag, m->ctag, m) == 0) |
| msgput(m); |
| if(verbose > 1) fprint(2, "%T fd#%d <- %F\n", c->fd, &m->rx); |
| rewritehdr(&m->rx, m->rpkt); |
| if(mwrite9p(io, c->fd, m->rpkt) < 0) |
| if(verbose) fprint(2, "%T write error: %r\n"); |
| msgput(m); |
| if(c->inputstalled && c->nmsg < MAXMSG) |
| nbsendp(c->inc, 0); |
| } |
| closeioproc(io); |
| free(c->outq); |
| c->outq = nil; |
| sendp(c->outqdead, nil); |
| } |
| |
| void |
| outputthread(void *arg) |
| { |
| Msg *m; |
| Ioproc *io; |
| |
| USED(arg); |
| io = ioproc(); |
| threadsetname("output"); |
| while((m = recvq(outq)) != nil){ |
| if(m->sync){ |
| sendp(m->c->outqdead, nil); |
| continue; |
| } |
| if(verbose > 1) fprint(2, "%T * <- %F\n", &m->tx); |
| rewritehdr(&m->tx, m->tpkt); |
| if(mwrite9p(io, 1, m->tpkt) < 0) |
| sysfatal("output error: %r"); |
| msgput(m); |
| } |
| closeioproc(io); |
| fprint(2, "%T output eof\n"); |
| threadexitsall(0); |
| } |
| |
| void |
| inputthread(void *arg) |
| { |
| uchar *pkt; |
| int n, nn, tag; |
| Msg *m; |
| Ioproc *io; |
| |
| threadsetname("input"); |
| if(verbose) fprint(2, "%T input thread\n"); |
| io = ioproc(); |
| USED(arg); |
| while((pkt = read9ppkt(io, 0)) != nil){ |
| n = GBIT32(pkt); |
| if(n < 7){ |
| fprint(2, "%T short 9P packet from server\n"); |
| free(pkt); |
| continue; |
| } |
| if(verbose > 2) fprint(2, "%T read %.*H\n", n, pkt); |
| tag = GBIT16(pkt+5); |
| if((m = msgget(tag)) == nil){ |
| fprint(2, "%T unexpected 9P response tag %d\n", tag); |
| free(pkt); |
| continue; |
| } |
| if((nn = convM2S(pkt, n, &m->rx)) != n){ |
| fprint(2, "%T bad packet - convM2S %d but %d\n", nn, n); |
| free(pkt); |
| msgput(m); |
| continue; |
| } |
| if(verbose > 1) fprint(2, "%T * -> %F%s\n", &m->rx, |
| m->internal ? " (internal)" : ""); |
| m->rpkt = pkt; |
| m->rx.tag = m->ctag; |
| if(m->internal) |
| sendp(m->c->internal, m); |
| else if(m->c->outq) |
| sendq(m->c->outq, m); |
| else |
| msgput(m); |
| } |
| closeioproc(io); |
| /*fprint(2, "%T input eof\n"); */ |
| threadexitsall(0); |
| } |
| |
| void* |
| gethash(Hash **ht, uint n) |
| { |
| Hash *h; |
| |
| for(h=ht[n%NHASH]; h; h=h->next) |
| if(h->n == n) |
| return h->v; |
| return nil; |
| } |
| |
| int |
| delhash(Hash **ht, uint n, void *v) |
| { |
| Hash *h, **l; |
| |
| for(l=&ht[n%NHASH]; h=*l; l=&h->next) |
| if(h->n == n){ |
| if(h->v != v){ |
| if(verbose) fprint(2, "%T delhash %d got %p want %p\n", n, h->v, v); |
| return -1; |
| } |
| *l = h->next; |
| free(h); |
| return 0; |
| } |
| return -1; |
| } |
| |
| int |
| puthash(Hash **ht, uint n, void *v) |
| { |
| Hash *h; |
| |
| if(gethash(ht, n)) |
| return -1; |
| h = emalloc(sizeof(Hash)); |
| h->next = ht[n%NHASH]; |
| h->n = n; |
| h->v = v; |
| ht[n%NHASH] = h; |
| return 0; |
| } |
| |
| Fid **fidtab; |
| int nfidtab; |
| Fid *freefid; |
| |
| Fid* |
| fidnew(int cfid) |
| { |
| Fid *f; |
| |
| if(freefid == nil){ |
| fidtab = erealloc(fidtab, (nfidtab+1)*sizeof(fidtab[0])); |
| if(nfidtab == xafid){ |
| fidtab[nfidtab++] = nil; |
| fidtab = erealloc(fidtab, (nfidtab+1)*sizeof(fidtab[0])); |
| } |
| fidtab[nfidtab] = emalloc(sizeof(Fid)); |
| freefid = fidtab[nfidtab]; |
| freefid->fid = nfidtab++; |
| } |
| f = freefid; |
| freefid = f->next; |
| f->cfid = cfid; |
| f->ref = 1; |
| f->offset = 0; |
| f->coffset = 0; |
| f->isdir = -1; |
| return f; |
| } |
| |
| void |
| fidput(Fid *f) |
| { |
| if(f == nil) |
| return; |
| assert(f->ref > 0); |
| if(--f->ref > 0) |
| return; |
| f->next = freefid; |
| f->cfid = -1; |
| freefid = f; |
| } |
| |
| Msg **msgtab; |
| int nmsgtab; |
| int nmsg; |
| Msg *freemsg; |
| |
| void |
| msgincref(Msg *m) |
| { |
| if(verbose > 1) fprint(2, "%T msgincref @0x%lux %p tag %d/%d ref %d=>%d\n", |
| getcallerpc(&m), m, m->tag, m->ctag, m->ref, m->ref+1); |
| m->ref++; |
| } |
| |
| Msg* |
| msgnew(int x) |
| { |
| Msg *m; |
| |
| if(freemsg == nil){ |
| msgtab = erealloc(msgtab, (nmsgtab+1)*sizeof(msgtab[0])); |
| msgtab[nmsgtab] = emalloc(sizeof(Msg)); |
| freemsg = msgtab[nmsgtab]; |
| freemsg->tag = nmsgtab++; |
| } |
| m = freemsg; |
| freemsg = m->next; |
| m->ref = 1; |
| if(verbose > 1) fprint(2, "%T msgnew @0x%lux %p tag %d ref %d\n", |
| getcallerpc(&x), m, m->tag, m->ref); |
| nmsg++; |
| return m; |
| } |
| |
| /* |
| * Clear data associated with connections, so that |
| * if all msgs have been msgcleared, the connection |
| * can be freed. Note that this does *not* free the tpkt |
| * and rpkt; they are freed in msgput with the msg itself. |
| * The io write thread might still be holding a ref to msg |
| * even once the connection has finished with it. |
| */ |
| void |
| msgclear(Msg *m) |
| { |
| if(m->c){ |
| m->c->nmsg--; |
| m->c = nil; |
| } |
| if(m->oldm){ |
| msgput(m->oldm); |
| m->oldm = nil; |
| } |
| if(m->fid){ |
| fidput(m->fid); |
| m->fid = nil; |
| } |
| if(m->afid){ |
| fidput(m->afid); |
| m->afid = nil; |
| } |
| if(m->newfid){ |
| fidput(m->newfid); |
| m->newfid = nil; |
| } |
| if(m->rx.type == Ropenfd && m->rx.unixfd >= 0){ |
| close(m->rx.unixfd); |
| m->rx.unixfd = -1; |
| } |
| } |
| |
| void |
| msgput(Msg *m) |
| { |
| if(m == nil) |
| return; |
| |
| if(verbose > 1) fprint(2, "%T msgput 0x%lux %p tag %d/%d ref %d\n", |
| getcallerpc(&m), m, m->tag, m->ctag, m->ref); |
| assert(m->ref > 0); |
| if(--m->ref > 0) |
| return; |
| nmsg--; |
| msgclear(m); |
| if(m->tpkt){ |
| free(m->tpkt); |
| m->tpkt = nil; |
| } |
| if(m->rpkt){ |
| free(m->rpkt); |
| m->rpkt = nil; |
| } |
| m->isopenfd = 0; |
| m->internal = 0; |
| m->next = freemsg; |
| freemsg = m; |
| } |
| |
| Msg* |
| msgget(int n) |
| { |
| Msg *m; |
| |
| if(n < 0 || n >= nmsgtab) |
| return nil; |
| m = msgtab[n]; |
| if(m->ref == 0) |
| return nil; |
| if(verbose) fprint(2, "%T msgget %d = %p\n", n, m); |
| msgincref(m); |
| return m; |
| } |
| |
| |
| void* |
| emalloc(int n) |
| { |
| void *v; |
| |
| v = mallocz(n, 1); |
| if(v == nil){ |
| abort(); |
| sysfatal("out of memory allocating %d", n); |
| } |
| return v; |
| } |
| |
| void* |
| erealloc(void *v, int n) |
| { |
| v = realloc(v, n); |
| if(v == nil){ |
| abort(); |
| sysfatal("out of memory reallocating %d", n); |
| } |
| return v; |
| } |
| |
| typedef struct Qel Qel; |
| struct Qel |
| { |
| Qel *next; |
| void *p; |
| }; |
| |
| struct Queue |
| { |
| QLock lk; |
| Rendez r; |
| Qel *head; |
| Qel *tail; |
| }; |
| |
| Queue* |
| qalloc(void) |
| { |
| Queue *q; |
| |
| q = mallocz(sizeof(Queue), 1); |
| if(q == nil) |
| return nil; |
| q->r.l = &q->lk; |
| return q; |
| } |
| |
| int |
| sendq(Queue *q, void *p) |
| { |
| Qel *e; |
| |
| e = emalloc(sizeof(Qel)); |
| qlock(&q->lk); |
| e->p = p; |
| e->next = nil; |
| if(q->head == nil) |
| q->head = e; |
| else |
| q->tail->next = e; |
| q->tail = e; |
| rwakeup(&q->r); |
| qunlock(&q->lk); |
| return 0; |
| } |
| |
| void* |
| recvq(Queue *q) |
| { |
| void *p; |
| Qel *e; |
| |
| qlock(&q->lk); |
| while(q->head == nil) |
| rsleep(&q->r); |
| e = q->head; |
| q->head = e->next; |
| qunlock(&q->lk); |
| p = e->p; |
| free(e); |
| return p; |
| } |
| |
| uchar* |
| read9ppkt(Ioproc *io, int fd) |
| { |
| uchar buf[4], *pkt; |
| int n, nn; |
| |
| n = ioreadn(io, fd, buf, 4); |
| if(n != 4) |
| return nil; |
| n = GBIT32(buf); |
| if(n > MAXMSGSIZE) |
| return nil; |
| pkt = emalloc(n); |
| PBIT32(pkt, n); |
| nn = ioreadn(io, fd, pkt+4, n-4); |
| if(nn != n-4){ |
| free(pkt); |
| return nil; |
| } |
| /* would do this if we ever got one of these, but we only generate them |
| if(pkt[4] == Ropenfd){ |
| newfd = iorecvfd(io, fd); |
| PBIT32(pkt+n-4, newfd); |
| } |
| */ |
| return pkt; |
| } |
| |
| Msg* |
| mread9p(Ioproc *io, int fd) |
| { |
| int n, nn; |
| uchar *pkt; |
| Msg *m; |
| |
| if((pkt = read9ppkt(io, fd)) == nil) |
| return nil; |
| |
| m = msgnew(0); |
| m->tpkt = pkt; |
| n = GBIT32(pkt); |
| nn = convM2S(pkt, n, &m->tx); |
| if(nn != n){ |
| fprint(2, "%T read bad packet from %d\n", fd); |
| return nil; |
| } |
| return m; |
| } |
| |
| int |
| mwrite9p(Ioproc *io, int fd, uchar *pkt) |
| { |
| int n, nfd; |
| |
| n = GBIT32(pkt); |
| if(verbose > 2) fprint(2, "%T write %d %d %.*H\n", fd, n, n, pkt); |
| if(verbose > 1) fprint(2, "%T before iowrite\n"); |
| if(iowrite(io, fd, pkt, n) != n){ |
| fprint(2, "%T write error: %r\n"); |
| return -1; |
| } |
| if(verbose > 1) fprint(2, "%T after iowrite\n"); |
| if(pkt[4] == Ropenfd){ |
| nfd = GBIT32(pkt+n-4); |
| if(iosendfd(io, fd, nfd) < 0){ |
| fprint(2, "%T send fd error: %r\n"); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| void |
| restring(uchar *pkt, int pn, char *s) |
| { |
| int n; |
| |
| if(s < (char*)pkt || s >= (char*)pkt+pn) |
| return; |
| |
| n = strlen(s); |
| memmove(s+1, s, n); |
| PBIT16((uchar*)s-1, n); |
| } |
| |
| void |
| repack(Fcall *f, uchar **ppkt) |
| { |
| uint n, nn; |
| uchar *pkt; |
| |
| pkt = *ppkt; |
| n = GBIT32(pkt); |
| nn = sizeS2M(f); |
| if(nn > n){ |
| free(pkt); |
| pkt = emalloc(nn); |
| *ppkt = pkt; |
| } |
| n = convS2M(f, pkt, nn); |
| if(n <= BIT16SZ) |
| sysfatal("convS2M conversion error"); |
| if(n != nn) |
| sysfatal("convS2M and sizeS2M disagree"); |
| } |
| |
| void |
| rewritehdr(Fcall *f, uchar *pkt) |
| { |
| int i, n; |
| |
| n = GBIT32(pkt); |
| PBIT16(pkt+5, f->tag); |
| switch(f->type){ |
| case Tversion: |
| case Rversion: |
| restring(pkt, n, f->version); |
| break; |
| case Tauth: |
| PBIT32(pkt+7, f->afid); |
| restring(pkt, n, f->uname); |
| restring(pkt, n, f->aname); |
| break; |
| case Tflush: |
| PBIT16(pkt+7, f->oldtag); |
| break; |
| case Tattach: |
| restring(pkt, n, f->uname); |
| restring(pkt, n, f->aname); |
| PBIT32(pkt+7, f->fid); |
| PBIT32(pkt+11, f->afid); |
| break; |
| case Twalk: |
| PBIT32(pkt+7, f->fid); |
| PBIT32(pkt+11, f->newfid); |
| for(i=0; i<f->nwname; i++) |
| restring(pkt, n, f->wname[i]); |
| break; |
| case Tcreate: |
| restring(pkt, n, f->name); |
| /* fall through */ |
| case Topen: |
| case Tclunk: |
| case Tremove: |
| case Tstat: |
| case Twstat: |
| case Twrite: |
| PBIT32(pkt+7, f->fid); |
| break; |
| case Tread: |
| PBIT32(pkt+7, f->fid); |
| PBIT64(pkt+11, f->offset); |
| break; |
| case Rerror: |
| restring(pkt, n, f->ename); |
| break; |
| } |
| } |
| |
| static long |
| _iolisten(va_list *arg) |
| { |
| char *a, *b; |
| |
| a = va_arg(*arg, char*); |
| b = va_arg(*arg, char*); |
| return listen(a, b); |
| } |
| |
| int |
| iolisten(Ioproc *io, char *a, char *b) |
| { |
| return iocall(io, _iolisten, a, b); |
| } |
| |
| static long |
| _ioaccept(va_list *arg) |
| { |
| int fd; |
| char *dir; |
| |
| fd = va_arg(*arg, int); |
| dir = va_arg(*arg, char*); |
| return accept(fd, dir); |
| } |
| |
| int |
| ioaccept(Ioproc *io, int fd, char *dir) |
| { |
| return iocall(io, _ioaccept, fd, dir); |
| } |
| |
| int |
| timefmt(Fmt *fmt) |
| { |
| static char *mon[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
| "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; |
| vlong ns; |
| Tm tm; |
| ns = nsec(); |
| tm = *localtime(time(0)); |
| return fmtprint(fmt, "%s %2d %02d:%02d:%02d.%03d", |
| mon[tm.mon], tm.mday, tm.hour, tm.min, tm.sec, |
| (int)(ns%1000000000)/1000000); |
| } |