/* * ``Exec'' network device. Mounted on net, provides /net/exec. * * exec protocol directory * n connection directory * ctl control messages (like connect) * data data * err errors * local local address (pid of command) * remote remote address (command) * status status */ #include #include #include #include #include <9p.h> #include "dat.h" int fsdebug; enum { Qroot, Qexec, Qclone, Qn, Qctl, Qdata, Qlocal, Qremote, Qstatus, }; #define PATH(type, n) ((type)|((n)<<8)) #define TYPE(path) ((int)(path) & 0xFF) #define NUM(path) ((uint)(path)>>8) typedef struct Tab Tab; struct Tab { char *name; ulong mode; }; Tab tab[] = { "/", DMDIR|0555, "exec", DMDIR|0555, "clone", 0666, nil, DMDIR|0555, "ctl", 0666, "data", 0666, "local", 0444, "remote", 0444, "status", 0444, }; void setexecname(char *s) { tab[Qexec].name = s; } ulong time0; static void fillstat(Dir *d, ulong path) { Tab *t; int type; char buf[32]; memset(d, 0, sizeof(*d)); d->uid = estrdup("exec"); d->gid = estrdup("exec"); d->qid.path = path; d->atime = d->mtime = time0; d->length = 0; type = TYPE(path); t = &tab[type]; if(t->name) d->name = estrdup(t->name); else{ snprint(buf, sizeof buf, "%ud", NUM(path)); d->name = estrdup(buf); } d->qid.type = t->mode>>24; d->mode = t->mode; } static void fsstat(Req *r) { fillstat(&r->d, r->fid->qid.path); respond(r, nil); } static int rootgen(int i, Dir *d, void*) { if(i < 1){ fillstat(d, PATH(Qexec, 0)); return 0; } return -1; } static int execgen(int i, Dir *d, void*) { if(i < 1){ fillstat(d, PATH(Qclone, 0)); return 0; } i -= 1; if(i < nclient){ fillstat(d, PATH(Qn, i)); return 0; } return -1; } static int conngen(int i, Dir *d, void *aux) { Client *c; c = aux; i += Qn+1; if(i <= Qstatus){ fillstat(d, PATH(i, c->num)); return 0; } return -1; } char *statusstr[] = { "Closed", "Exec", "Established", "Hangup", }; static void fsread(Req *r) { char e[ERRMAX], *s; ulong path; path = r->fid->qid.path; switch(TYPE(path)){ default: snprint(e, sizeof e, "bug in execnet path=%lux", path); respond(r, e); break; case Qroot: dirread9p(r, rootgen, nil); respond(r, nil); break; case Qexec: dirread9p(r, execgen, nil); respond(r, nil); break; case Qn: dirread9p(r, conngen, client[NUM(path)]); respond(r, nil); break; case Qctl: snprint(e, sizeof e, "%ud", NUM(path)); readstr(r, e); respond(r, nil); break; case Qdata: dataread(r, client[NUM(path)]); break; case Qlocal: snprint(e, sizeof e, "%d", client[NUM(path)]->pid); readstr(r, e); respond(r, nil); break; case Qremote: s = client[NUM(path)]->cmd; if(strlen(s) >= 5) /* "exec " */ readstr(r, s+5); else readstr(r, s); respond(r, nil); break; case Qstatus: readstr(r, statusstr[client[NUM(path)]->status]); respond(r, nil); break; } } static void fswrite(Req *r) { char e[ERRMAX]; ulong path; path = r->fid->qid.path; switch(TYPE(path)){ default: snprint(e, sizeof e, "bug in execnet path=%lux", path); respond(r, e); break; case Qctl: ctlwrite(r, client[NUM(path)]); break; case Qdata: datawrite(r, client[NUM(path)]); break; } } static void fsflush(Req *r) { ulong path; Req *or; for(or=r; or->ifcall.type==Tflush; or=or->oldreq) ; if(or->ifcall.type != Tread && or->ifcall.type != Twrite) abort(); path = or->fid->qid.path; if(TYPE(path) != Qdata) abort(); clientflush(or, client[NUM(path)]); respond(r, nil); } static void fsattach(Req *r) { if(r->ifcall.aname && r->ifcall.aname[0]){ respond(r, "invalid attach specifier"); return; } r->fid->qid.path = PATH(Qroot, 0); r->fid->qid.type = QTDIR; r->fid->qid.vers = 0; r->ofcall.qid = r->fid->qid; respond(r, nil); } static char* fswalk1(Fid *fid, char *name, Qid *qid) { char buf[32]; int i, n; ulong path; if(!(fid->qid.type&QTDIR)) return "walk in non-directory"; path = fid->qid.path; if(strcmp(name, "..") == 0){ switch(TYPE(path)){ case Qn: qid->path = PATH(Qexec, 0); qid->type = QTDIR; return nil; case Qroot: case Qexec: qid->path = PATH(Qroot, 0); qid->type = QTDIR; return nil; default: return "bug in fswalk1"; } } i = TYPE(path)+1; for(; ipath = PATH(Qn, n); qid->type = QTDIR; return nil; } break; } if(strcmp(tab[i].name, name) == 0){ qid->path = PATH(i, NUM(path)); qid->type = tab[i].mode>>24; return nil; } if(tab[i].mode&DMDIR) break; } return "directory entry not found"; } static void fsopen(Req *r) { static int need[4] = { 4, 2, 6, 1 }; ulong path; int n; Tab *t; /* * lib9p already handles the blatantly obvious. * we just have to enforce the permissions we have set. */ path = r->fid->qid.path; t = &tab[TYPE(path)]; n = need[r->ifcall.mode&3]; if((n&t->mode) != n){ respond(r, "permission denied"); return; } switch(TYPE(path)){ case Qclone: n = newclient(); path = PATH(Qctl, n); r->fid->qid.path = path; r->ofcall.qid.path = path; if(fsdebug) fprint(2, "open clone => path=%lux\n", path); t = &tab[Qctl]; /* fall through */ default: if(t-tab >= Qn) client[NUM(path)]->ref++; respond(r, nil); break; } } Channel *cclunk; Channel *cclunkwait; Channel *creq; Channel *creqwait; static void fsthread(void*) { ulong path; Alt a[3]; Fid *fid; Req *r; threadsetname("fsthread"); a[0].op = CHANRCV; a[0].c = cclunk; a[0].v = &fid; a[1].op = CHANRCV; a[1].c = creq; a[1].v = &r; a[2].op = CHANEND; for(;;){ switch(alt(a)){ case 0: path = fid->qid.path; if(fid->omode != -1 && TYPE(path) >= Qn) closeclient(client[NUM(path)]); sendp(cclunkwait, nil); break; case 1: switch(r->ifcall.type){ case Tattach: fsattach(r); break; case Topen: fsopen(r); break; case Tread: fsread(r); break; case Twrite: fswrite(r); break; case Tstat: fsstat(r); break; case Tflush: fsflush(r); break; default: respond(r, "bug in fsthread"); break; } sendp(creqwait, 0); break; } } } static void fsdestroyfid(Fid *fid) { sendp(cclunk, fid); recvp(cclunkwait); } static void fssend(Req *r) { sendp(creq, r); recvp(creqwait); /* avoids need to deal with spurious flushes */ } void initfs(void) { time0 = time(0); creq = chancreate(sizeof(void*), 0); creqwait = chancreate(sizeof(void*), 0); cclunk = chancreate(sizeof(void*), 0); cclunkwait = chancreate(sizeof(void*), 0); procrfork(fsthread, nil, STACK, RFNAMEG); } Srv fs = { .attach= fssend, .destroyfid= fsdestroyfid, .walk1= fswalk1, .open= fssend, .read= fssend, .write= fssend, .stat= fssend, .flush= fssend, };