7 #define err err9pserve /* Darwin x86 */
13 MAXMSG = 64, /* per connection */
14 MAXMSGSIZE = 4*1024*1024
17 typedef struct Hash Hash;
18 typedef struct Fid Fid;
19 typedef struct Msg Msg;
20 typedef struct Conn Conn;
21 typedef struct Queue Queue;
97 void *gethash(Hash**, uint);
98 int puthash(Hash**, uint, void*);
99 int delhash(Hash**, uint, void*);
100 Msg *mread9p(Ioproc*, int, int);
101 int mwrite9p(Ioproc*, int, uchar*);
102 uchar *read9ppkt(Ioproc*, int);
103 int write9ppkt(int, uchar*);
108 void msgincref(Msg*);
112 void *erealloc(void*, int);
114 int sendq(Queue*, void*);
116 void connthread(void*);
117 void connoutthread(void*);
118 void listenthread(void*);
119 void outputthread(void*);
120 void inputthread(void*);
121 void rewritehdr(Fcall*, uchar*);
122 void repack(Fcall*, uchar**, int);
123 int tlisten(char*, char*);
124 int taccept(int, char*);
125 int iolisten(Ioproc*, char*, char*);
126 int ioaccept(Ioproc*, int, char*);
127 int iorecvfd(Ioproc*, int);
128 int iosendfd(Ioproc*, int, int);
129 void mainproc(void*);
130 int ignorepipe(void*, char*);
132 void dorootstat(void);
133 int stripudirread(Msg*);
134 int cvtustat(Fcall*, uchar**, int);
139 fprint(2, "usage: 9pserve [-lnv] [-A aname afid] [-M msize] address\n");
140 fprint(2, "\treads/writes 9P messages on stdin/stdout\n");
141 threadexitsall("usage");
145 extern int _threaddebuglevel;
147 threadmain(int argc, char **argv)
152 x = getenv("verbose9pserve");
155 fprint(2, "verbose9pserve %s => %d\n", x, verbose);
162 xaname = EARGF(usage());
163 xafid = atoi(EARGF(usage()));
167 msize = atoi(EARGF(usage()));
183 if(attached && !versioned){
184 fprint(2, "-A must be used with -M\n");
192 fmtinstall('T', timefmt);
194 if((afd = announce(addr, adir)) < 0)
195 sysfatal("announce %s: %r", addr);
197 if(strncmp(addr, "unix!", 5) == 0)
199 file = smprint("%s.log", addr);
201 sysfatal("smprint log: %r");
202 if((fd = create(file, OWRITE, 0666)) < 0)
203 sysfatal("create %s: %r", file);
208 if(verbose) fprint(2, "%T 9pserve running\n");
209 proccreate(mainproc, nil, STACK);
219 atnotify(ignorepipe, 1);
220 fmtinstall('D', dirfmt);
221 fmtinstall('M', dirmodefmt);
222 fmtinstall('F', fcallfmt);
223 fmtinstall('H', encodefmt);
230 f.version = "9P2000.u";
233 n = convS2M(&f, vbuf, sizeof vbuf);
235 sysfatal("convS2M conversion error");
236 if(verbose > 1) fprint(2, "%T * <- %F\n", &f);
237 nn = write(1, vbuf, n);
239 sysfatal("error writing Tversion: %r\n");
240 n = read9pmsg(0, vbuf, sizeof vbuf);
242 sysfatal("read9pmsg failure");
243 if(convM2S(vbuf, n, &f) != n)
244 sysfatal("convM2S failure");
247 if(verbose > 1) fprint(2, "%T * -> %F\n", &f);
248 dotu = strncmp(f.version, "9P2000.u", 8) == 0;
251 threadcreate(inputthread, nil, STACK);
252 threadcreate(outputthread, nil, STACK);
257 threadcreate(listenthread, nil, STACK);
262 ignorepipe(void *v, char *s)
265 if(strcmp(s, "sys: write on closed pipe") == 0)
267 if(strcmp(s, "sys: tstp") == 0)
269 if(strcmp(s, "sys: window size change") == 0)
271 fprint(2, "9pserve %s: %T note: %s\n", addr, s);
276 listenthread(void *arg)
283 threadsetname("listen %s", adir);
285 c = emalloc(sizeof(Conn));
286 c->fd = iolisten(io, adir, c->dir);
288 if(verbose) fprint(2, "%T listen: %r\n");
293 c->inc = chancreate(sizeof(void*), 0);
294 c->internal = chancreate(sizeof(void*), 0);
297 c->outqdead = chancreate(sizeof(void*), 0);
298 if(verbose) fprint(2, "%T incoming call on %s\n", c->dir);
299 threadcreate(connthread, c, STACK);
308 n = sizeS2Mu(&m->rx, m->c->dotu);
309 m->rpkt = emalloc(n);
310 nn = convS2Mu(&m->rx, m->rpkt, n, m->c->dotu);
312 sysfatal("convS2Mu conversion error");
314 sysfatal("sizeS2Mu and convS2Mu disagree");
315 sendq(m->c->outq, m);
323 n = sizeS2Mu(&m->tx, m->c->dotu);
324 m->tpkt = emalloc(n);
325 nn = convS2Mu(&m->tx, m->tpkt, n, m->c->dotu);
327 sysfatal("convS2Mu conversion error");
329 sysfatal("sizeS2Mu and convS2Mu disagree");
334 err(Msg *m, char *ename)
338 m->rx.tag = m->tx.tag;
347 t = emalloc(strlen(s)+1);
353 connthread(void *arg)
358 Msg *m, *om, *mm, sync;
363 threadsetname("conn %s", c->dir);
365 fd = ioaccept(io, c->fd, c->dir);
367 if(verbose) fprint(2, "%T accept %s: %r\n", c->dir);
372 threadcreate(connoutthread, c, STACK);
373 while((m = mread9p(io, c->fd, c->dotu)) != nil){
374 if(verbose > 1) fprint(2, "%T fd#%d -> %F\n", c->fd, &m->tx);
378 if(verbose > 1) fprint(2, "%T fd#%d: new msg %p\n", c->fd, m);
379 if(puthash(c->tag, m->tx.tag, m) < 0){
380 err(m, "duplicate tag");
386 m->rx.tag = m->tx.tag;
387 m->rx.msize = m->tx.msize;
388 if(m->rx.msize > msize)
390 m->rx.version = "9P2000";
392 if(dotu && strncmp(m->tx.version, "9P2000.u", 8) == 0){
393 m->rx.version = "9P2000.u";
396 m->rx.type = Rversion;
400 if((m->oldm = gethash(c->tag, m->tx.oldtag)) == nil){
401 m->rx.tag = m->tx.tag;
410 if(m->tx.afid != NOFID
411 && (m->afid = gethash(c->fid, m->tx.afid)) == nil){
412 err(m, "unknown fid");
417 m->fid = fidnew(m->tx.fid);
418 if(puthash(c->fid, m->tx.fid, m->fid) < 0){
419 err(m, "duplicate fid");
423 if(attached && m->afid==nil){
424 if(m->tx.aname[0] && strcmp(xaname, m->tx.aname) != 0){
425 err(m, "invalid attach name");
429 m->tx.aname = xaname;
430 m->tx.uname = getuser(); /* what srv.c used */
431 repack(&m->tx, &m->tpkt, c->dotu);
435 if((m->fid = gethash(c->fid, m->tx.fid)) == nil){
436 err(m, "unknown fid");
440 if(m->tx.newfid == m->tx.fid){
444 m->newfid = fidnew(m->tx.newfid);
445 if(puthash(c->fid, m->tx.newfid, m->newfid) < 0){
446 err(m, "duplicate fid");
454 err(m, "authentication not required");
458 err(m, "authentication rejected");
461 m->afid = fidnew(m->tx.afid);
462 if(puthash(c->fid, m->tx.afid, m->afid) < 0){
463 err(m, "duplicate fid");
469 if(dotu && !c->dotu && (m->tx.perm&(DMSYMLINK|DMDEVICE|DMNAMEDPIPE|DMSOCKET))){
470 err(m, "unsupported file type");
475 if(m->tx.mode&~(OTRUNC|3)){
476 err(m, "bad openfd mode");
491 if((m->fid = gethash(c->fid, m->tx.fid)) == nil){
492 err(m, "unknown fid");
496 if(m->tx.type==Twstat && dotu && !c->dotu){
497 if(cvtustat(&m->tx, &m->tpkt, 1) < 0){
498 err(m, "cannot convert stat buffer");
502 if(m->tx.type==Tread && m->fid->isdir && dotu && !c->dotu){
503 if(m->tx.offset = m->fid->coffset)
504 m->tx.offset = m->fid->offset;
506 m->fid->offset = m->fid->coffset;
511 /* have everything - translate and send */
516 m->tx.fid = m->fid->fid;
518 m->tx.newfid = m->newfid->fid;
520 m->tx.afid = m->afid->fid;
522 m->tx.oldtag = m->oldm->tag;
523 /* reference passes to outq */
525 while(c->nmsg >= MAXMSG){
531 if(verbose) fprint(2, "%T fd#%d eof; flushing conn\n", c->fd);
533 /* flush all outstanding messages */
534 for(i=0; i<NHASH; i++){
535 while((h = c->tag[i]) != nil){
537 msgincref(om); /* for us */
544 m->tx.oldtag = om->tag;
547 msgincref(m); /* for outq */
549 mm = recvp(c->internal);
551 msgput(m); /* got from recvp */
552 msgput(m); /* got from msgnew */
553 if(delhash(c->tag, om->ctag, om) == 0)
554 msgput(om); /* got from hash table */
555 msgput(om); /* got from msgincref */
560 * outputthread has written all its messages
561 * to the remote connection (because we've gotten all the replies!),
562 * but it might not have gotten a chance to msgput
563 * the very last one. sync up to make sure.
565 memset(&sync, 0, sizeof sync);
571 /* everything is quiet; can close the local output queue. */
575 /* should be no messages left anywhere. */
576 assert(c->nmsg == 0);
578 /* clunk all outstanding fids */
579 for(i=0; i<NHASH; i++){
580 for(h=c->fid[i]; h; h=hnext){
593 mm = recvp(c->internal);
596 msgput(m); /* got from recvp */
597 msgput(m); /* got from msgnew */
598 fidput(f); /* got from hash table */
606 assert(c->nmsg == 0);
607 assert(c->nfid == 0);
609 chanfree(c->internal);
619 openfdthread(void *v)
632 threadsetname("openfd %s", c->fdfid);
635 if(c->fdmode == OREAD){
637 if(verbose) fprint(2, "%T tread...");
642 m->tx.count = msize - IOHDRSZ;
643 m->tx.fid = fid->fid;
651 if(m->rx.type == Rerror){
652 /* fprint(2, "%T read error: %s\n", m->rx.ename); */
658 if(iowrite(io, c->fd, m->rx.data, m->rx.count) != m->rx.count){
659 /* fprint(2, "%T pipe write error: %r\n"); */
668 if(verbose) fprint(2, "%T twrite...");
672 if((n=ioread(io, c->fd, buf, n)) <= 0){
674 fprint(2, "%T pipe read error: %r\n");
681 m->tx.fid = fid->fid;
691 if(m->rx.type == Rerror){
692 /* fprint(2, "%T write error: %s\n", m->rx.ename); */
700 if(verbose) fprint(2, "%T eof on %d fid %d\n", c->fd, fid->fid);
707 if(verbose) fprint(2, "%T eof on %d fid %d ref %d\n", c->fd, fid->fid, fid->ref);
708 if(--fid->openfd == 0){
714 m->tx.fid = fid->fid;
725 chanfree(c->internal);
738 rerrstr(errs, sizeof errs);
740 /* XXX return here? */
742 if(verbose) fprint(2, "%T xopen pipe %d %d...", p[0], p[1]);
744 /* now we're committed. */
746 /* a new connection for this fid */
747 nc = emalloc(sizeof(Conn));
748 nc->internal = chancreate(sizeof(void*), 0);
754 nc->fdmode = m->tx.mode;
757 /* a thread to tend the pipe */
758 threadcreate(openfdthread, nc, STACK);
760 /* if mode is ORDWR, that openfdthread will write; start a reader */
761 if((m->tx.mode&3) == ORDWR){
762 nc = emalloc(sizeof(Conn));
763 nc->internal = chancreate(sizeof(void*), 0);
768 nc->fd = dup(p[0], -1);
769 threadcreate(openfdthread, nc, STACK);
772 /* steal fid from other connection */
773 if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0)
776 /* rewrite as Ropenfd */
777 m->rx.type = Ropenfd;
779 m->rpkt = erealloc(m->rpkt, n+4);
780 PBIT32(m->rpkt+n, p[1]);
783 m->rpkt[4] = Ropenfd;
789 connoutthread(void *arg)
799 threadsetname("connout %s", c->dir);
800 while((m = recvq(c->outq)) != nil){
801 err = m->tx.type+1 != m->rx.type;
802 if(!err && m->isopenfd)
809 if(delhash(om->c->tag, om->ctag, om) == 0)
815 if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0)
820 if(verbose) fprint(2, "%T auth error\n");
821 if(delhash(m->c->fid, m->afid->cfid, m->afid) == 0)
827 if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0)
831 if(err || m->rx.nwqid < m->tx.nwname)
832 if(m->tx.fid != m->tx.newfid && m->newfid)
833 if(delhash(m->c->fid, m->newfid->cfid, m->newfid) == 0)
837 if(!err && m->fid->isdir && dotu && !m->c->dotu){
838 m->fid->offset += m->rx.count;
840 m->fid->coffset += m->rx.count;
844 if(!err && dotu && !m->c->dotu)
845 cvtustat(&m->rx, &m->rpkt, 0);
849 m->fid->isdir = (m->rx.qid.type & QTDIR);
852 if(m->rx.type==Rerror && dotu && !c->dotu){
853 ename = estrdup(m->rx.ename);
855 repack(&m->rx, &m->rpkt, c->dotu);
859 if(delhash(m->c->tag, m->ctag, m) == 0)
861 if(verbose > 1) fprint(2, "%T fd#%d <- %F\n", c->fd, &m->rx);
862 rewritehdr(&m->rx, m->rpkt);
863 if(mwrite9p(io, c->fd, m->rpkt) < 0)
864 if(verbose) fprint(2, "%T write error: %r\n");
866 if(c->inputstalled && c->nmsg < MAXMSG)
872 sendp(c->outqdead, nil);
876 outputthread(void *arg)
883 threadsetname("output");
884 while((m = recvq(outq)) != nil){
886 sendp(m->c->outqdead, nil);
889 if(verbose > 1) fprint(2, "%T * <- %F\n", &m->tx);
890 rewritehdr(&m->tx, m->tpkt);
891 if(mwrite9p(io, 1, m->tpkt) < 0)
892 sysfatal("output error: %r");
896 fprint(2, "%T output eof\n");
901 inputthread(void *arg)
908 threadsetname("input");
909 if(verbose) fprint(2, "%T input thread\n");
912 while((pkt = read9ppkt(io, 0)) != nil){
915 fprint(2, "%T short 9P packet from server\n");
919 if(verbose > 2) fprint(2, "%T read %.*H\n", n, pkt);
921 if((m = msgget(tag)) == nil){
922 fprint(2, "%T unexpected 9P response tag %d\n", tag);
926 if((nn = convM2Su(pkt, n, &m->rx, dotu)) != n){
927 fprint(2, "%T bad packet - convM2S %d but %d\n", nn, n);
932 if(verbose > 1) fprint(2, "%T * -> %F%s\n", &m->rx,
933 m->internal ? " (internal)" : "");
937 sendp(m->c->internal, m);
939 sendq(m->c->outq, m);
944 /*fprint(2, "%T input eof\n"); */
949 gethash(Hash **ht, uint n)
953 for(h=ht[n%NHASH]; h; h=h->next)
960 delhash(Hash **ht, uint n, void *v)
964 for(l=&ht[n%NHASH]; h=*l; l=&h->next)
967 if(verbose) fprint(2, "%T delhash %d got %p want %p\n", n, h->v, v);
978 puthash(Hash **ht, uint n, void *v)
984 h = emalloc(sizeof(Hash));
985 h->next = ht[n%NHASH];
1002 fidtab = erealloc(fidtab, (nfidtab+1)*sizeof(fidtab[0]));
1003 if(nfidtab == xafid){
1004 fidtab[nfidtab++] = nil;
1005 fidtab = erealloc(fidtab, (nfidtab+1)*sizeof(fidtab[0]));
1007 fidtab[nfidtab] = emalloc(sizeof(Fid));
1008 freefid = fidtab[nfidtab];
1009 freefid->fid = nfidtab++;
1042 if(verbose > 1) fprint(2, "%T msgincref @0x%lux %p tag %d/%d ref %d=>%d\n",
1043 getcallerpc(&m), m, m->tag, m->ctag, m->ref, m->ref+1);
1053 msgtab = erealloc(msgtab, (nmsgtab+1)*sizeof(msgtab[0]));
1054 msgtab[nmsgtab] = emalloc(sizeof(Msg));
1055 freemsg = msgtab[nmsgtab];
1056 freemsg->tag = nmsgtab++;
1061 if(verbose > 1) fprint(2, "%T msgnew @0x%lux %p tag %d ref %d\n",
1062 getcallerpc(&x), m, m->tag, m->ref);
1068 * Clear data associated with connections, so that
1069 * if all msgs have been msgcleared, the connection
1070 * can be freed. Note that this does *not* free the tpkt
1071 * and rpkt; they are freed in msgput with the msg itself.
1072 * The io write thread might still be holding a ref to msg
1073 * even once the connection has finished with it.
1098 if(m->rx.type == Ropenfd && m->rx.unixfd >= 0){
1099 close(m->rx.unixfd);
1110 if(verbose > 1) fprint(2, "%T msgput 0x%lux %p tag %d/%d ref %d\n",
1111 getcallerpc(&m), m, m->tag, m->ctag, m->ref);
1136 if(n < 0 || n >= nmsgtab)
1141 if(verbose) fprint(2, "%T msgget %d = %p\n", n, m);
1155 sysfatal("out of memory allocating %d", n);
1161 erealloc(void *v, int n)
1166 sysfatal("out of memory reallocating %d", n);
1171 typedef struct Qel Qel;
1191 q = mallocz(sizeof(Queue), 1);
1199 sendq(Queue *q, void *p)
1203 e = emalloc(sizeof(Qel));
1224 while(q->head == nil)
1235 read9ppkt(Ioproc *io, int fd)
1240 n = ioreadn(io, fd, buf, 4);
1248 nn = ioreadn(io, fd, pkt+4, n-4);
1253 /* would do this if we ever got one of these, but we only generate them
1254 if(pkt[4] == Ropenfd){
1255 newfd = iorecvfd(io, fd);
1256 PBIT32(pkt+n-4, newfd);
1263 mread9p(Ioproc *io, int fd, int dotu)
1269 if((pkt = read9ppkt(io, fd)) == nil)
1275 nn = convM2Su(pkt, n, &m->tx, dotu);
1277 fprint(2, "%T read bad packet from %d\n", fd);
1284 mwrite9p(Ioproc *io, int fd, uchar *pkt)
1289 if(verbose > 2) fprint(2, "%T write %d %d %.*H\n", fd, n, n, pkt);
1290 if(verbose > 1) fprint(2, "%T before iowrite\n");
1291 if(iowrite(io, fd, pkt, n) != n){
1292 fprint(2, "%T write error: %r\n");
1295 if(verbose > 1) fprint(2, "%T after iowrite\n");
1296 if(pkt[4] == Ropenfd){
1297 nfd = GBIT32(pkt+n-4);
1298 if(iosendfd(io, fd, nfd) < 0){
1299 fprint(2, "%T send fd error: %r\n");
1307 restring(uchar *pkt, int pn, char *s)
1311 if(s < (char*)pkt || s >= (char*)pkt+pn)
1316 PBIT16((uchar*)s-1, n);
1320 repack(Fcall *f, uchar **ppkt, int dotu)
1327 nn = sizeS2Mu(f, dotu);
1333 n = convS2Mu(f, pkt, nn, dotu);
1335 sysfatal("convS2M conversion error");
1337 sysfatal("convS2Mu and sizeS2Mu disagree");
1341 rewritehdr(Fcall *f, uchar *pkt)
1346 PBIT16(pkt+5, f->tag);
1350 restring(pkt, n, f->version);
1353 PBIT32(pkt+7, f->afid);
1354 restring(pkt, n, f->uname);
1355 restring(pkt, n, f->aname);
1358 PBIT16(pkt+7, f->oldtag);
1361 restring(pkt, n, f->uname);
1362 restring(pkt, n, f->aname);
1363 PBIT32(pkt+7, f->fid);
1364 PBIT32(pkt+11, f->afid);
1367 PBIT32(pkt+7, f->fid);
1368 PBIT32(pkt+11, f->newfid);
1369 for(i=0; i<f->nwname; i++)
1370 restring(pkt, n, f->wname[i]);
1373 restring(pkt, n, f->name);
1381 PBIT32(pkt+7, f->fid);
1384 PBIT32(pkt+7, f->fid);
1385 PBIT64(pkt+11, f->offset);
1388 restring(pkt, n, f->ename);
1394 _iolisten(va_list *arg)
1398 a = va_arg(*arg, char*);
1399 b = va_arg(*arg, char*);
1400 return listen(a, b);
1404 iolisten(Ioproc *io, char *a, char *b)
1406 return iocall(io, _iolisten, a, b);
1410 _ioaccept(va_list *arg)
1415 fd = va_arg(*arg, int);
1416 dir = va_arg(*arg, char*);
1417 return accept(fd, dir);
1421 ioaccept(Ioproc *io, int fd, char *dir)
1423 return iocall(io, _ioaccept, fd, dir);
1429 static char *mon[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1430 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
1434 tm = *localtime(time(0));
1435 return fmtprint(fmt, "%s %2d %02d:%02d:%02d.%03d",
1436 mon[tm.mon], tm.mday, tm.hour, tm.min, tm.sec,
1437 (int)(ns%1000000000)/1000000);
1441 cvtustat(Fcall *f, uchar **fpkt, int tounix)
1448 str = emalloc(f->nstat);
1449 n = convM2Du(f->stat, f->nstat, &dir, str, !tounix);
1455 n = sizeD2Mu(&dir, tounix);
1457 if(convD2Mu(&dir, buf, n, tounix) != n)
1458 sysfatal("convD2Mu conversion error");
1462 repack(f, fpkt, dotu);
1464 f->stat = nil; /* is this okay ??? */
1471 stripudirread(Msg* msg)
1484 for(i = 0; i < rx->count; i += m){
1485 m = BIT16SZ + GBIT16(&rx->data[i]);
1486 if(statchecku((uchar*)&rx->data[i], m, 1) < 0)
1494 buf = emalloc(rx->count);
1497 for(i = 0; i < rx->count; i += m){
1498 m = BIT16SZ + GBIT16(&rx->data[i]);
1499 if(convM2Du((uchar*)&rx->data[i], m, &d, str, 1) != m){
1505 n = convD2M(&d, &buf[nn], rx->count - nn);
1516 rx->data = (char*)buf;
1518 repack(&msg->rx, &msg->rpkt, 0);
1521 rx->data = nil; /* is this okay ??? */