452 lines
6.8 KiB
C
452 lines
6.8 KiB
C
/*
|
|
* ``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 <u.h>
|
|
#include <libc.h>
|
|
#include <fcall.h>
|
|
#include <thread.h>
|
|
#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(; i<nelem(tab); i++){
|
|
if(i==Qn){
|
|
n = atoi(name);
|
|
snprint(buf, sizeof buf, "%d", n);
|
|
if(n < nclient && strcmp(buf, name) == 0){
|
|
qid->path = 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,
|
|
};
|