Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * ``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,
};