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] [-c addr] [-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)
149 char *file, *x, *addr;
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()));
170 addr = netmkaddr(EARGF(usage()), "net", "9fs");
171 if((fd = dial(addr, nil, nil, nil)) < 0)
172 sysfatal("dial %s: %r", addr);
192 if(attached && !versioned){
193 fprint(2, "-A must be used with -M\n");
201 fmtinstall('T', timefmt);
203 if((afd = announce(addr, adir)) < 0)
204 sysfatal("announce %s: %r", addr);
206 if(strncmp(addr, "unix!", 5) == 0)
208 file = smprint("%s.log", addr);
210 sysfatal("smprint log: %r");
211 if((fd = create(file, OWRITE, 0666)) < 0)
212 sysfatal("create %s: %r", file);
217 if(verbose) fprint(2, "%T 9pserve running\n");
218 proccreate(mainproc, nil, STACK);
228 atnotify(ignorepipe, 1);
229 fmtinstall('D', dirfmt);
230 fmtinstall('M', dirmodefmt);
231 fmtinstall('F', fcallfmt);
232 fmtinstall('H', encodefmt);
239 f.version = "9P2000.u";
242 n = convS2M(&f, vbuf, sizeof vbuf);
244 sysfatal("convS2M conversion error");
245 if(verbose > 1) fprint(2, "%T * <- %F\n", &f);
246 nn = write(1, vbuf, n);
248 sysfatal("error writing Tversion: %r\n");
249 n = read9pmsg(0, vbuf, sizeof vbuf);
251 sysfatal("read9pmsg failure");
252 if(convM2S(vbuf, n, &f) != n)
253 sysfatal("convM2S failure");
256 if(verbose > 1) fprint(2, "%T * -> %F\n", &f);
257 dotu = strncmp(f.version, "9P2000.u", 8) == 0;
260 threadcreate(inputthread, nil, STACK);
261 threadcreate(outputthread, nil, STACK);
266 threadcreate(listenthread, nil, STACK);
271 ignorepipe(void *v, char *s)
274 if(strcmp(s, "sys: write on closed pipe") == 0)
276 if(strcmp(s, "sys: tstp") == 0)
278 if(strcmp(s, "sys: window size change") == 0)
280 fprint(2, "9pserve %s: %T note: %s\n", addr, s);
285 listenthread(void *arg)
292 threadsetname("listen %s", adir);
294 c = emalloc(sizeof(Conn));
295 c->fd = iolisten(io, adir, c->dir);
297 if(verbose) fprint(2, "%T listen: %r\n");
302 c->inc = chancreate(sizeof(void*), 0);
303 c->internal = chancreate(sizeof(void*), 0);
306 c->outqdead = chancreate(sizeof(void*), 0);
307 if(verbose) fprint(2, "%T incoming call on %s\n", c->dir);
308 threadcreate(connthread, c, STACK);
317 n = sizeS2Mu(&m->rx, m->c->dotu);
318 m->rpkt = emalloc(n);
319 nn = convS2Mu(&m->rx, m->rpkt, n, m->c->dotu);
321 sysfatal("convS2Mu conversion error");
323 sysfatal("sizeS2Mu and convS2Mu disagree");
324 sendq(m->c->outq, m);
332 n = sizeS2Mu(&m->tx, m->c->dotu);
333 m->tpkt = emalloc(n);
334 nn = convS2Mu(&m->tx, m->tpkt, n, m->c->dotu);
336 sysfatal("convS2Mu conversion error");
338 sysfatal("sizeS2Mu and convS2Mu disagree");
343 err(Msg *m, char *ename)
347 m->rx.tag = m->tx.tag;
356 t = emalloc(strlen(s)+1);
362 connthread(void *arg)
367 Msg *m, *om, *mm, sync;
372 threadsetname("conn %s", c->dir);
374 fd = ioaccept(io, c->fd, c->dir);
376 if(verbose) fprint(2, "%T accept %s: %r\n", c->dir);
381 threadcreate(connoutthread, c, STACK);
382 while((m = mread9p(io, c->fd, c->dotu)) != nil){
383 if(verbose > 1) fprint(2, "%T fd#%d -> %F\n", c->fd, &m->tx);
387 if(verbose > 1) fprint(2, "%T fd#%d: new msg %p\n", c->fd, m);
388 if(puthash(c->tag, m->tx.tag, m) < 0){
389 err(m, "duplicate tag");
395 m->rx.tag = m->tx.tag;
396 m->rx.msize = m->tx.msize;
397 if(m->rx.msize > msize)
399 m->rx.version = "9P2000";
401 if(dotu && strncmp(m->tx.version, "9P2000.u", 8) == 0){
402 m->rx.version = "9P2000.u";
405 m->rx.type = Rversion;
409 if((m->oldm = gethash(c->tag, m->tx.oldtag)) == nil){
410 m->rx.tag = m->tx.tag;
419 if(m->tx.afid != NOFID
420 && (m->afid = gethash(c->fid, m->tx.afid)) == nil){
421 err(m, "unknown fid");
426 m->fid = fidnew(m->tx.fid);
427 if(puthash(c->fid, m->tx.fid, m->fid) < 0){
428 err(m, "duplicate fid");
432 if(attached && m->afid==nil){
433 if(m->tx.aname[0] && strcmp(xaname, m->tx.aname) != 0){
434 err(m, "invalid attach name");
438 m->tx.aname = xaname;
439 m->tx.uname = getuser(); /* what srv.c used */
440 repack(&m->tx, &m->tpkt, c->dotu);
444 if((m->fid = gethash(c->fid, m->tx.fid)) == nil){
445 err(m, "unknown fid");
449 if(m->tx.newfid == m->tx.fid){
453 m->newfid = fidnew(m->tx.newfid);
454 if(puthash(c->fid, m->tx.newfid, m->newfid) < 0){
455 err(m, "duplicate fid");
463 err(m, "authentication not required");
467 err(m, "authentication rejected");
470 m->afid = fidnew(m->tx.afid);
471 if(puthash(c->fid, m->tx.afid, m->afid) < 0){
472 err(m, "duplicate fid");
478 if(dotu && !c->dotu && (m->tx.perm&(DMSYMLINK|DMDEVICE|DMNAMEDPIPE|DMSOCKET))){
479 err(m, "unsupported file type");
484 if(m->tx.mode&~(OTRUNC|3)){
485 err(m, "bad openfd mode");
500 if((m->fid = gethash(c->fid, m->tx.fid)) == nil){
501 err(m, "unknown fid");
505 if(m->tx.type==Twstat && dotu && !c->dotu){
506 if(cvtustat(&m->tx, &m->tpkt, 1) < 0){
507 err(m, "cannot convert stat buffer");
511 if(m->tx.type==Tread && m->fid->isdir && dotu && !c->dotu){
512 if(m->tx.offset = m->fid->coffset)
513 m->tx.offset = m->fid->offset;
515 m->fid->offset = m->fid->coffset;
520 /* have everything - translate and send */
525 m->tx.fid = m->fid->fid;
527 m->tx.newfid = m->newfid->fid;
529 m->tx.afid = m->afid->fid;
531 m->tx.oldtag = m->oldm->tag;
532 /* reference passes to outq */
534 while(c->nmsg >= MAXMSG){
540 if(verbose) fprint(2, "%T fd#%d eof; flushing conn\n", c->fd);
542 /* flush all outstanding messages */
543 for(i=0; i<NHASH; i++){
544 while((h = c->tag[i]) != nil){
546 msgincref(om); /* for us */
553 m->tx.oldtag = om->tag;
556 msgincref(m); /* for outq */
558 mm = recvp(c->internal);
560 msgput(m); /* got from recvp */
561 msgput(m); /* got from msgnew */
562 if(delhash(c->tag, om->ctag, om) == 0)
563 msgput(om); /* got from hash table */
564 msgput(om); /* got from msgincref */
569 * outputthread has written all its messages
570 * to the remote connection (because we've gotten all the replies!),
571 * but it might not have gotten a chance to msgput
572 * the very last one. sync up to make sure.
574 memset(&sync, 0, sizeof sync);
580 /* everything is quiet; can close the local output queue. */
584 /* should be no messages left anywhere. */
585 assert(c->nmsg == 0);
587 /* clunk all outstanding fids */
588 for(i=0; i<NHASH; i++){
589 for(h=c->fid[i]; h; h=hnext){
602 mm = recvp(c->internal);
605 msgput(m); /* got from recvp */
606 msgput(m); /* got from msgnew */
607 fidput(f); /* got from hash table */
615 assert(c->nmsg == 0);
616 assert(c->nfid == 0);
618 chanfree(c->internal);
628 openfdthread(void *v)
641 threadsetname("openfd %s", c->fdfid);
644 if(c->fdmode == OREAD){
646 if(verbose) fprint(2, "%T tread...");
651 m->tx.count = msize - IOHDRSZ;
652 m->tx.fid = fid->fid;
660 if(m->rx.type == Rerror){
661 /* fprint(2, "%T read error: %s\n", m->rx.ename); */
667 if(iowrite(io, c->fd, m->rx.data, m->rx.count) != m->rx.count){
668 /* fprint(2, "%T pipe write error: %r\n"); */
677 if(verbose) fprint(2, "%T twrite...");
681 if((n=ioread(io, c->fd, buf, n)) <= 0){
683 fprint(2, "%T pipe read error: %r\n");
690 m->tx.fid = fid->fid;
700 if(m->rx.type == Rerror){
701 /* fprint(2, "%T write error: %s\n", m->rx.ename); */
709 if(verbose) fprint(2, "%T eof on %d fid %d\n", c->fd, fid->fid);
716 if(verbose) fprint(2, "%T eof on %d fid %d ref %d\n", c->fd, fid->fid, fid->ref);
717 if(--fid->openfd == 0){
723 m->tx.fid = fid->fid;
734 chanfree(c->internal);
747 rerrstr(errs, sizeof errs);
749 /* XXX return here? */
751 if(verbose) fprint(2, "%T xopen pipe %d %d...", p[0], p[1]);
753 /* now we're committed. */
755 /* a new connection for this fid */
756 nc = emalloc(sizeof(Conn));
757 nc->internal = chancreate(sizeof(void*), 0);
763 nc->fdmode = m->tx.mode;
766 /* a thread to tend the pipe */
767 threadcreate(openfdthread, nc, STACK);
769 /* if mode is ORDWR, that openfdthread will write; start a reader */
770 if((m->tx.mode&3) == ORDWR){
771 nc = emalloc(sizeof(Conn));
772 nc->internal = chancreate(sizeof(void*), 0);
777 nc->fd = dup(p[0], -1);
778 threadcreate(openfdthread, nc, STACK);
781 /* steal fid from other connection */
782 if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0)
785 /* rewrite as Ropenfd */
786 m->rx.type = Ropenfd;
788 m->rpkt = erealloc(m->rpkt, n+4);
789 PBIT32(m->rpkt+n, p[1]);
792 m->rpkt[4] = Ropenfd;
798 connoutthread(void *arg)
808 threadsetname("connout %s", c->dir);
809 while((m = recvq(c->outq)) != nil){
810 err = m->tx.type+1 != m->rx.type;
811 if(!err && m->isopenfd)
818 if(delhash(om->c->tag, om->ctag, om) == 0)
824 if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0)
829 if(verbose) fprint(2, "%T auth error\n");
830 if(delhash(m->c->fid, m->afid->cfid, m->afid) == 0)
836 if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0)
840 if(err || m->rx.nwqid < m->tx.nwname)
841 if(m->tx.fid != m->tx.newfid && m->newfid)
842 if(delhash(m->c->fid, m->newfid->cfid, m->newfid) == 0)
846 if(!err && m->fid->isdir && dotu && !m->c->dotu){
847 m->fid->offset += m->rx.count;
849 m->fid->coffset += m->rx.count;
853 if(!err && dotu && !m->c->dotu)
854 cvtustat(&m->rx, &m->rpkt, 0);
858 m->fid->isdir = (m->rx.qid.type & QTDIR);
861 if(m->rx.type==Rerror && dotu && !c->dotu){
862 ename = estrdup(m->rx.ename);
864 repack(&m->rx, &m->rpkt, c->dotu);
868 if(delhash(m->c->tag, m->ctag, m) == 0)
870 if(verbose > 1) fprint(2, "%T fd#%d <- %F\n", c->fd, &m->rx);
871 rewritehdr(&m->rx, m->rpkt);
872 if(mwrite9p(io, c->fd, m->rpkt) < 0)
873 if(verbose) fprint(2, "%T write error: %r\n");
875 if(c->inputstalled && c->nmsg < MAXMSG)
881 sendp(c->outqdead, nil);
885 outputthread(void *arg)
892 threadsetname("output");
893 while((m = recvq(outq)) != nil){
895 sendp(m->c->outqdead, nil);
898 if(verbose > 1) fprint(2, "%T * <- %F\n", &m->tx);
899 rewritehdr(&m->tx, m->tpkt);
900 if(mwrite9p(io, 1, m->tpkt) < 0)
901 sysfatal("output error: %r");
905 fprint(2, "%T output eof\n");
910 inputthread(void *arg)
917 threadsetname("input");
918 if(verbose) fprint(2, "%T input thread\n");
921 while((pkt = read9ppkt(io, 0)) != nil){
924 fprint(2, "%T short 9P packet from server\n");
928 if(verbose > 2) fprint(2, "%T read %.*H\n", n, pkt);
930 if((m = msgget(tag)) == nil){
931 fprint(2, "%T unexpected 9P response tag %d\n", tag);
935 if((nn = convM2Su(pkt, n, &m->rx, dotu)) != n){
936 fprint(2, "%T bad packet - convM2S %d but %d\n", nn, n);
941 if(verbose > 1) fprint(2, "%T * -> %F%s\n", &m->rx,
942 m->internal ? " (internal)" : "");
946 sendp(m->c->internal, m);
948 sendq(m->c->outq, m);
953 /*fprint(2, "%T input eof\n"); */
958 gethash(Hash **ht, uint n)
962 for(h=ht[n%NHASH]; h; h=h->next)
969 delhash(Hash **ht, uint n, void *v)
973 for(l=&ht[n%NHASH]; h=*l; l=&h->next)
976 if(verbose) fprint(2, "%T delhash %d got %p want %p\n", n, h->v, v);
987 puthash(Hash **ht, uint n, void *v)
993 h = emalloc(sizeof(Hash));
994 h->next = ht[n%NHASH];
1011 fidtab = erealloc(fidtab, (nfidtab+1)*sizeof(fidtab[0]));
1012 if(nfidtab == xafid){
1013 fidtab[nfidtab++] = nil;
1014 fidtab = erealloc(fidtab, (nfidtab+1)*sizeof(fidtab[0]));
1016 fidtab[nfidtab] = emalloc(sizeof(Fid));
1017 freefid = fidtab[nfidtab];
1018 freefid->fid = nfidtab++;
1051 if(verbose > 1) fprint(2, "%T msgincref @0x%lux %p tag %d/%d ref %d=>%d\n",
1052 getcallerpc(&m), m, m->tag, m->ctag, m->ref, m->ref+1);
1062 msgtab = erealloc(msgtab, (nmsgtab+1)*sizeof(msgtab[0]));
1063 msgtab[nmsgtab] = emalloc(sizeof(Msg));
1064 freemsg = msgtab[nmsgtab];
1065 freemsg->tag = nmsgtab++;
1070 if(verbose > 1) fprint(2, "%T msgnew @0x%lux %p tag %d ref %d\n",
1071 getcallerpc(&x), m, m->tag, m->ref);
1077 * Clear data associated with connections, so that
1078 * if all msgs have been msgcleared, the connection
1079 * can be freed. Note that this does *not* free the tpkt
1080 * and rpkt; they are freed in msgput with the msg itself.
1081 * The io write thread might still be holding a ref to msg
1082 * even once the connection has finished with it.
1107 if(m->rx.type == Ropenfd && m->rx.unixfd >= 0){
1108 close(m->rx.unixfd);
1119 if(verbose > 1) fprint(2, "%T msgput 0x%lux %p tag %d/%d ref %d\n",
1120 getcallerpc(&m), m, m->tag, m->ctag, m->ref);
1145 if(n < 0 || n >= nmsgtab)
1150 if(verbose) fprint(2, "%T msgget %d = %p\n", n, m);
1164 sysfatal("out of memory allocating %d", n);
1170 erealloc(void *v, int n)
1175 sysfatal("out of memory reallocating %d", n);
1180 typedef struct Qel Qel;
1200 q = mallocz(sizeof(Queue), 1);
1208 sendq(Queue *q, void *p)
1212 e = emalloc(sizeof(Qel));
1233 while(q->head == nil)
1244 read9ppkt(Ioproc *io, int fd)
1249 n = ioreadn(io, fd, buf, 4);
1257 nn = ioreadn(io, fd, pkt+4, n-4);
1262 /* would do this if we ever got one of these, but we only generate them
1263 if(pkt[4] == Ropenfd){
1264 newfd = iorecvfd(io, fd);
1265 PBIT32(pkt+n-4, newfd);
1272 mread9p(Ioproc *io, int fd, int dotu)
1278 if((pkt = read9ppkt(io, fd)) == nil)
1284 nn = convM2Su(pkt, n, &m->tx, dotu);
1286 fprint(2, "%T read bad packet from %d\n", fd);
1293 mwrite9p(Ioproc *io, int fd, uchar *pkt)
1298 if(verbose > 2) fprint(2, "%T write %d %d %.*H\n", fd, n, n, pkt);
1299 if(verbose > 1) fprint(2, "%T before iowrite\n");
1300 if(iowrite(io, fd, pkt, n) != n){
1301 fprint(2, "%T write error: %r\n");
1304 if(verbose > 1) fprint(2, "%T after iowrite\n");
1305 if(pkt[4] == Ropenfd){
1306 nfd = GBIT32(pkt+n-4);
1307 if(iosendfd(io, fd, nfd) < 0){
1308 fprint(2, "%T send fd error: %r\n");
1316 restring(uchar *pkt, int pn, char *s)
1320 if(s < (char*)pkt || s >= (char*)pkt+pn)
1325 PBIT16((uchar*)s-1, n);
1329 repack(Fcall *f, uchar **ppkt, int dotu)
1336 nn = sizeS2Mu(f, dotu);
1342 n = convS2Mu(f, pkt, nn, dotu);
1344 sysfatal("convS2M conversion error");
1346 sysfatal("convS2Mu and sizeS2Mu disagree");
1350 rewritehdr(Fcall *f, uchar *pkt)
1355 PBIT16(pkt+5, f->tag);
1359 restring(pkt, n, f->version);
1362 PBIT32(pkt+7, f->afid);
1363 restring(pkt, n, f->uname);
1364 restring(pkt, n, f->aname);
1367 PBIT16(pkt+7, f->oldtag);
1370 restring(pkt, n, f->uname);
1371 restring(pkt, n, f->aname);
1372 PBIT32(pkt+7, f->fid);
1373 PBIT32(pkt+11, f->afid);
1376 PBIT32(pkt+7, f->fid);
1377 PBIT32(pkt+11, f->newfid);
1378 for(i=0; i<f->nwname; i++)
1379 restring(pkt, n, f->wname[i]);
1382 restring(pkt, n, f->name);
1390 PBIT32(pkt+7, f->fid);
1393 PBIT32(pkt+7, f->fid);
1394 PBIT64(pkt+11, f->offset);
1397 restring(pkt, n, f->ename);
1403 _iolisten(va_list *arg)
1407 a = va_arg(*arg, char*);
1408 b = va_arg(*arg, char*);
1409 return listen(a, b);
1413 iolisten(Ioproc *io, char *a, char *b)
1415 return iocall(io, _iolisten, a, b);
1419 _ioaccept(va_list *arg)
1424 fd = va_arg(*arg, int);
1425 dir = va_arg(*arg, char*);
1426 return accept(fd, dir);
1430 ioaccept(Ioproc *io, int fd, char *dir)
1432 return iocall(io, _ioaccept, fd, dir);
1438 static char *mon[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1439 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
1443 tm = *localtime(time(0));
1444 return fmtprint(fmt, "%s %2d %02d:%02d:%02d.%03d",
1445 mon[tm.mon], tm.mday, tm.hour, tm.min, tm.sec,
1446 (int)(ns%1000000000)/1000000);
1450 cvtustat(Fcall *f, uchar **fpkt, int tounix)
1457 str = emalloc(f->nstat);
1458 n = convM2Du(f->stat, f->nstat, &dir, str, !tounix);
1464 n = sizeD2Mu(&dir, tounix);
1466 if(convD2Mu(&dir, buf, n, tounix) != n)
1467 sysfatal("convD2Mu conversion error");
1471 repack(f, fpkt, dotu);
1473 f->stat = nil; /* is this okay ??? */
1480 stripudirread(Msg* msg)
1493 for(i = 0; i < rx->count; i += m){
1494 m = BIT16SZ + GBIT16(&rx->data[i]);
1495 if(statchecku((uchar*)&rx->data[i], m, 1) < 0)
1503 buf = emalloc(rx->count);
1506 for(i = 0; i < rx->count; i += m){
1507 m = BIT16SZ + GBIT16(&rx->data[i]);
1508 if(convM2Du((uchar*)&rx->data[i], m, &d, str, 1) != m){
1514 n = convD2M(&d, &buf[nn], rx->count - nn);
1525 rx->data = (char*)buf;
1527 repack(&msg->rx, &msg->rpkt, 0);
1530 rx->data = nil; /* is this okay ??? */