16 typedef struct Dirtab Dirtab;
17 typedef struct Fid Fid;
18 typedef struct Holdq Holdq;
19 typedef struct Readreq Readreq;
20 typedef struct Sendreq Sendreq;
28 int nopen; /* #fids open on this port */
43 long offset; /* zeroed at beginning of each message, read or write */
44 char *writebuf; /* partial message written so far; offset tells how much */
59 int nfid; /* number of fids that should receive this message */
60 int nleft; /* number left that haven't received it */
61 Fid **fid; /* fid[nfid] */
63 char *pack; /* plumbpack()ed message */
64 int npack; /* length of pack */
74 struct /* needed because incref() doesn't return value */
93 static Dirtab dir[NDIR] =
95 { ".", QTDIR, Qdir, 0500|DMDIR },
96 { "rules", QTFILE, Qrules, 0600 },
97 { "send", QTFILE, Qsend, 0200 },
99 static int ndir = NQID;
102 static int srvclosefd; /* rock for end of pipe to close */
105 static Fid *fids[Nhash];
106 static QLock readlock;
108 static char srvfile[128];
109 static int messagesize = 8192+IOHDRSZ; /* good start */
111 static void fsysproc(void*);
112 static void fsysrespond(Fcall*, uchar*, char*);
113 static Fid* newfid(int);
115 static Fcall* fsysflush(Fcall*, uchar*, Fid*);
116 static Fcall* fsysversion(Fcall*, uchar*, Fid*);
117 static Fcall* fsysauth(Fcall*, uchar*, Fid*);
118 static Fcall* fsysattach(Fcall*, uchar*, Fid*);
119 static Fcall* fsyswalk(Fcall*, uchar*, Fid*);
120 static Fcall* fsysopen(Fcall*, uchar*, Fid*);
121 static Fcall* fsyscreate(Fcall*, uchar*, Fid*);
122 static Fcall* fsysread(Fcall*, uchar*, Fid*);
123 static Fcall* fsyswrite(Fcall*, uchar*, Fid*);
124 static Fcall* fsysclunk(Fcall*, uchar*, Fid*);
125 static Fcall* fsysremove(Fcall*, uchar*, Fid*);
126 static Fcall* fsysstat(Fcall*, uchar*, Fid*);
127 static Fcall* fsyswstat(Fcall*, uchar*, Fid*);
129 Fcall* (*fcall[Tmax])(Fcall*, uchar*, Fid*) =
131 [Tflush] = fsysflush,
132 [Tversion] = fsysversion,
134 [Tattach] = fsysattach,
137 [Tcreate] = fsyscreate,
139 [Twrite] = fsyswrite,
140 [Tclunk] = fsysclunk,
141 [Tremove]= fsysremove,
143 [Twstat] = fsyswstat,
146 char Ebadfcall[] = "bad fcall type";
147 char Eperm[] = "permission denied";
148 char Enomem[] = "malloc failed for buffer";
149 char Enotdir[] = "not a directory";
150 char Enoexist[] = "plumb file does not exist";
151 char Eisdir[] = "file is a directory";
152 char Ebadmsg[] = "bad plumb message format";
153 char Enosuchport[] ="no such plumb port";
154 char Enoport[] = "couldn't find destination for message";
155 char Einuse[] = "file already open";
158 * Add new port. A no-op if port already exists or is the null string
167 for(i=NQID; i<ndir; i++)
168 if(strcmp(port, dir[i].name) == 0)
171 fprint(2, "plumb: too many ports; max %d\n", NDIR);
175 dir[i].name = estrdup(port);
179 ports = erealloc(ports, nports*sizeof(char*));
180 ports[nports-1] = dir[i].name;
189 read(clockfd, buf, sizeof buf);
198 fmtinstall('F', fcallfmt);
199 clockfd = open("/dev/time", OREAD|OCEXEC);
202 error("can't create pipe: %r");
203 /* 0 will be server end, 1 will be client end */
206 sprint(srvfile, "/srv/plumb.%s.%d", user, getpid());
207 if(putenv("plumbsrv", srvfile) < 0)
208 error("can't write $plumbsrv: %r");
209 fd = create(srvfile, OWRITE|OCEXEC|ORCLOSE, 0600);
211 error("can't create /srv file: %r");
212 if(fprint(fd, "%d", p[1]) <= 0)
213 error("can't write /srv/file: %r");
214 /* leave fd open; ORCLOSE will take care of it */
216 procrfork(fsysproc, nil, Stack, RFFDG);
219 if(mount(p[1], -1, "/mnt/plumb", MREPL, "") < 0)
220 error("can't mount /mnt/plumb: %r");
236 buf = malloc(messagesize); /* avoid memset of emalloc */
238 error("malloc failed: %r");
240 n = read9pmsg(srvfd, buf, messagesize);
242 threadexitsall("unmounted");
244 error("mount read: %r");
245 if(readlock.head == nil) /* no other processes waiting to read; start one */
246 proccreate(fsysproc, nil, Stack);
249 t = emalloc(sizeof(Fcall));
250 if(convM2S(buf, n, t) != n)
251 error("convert error in convM2S");
253 fprint(2, "<= %F\n", t);
254 if(fcall[t->type] == nil)
255 fsysrespond(t, buf, Ebadfcall);
257 if(t->type==Tversion || t->type==Tauth)
261 t = (*fcall[t->type])(t, buf, f);
267 fsysrespond(Fcall *t, uchar *buf, char *err)
277 buf = emalloc(messagesize);
278 n = convS2M(t, buf, messagesize);
280 error("convert error in convS2M");
281 if(write(srvfd, buf, n) != n)
282 error("write error in respond");
284 fprint(2, "=> %F\n", t);
296 fh = &fids[fid&(Nhash-1)];
297 for(f=*fh; f; f=f->next)
300 else if(ff==nil && !f->busy)
307 f = emalloc(sizeof *f);
317 dostat(Dirtab *dir, uchar *buf, uint nbuf, uint clock)
321 d.qid.type = dir->type;
322 d.qid.path = dir->qid;
325 d.length = 0; /* would be nice to do better */
332 return convD2M(&d, buf, nbuf);
336 queuesend(Dirtab *d, Plumbmsg *m)
342 s = emalloc(sizeof(Sendreq));
345 s->fid = emalloc(s->nfid*sizeof(Fid*));
347 /* build array of fids open on this channel */
348 for(f=d->fopen; f!=nil; f=f->nextopen)
352 /* link to end of queue; drainqueue() searches in sender order so this implements a FIFO */
353 for(t=d->sendq; t!=nil; t=t->next)
363 queueread(Dirtab *d, Fcall *t, uchar *buf, Fid *f)
367 r = emalloc(sizeof(Readreq));
376 drainqueue(Dirtab *d)
378 Readreq *r, *nextr, *prevr;
379 Sendreq *s, *nexts, *prevs;
383 for(s=d->sendq; s!=nil; s=nexts){
385 for(i=0; i<s->nfid; i++){
387 for(r=d->readq; r!=nil; r=nextr){
389 if(r->fid == s->fid[i]){
390 /* pack the message if necessary */
392 s->pack = plumbpack(s->msg, &s->npack);
393 /* exchange the stuff... */
394 r->fcall->data = s->pack+r->fid->offset;
395 n = s->npack - r->fid->offset;
396 if(n > messagesize-IOHDRSZ)
397 n = messagesize-IOHDRSZ;
398 if(n > r->fcall->count)
401 fsysrespond(r->fcall, r->buf, nil);
403 if(r->fid->offset >= s->npack){
404 /* message transferred; delete this fid from send queue */
409 /* delete read request from queue */
411 prevr->next = r->next;
421 /* if no fids left, delete this send from queue */
427 prevs->next = s->next;
436 /* can't flush a send because they are always answered synchronously */
438 flushqueue(Dirtab *d, int oldtag)
443 for(r=d->readq; r!=nil; r=r->next){
444 if(oldtag == r->fcall->tag){
445 /* delete read request from queue */
447 prevr->next = r->next;
459 /* remove messages awaiting delivery to now-closing fid */
461 removesenders(Dirtab *d, Fid *fid)
463 Sendreq *s, *nexts, *prevs;
467 for(s=d->sendq; s!=nil; s=nexts){
469 for(i=0; i<s->nfid; i++)
470 if(fid == s->fid[i]){
471 /* delete this fid from send queue */
476 /* if no fids left, delete this send from queue */
482 prevs->next = s->next;
492 hold(Plumbmsg *m, Dirtab *d)
496 h = emalloc(sizeof(Holdq));
498 /* add to end of queue */
502 for(q=d->holdq; q->next!=nil; q=q->next)
513 while(d->holdq != nil){
516 queuesend(d, h->msg);
517 /* no need to drain queue because we know no-one is reading yet */
523 dispose(Fcall *t, uchar *buf, Plumbmsg *m, Ruleset *rs, Exec *e)
530 if(m->dst==nil || m->dst[0]=='\0'){
533 err = startup(rs, e);
536 for(i=NQID; i<ndir; i++)
537 if(strcmp(m->dst, dir[i].name) == 0){
538 if(dir[i].nopen == 0){
539 err = startup(rs, e);
540 if(e!=nil && e->holdforclient)
545 queuesend(&dir[i], m);
552 fsysrespond(t, buf, err);
557 fsysversion(Fcall *t, uchar *buf, Fid*)
560 fsysrespond(t, buf, "version: message size too small");
563 if(t->msize < messagesize)
564 messagesize = t->msize;
565 t->msize = messagesize;
566 if(strncmp(t->version, "9P2000", 6) != 0){
567 fsysrespond(t, buf, "unrecognized 9P version");
570 t->version = "9P2000";
571 fsysrespond(t, buf, nil);
576 fsysauth(Fcall *t, uchar *buf, Fid*)
578 fsysrespond(t, buf, "plumber: authentication not required");
583 fsysattach(Fcall *t, uchar *buf, Fid *f)
587 if(strcmp(t->uname, user) != 0){
588 fsysrespond(&out, buf, Eperm);
597 memset(&out, 0, sizeof(Fcall));
602 fsysrespond(&out, buf, nil);
607 fsysflush(Fcall *t, uchar *buf, Fid*)
612 for(i=NQID; i<ndir; i++)
613 flushqueue(&dir[i], t->oldtag);
615 fsysrespond(t, buf, nil);
620 fsyswalk(Fcall *t, uchar *buf, Fid *f)
632 fsysrespond(t, buf, "clone of an open fid");
637 if(t->fid != t->newfid){
638 nf = newfid(t->newfid);
640 fsysrespond(t, buf, "clone to a busy fid");
656 for(i=0; i<t->nwname; i++){
657 if((q.type & QTDIR) == 0){
661 if(strcmp(t->wname[i], "..") == 0){
668 out.wqid[out.nwqid++] = q;
674 if(strcmp(t->wname[i], d->name) == 0){
687 if(err!=nil || out.nwqid<t->nwname){
690 }else if(out.nwqid == t->nwname){
695 fsysrespond(&out, buf, err);
700 fsysopen(Fcall *t, uchar *buf, Fid *f)
702 int m, clearrules, mode;
705 if(t->mode & OTRUNC){
706 if(f->qid.path != Qrules)
710 /* can't truncate anything, so just disregard */
711 mode = t->mode & ~(OTRUNC|OCEXEC);
712 /* can't execute or remove anything */
713 if(mode==OEXEC || (mode&ORCLOSE))
728 if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
730 if(f->qid.path==Qrules && (mode==OWRITE || mode==ORDWR)){
732 if(rulesref.ref++ != 0){
735 fsysrespond(t, buf, Einuse);
742 for(m=0; rules[m]; m++){
743 freeruleset(rules[m]);
753 f->nextopen = f->dir->fopen;
757 fsysrespond(t, buf, nil);
761 fsysrespond(t, buf, Eperm);
766 fsyscreate(Fcall *t, uchar *buf, Fid*)
768 fsysrespond(t, buf, Eperm);
773 fsysreadrules(Fcall *t, uchar *buf)
784 t->data = p+t->offset;
785 if(t->offset+t->count > n)
786 t->count = n-t->offset;
788 fsysrespond(t, buf, nil);
794 fsysread(Fcall *t, uchar *buf, Fid *f)
802 if(f->qid.path != Qdir){
803 if(f->qid.path == Qrules)
804 return fsysreadrules(t, buf);
806 if(f->qid.path < NQID){
807 fsysrespond(t, buf, "internal error: unknown read port");
811 queueread(f->dir, t, buf, f);
817 e = t->offset+t->count;
819 b = malloc(messagesize-IOHDRSZ);
821 fsysrespond(t, buf, Enomem);
826 d++; /* first entry is '.' */
827 for(i=0; d->name!=nil && i<e; i+=len){
828 len = dostat(d, b+n, messagesize-IOHDRSZ-n, clock);
837 fsysrespond(t, buf, nil);
843 fsyswrite(Fcall *t, uchar *buf, Fid *f)
851 switch((int)f->qid.path){
853 fsysrespond(t, buf, Eisdir);
857 fsysrespond(t, buf, writerules(t->data, t->count));
864 /* partial message already assembled */
865 f->writebuf = erealloc(f->writebuf, f->offset + t->count);
866 memmove(f->writebuf+f->offset, t->data, t->count);
868 count = f->offset+t->count;
870 m = plumbunpackpartial(data, count, &n);
876 fsysrespond(t, buf, Ebadmsg);
879 /* can read more... */
881 f->writebuf = emalloc(t->count);
882 memmove(f->writebuf, t->data, t->count);
884 /* else buffer has already been grown */
885 f->offset += t->count;
886 fsysrespond(t, buf, nil);
889 /* release partial buffer */
893 for(i=0; rules[i]; i++)
894 if((e=matchruleset(m, rules[i])) != nil){
895 dispose(t, buf, m, rules[i], e);
899 dispose(t, buf, m, nil, nil);
902 fsysrespond(t, buf, "no matching plumb rule");
905 fsysrespond(t, buf, "internal error: write to unknown file");
910 fsysstat(Fcall *t, uchar *buf, Fid *f)
912 t->stat = emalloc(messagesize-IOHDRSZ);
913 t->nstat = dostat(f->dir, t->stat, messagesize-IOHDRSZ, clock);
914 fsysrespond(t, buf, nil);
921 fsyswstat(Fcall *t, uchar *buf, Fid*)
923 fsysrespond(t, buf, Eperm);
928 fsysremove(Fcall *t, uchar *buf, Fid*)
930 fsysrespond(t, buf, Eperm);
935 fsysclunk(Fcall *t, uchar *buf, Fid *f)
944 if(d->qid==Qrules && (f->mode==OWRITE || f->mode==ORDWR)){
946 * just to be sure last rule is parsed; error messages will be lost, though,
947 * unless last write ended with a blank line
955 for(p=d->fopen; p; p=p->nextopen){
958 prev->nextopen = f->nextopen;
960 d->fopen = f->nextopen;
970 if(f->writebuf != nil){
975 fsysrespond(t, buf, nil);