123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "../port/error.h"
- #include "kexec.h"
- enum
- {
- Qtopdir, /* top level directory */
- Qcmd,
- Qclonus,
- Qconvdir,
- Qconvbase,
- Qdata = Qconvbase,
- Qstderr,
- Qctl,
- Qalloc,
- Qexec,
- Qstatus,
- Qwait,
- Debug=0 /* to help debug os.c */
- };
- #define TYPE(x) ((ulong)(x).path & 0xf)
- #define CONV(x) (((ulong)(x).path >> 4)&0xfff)
- #define QID(c, y) (((c)<<4) | (y))
- typedef struct Conv Conv;
- struct Conv
- {
- int x;
- int inuse;
- int fd[3]; /* stdin, stdout, and stderr */
- int count[3]; /* number of readers on stdin/stdout/stderr */
- int perm;
- uint32_t esz;
- char* owner;
- char* state;
- Cmdbuf* cmd;
- char* dir;
- QLock l; /* protects state changes */
- Queue* waitq;
- void* child;
- char* error; /* on start up */
- int nice;
- int16_t killonclose;
- int16_t killed;
- Rendez startr;
- Proc *p;
- };
- static struct
- {
- QLock l;
- int nc;
- int maxconv;
- Conv** conv;
- } cmd;
- static Conv* cmdclone(char*);
- static void cmdproc(void*);
- static int
- cmd3gen(Chan *c, int i, Dir *dp)
- {
- Qid q;
- Conv *cv;
- cv = cmd.conv[CONV(c->qid)];
- switch(i){
- default:
- return -1;
- case Qdata:
- mkqid(&q, QID(CONV(c->qid), Qdata), 0, QTFILE);
- devdir(c, q, "data", 0, cv->owner, cv->perm, dp);
- return 1;
- case Qstderr:
- mkqid(&q, QID(CONV(c->qid), Qstderr), 0, QTFILE);
- devdir(c, q, "stderr", 0, cv->owner, 0444, dp);
- return 1;
- case Qalloc:
- mkqid(&q, QID(CONV(c->qid), Qalloc), 0, QTFILE);
- devdir(c, q, "alloc", 0, cv->owner, cv->perm, dp);
- return 1;
- case Qexec:
- mkqid(&q, QID(CONV(c->qid), Qexec), 0, QTFILE);
- devdir(c, q, "exec", 0, cv->owner, cv->perm, dp);
- return 1;
- case Qctl:
- mkqid(&q, QID(CONV(c->qid), Qctl), 0, QTFILE);
- devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp);
- return 1;
- case Qstatus:
- mkqid(&q, QID(CONV(c->qid), Qstatus), 0, QTFILE);
- devdir(c, q, "status", 0, cv->owner, 0444, dp);
- return 1;
- case Qwait:
- mkqid(&q, QID(CONV(c->qid), Qwait), 0, QTFILE);
- devdir(c, q, "wait", 0, cv->owner, 0444, dp);
- return 1;
- }
- }
- static int
- cmdgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp)
- {
- Qid q;
- Conv *cv;
- USED(name);
- USED(nd);
- USED(d);
- if(s == DEVDOTDOT){
- switch(TYPE(c->qid)){
- case Qtopdir:
- case Qcmd:
- mkqid(&q, QID(0, Qtopdir), 0, QTDIR);
- devdir(c, q, "#C", 0, eve, DMDIR|0555, dp);
- break;
- case Qconvdir:
- mkqid(&q, QID(0, Qcmd), 0, QTDIR);
- devdir(c, q, "cmd", 0, eve, DMDIR|0555, dp);
- break;
- default:
- panic("cmdgen %llux", c->qid.path);
- }
- return 1;
- }
- switch(TYPE(c->qid)) {
- case Qtopdir:
- if(s >= 1)
- return -1;
- mkqid(&q, QID(0, Qcmd), 0, QTDIR);
- devdir(c, q, "cmd", 0, "cmd", DMDIR|0555, dp);
- return 1;
- case Qcmd:
- if(s < cmd.nc) {
- cv = cmd.conv[s];
- mkqid(&q, QID(s, Qconvdir), 0, QTDIR);
- sprint(up->genbuf, "%d", s);
- devdir(c, q, up->genbuf, 0, cv->owner, DMDIR|0555, dp);
- return 1;
- }
- s -= cmd.nc;
- if(s == 0){
- mkqid(&q, QID(0, Qclonus), 0, QTFILE);
- devdir(c, q, "clone", 0, "cmd", 0666, dp);
- return 1;
- }
- return -1;
- case Qclonus:
- if(s == 0){
- mkqid(&q, QID(0, Qclonus), 0, QTFILE);
- devdir(c, q, "clone", 0, "cmd", 0666, dp);
- return 1;
- }
- return -1;
- case Qconvdir:
- return cmd3gen(c, Qconvbase+s, dp);
- case Qdata:
- case Qstderr:
- case Qalloc:
- case Qexec:
- case Qctl:
- case Qstatus:
- case Qwait:
- return cmd3gen(c, TYPE(c->qid), dp);
- }
- return -1;
- }
- static void
- cmdinit(void)
- {
- cmd.maxconv = 1000;
- cmd.conv = mallocz(sizeof(Conv*)*(cmd.maxconv+1), 1);
- /* cmd.conv is checked by cmdattach, below */
- }
- static Chan *
- cmdattach(char *spec)
- {
- Chan *c;
- if(cmd.conv == nil)
- error(Enomem);
- c = devattach('C', spec);
- mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
- return c;
- }
- static Walkqid*
- cmdwalk(Chan *c, Chan *nc, char **name, int nname)
- {
- return devwalk(c, nc, name, nname, 0, 0, cmdgen);
- }
- static int32_t
- cmdstat(Chan *c, uint8_t *db, int32_t n)
- {
- return devstat(c, db, n, 0, 0, cmdgen);
- }
- static Chan *
- cmdopen(Chan *c, int omode)
- {
- int perm;
- Conv *cv;
- char *user;
- perm = 0;
- omode = openmode(omode);
- switch(omode) {
- case OREAD:
- perm = 4;
- break;
- case OWRITE:
- perm = 2;
- break;
- case ORDWR:
- perm = 6;
- break;
- }
- switch(TYPE(c->qid)) {
- default:
- break;
- case Qtopdir:
- case Qcmd:
- case Qconvdir:
- case Qstatus:
- if(omode != OREAD)
- error(Eperm);
- break;
- case Qclonus:
- qlock(&cmd.l);
- if(waserror()){
- qunlock(&cmd.l);
- nexterror();
- }
- cv = cmdclone(up->user);
- poperror();
- qunlock(&cmd.l);
- if(cv == 0)
- error(Enodev);
- mkqid(&c->qid, QID(cv->x, Qctl), 0, QTFILE);
- break;
- case Qdata:
- case Qstderr:
- case Qctl:
- case Qalloc:
- case Qexec:
- case Qwait:
- qlock(&cmd.l);
- cv = cmd.conv[CONV(c->qid)];
- qlock(&cv->l);
- if(waserror()){
- qunlock(&cv->l);
- qunlock(&cmd.l);
- nexterror();
- }
- user = up->user;
- if((perm & (cv->perm>>6)) != perm) {
- if(strcmp(user, cv->owner) != 0 ||
- (perm & cv->perm) != perm)
- error(Eperm);
- }
- switch(TYPE(c->qid)){
- case Qdata:
- if(omode == OWRITE || omode == ORDWR)
- cv->count[0]++;
- if(omode == OREAD || omode == ORDWR)
- cv->count[1]++;
- break;
- case Qstderr:
- if(omode != OREAD)
- error(Eperm);
- cv->count[2]++;
- break;
- case Qwait:
- if(cv->waitq == nil)
- cv->waitq = qopen(1024, Qmsg, nil, 0);
- break;
- }
- cv->inuse++;
- if(cv->inuse == 1) {
- cv->state = "Open";
- kstrdup(&cv->owner, user);
- cv->perm = 0660;
- cv->nice = 0;
- }
- poperror();
- qunlock(&cv->l);
- qunlock(&cmd.l);
- break;
- }
- c->mode = omode;
- c->flag |= COPEN;
- c->offset = 0;
- return c;
- }
- static void
- closeconv(Conv *c)
- {
- kstrdup(&c->owner, "cmd");
- kstrdup(&c->dir, "FIXME");
- c->perm = 0666;
- c->state = "Closed";
- c->killonclose = 0;
- c->killed = 0;
- c->nice = 0;
- free(c->cmd);
- c->cmd = nil;
- if(c->waitq != nil){
- qfree(c->waitq);
- c->waitq = nil;
- }
- free(c->error);
- c->error = nil;
- }
- static void
- cmdfdclose(Conv *c, int fd)
- {
- if(--c->count[fd] == 0 && c->fd[fd] != -1){
- // close(c->fd[fd]);
- c->fd[fd] = -1;
- }
- }
- static void
- cmdclose(Chan *c)
- {
- Conv *cc;
- int r;
- if((c->flag & COPEN) == 0)
- return;
- switch(TYPE(c->qid)) {
- case Qctl:
- case Qalloc:
- case Qexec:
- case Qdata:
- case Qstderr:
- case Qwait:
- cc = cmd.conv[CONV(c->qid)];
- qlock(&cc->l);
- if(TYPE(c->qid) == Qdata){
- if(c->mode == OWRITE || c->mode == ORDWR)
- cmdfdclose(cc, 0);
- if(c->mode == OREAD || c->mode == ORDWR)
- cmdfdclose(cc, 1);
- }else if(TYPE(c->qid) == Qstderr)
- cmdfdclose(cc, 2);
- r = --cc->inuse;
- if(cc->child != nil){
- if(!cc->killed)
- if(r == 0 || (cc->killonclose && TYPE(c->qid) == Qctl)){
- // oscmdkill(cc->child);
- cc->killed = 1;
- }
- }else if(r == 0)
- closeconv(cc);
- qunlock(&cc->l);
- break;
- }
- }
- static int32_t
- cmdread(Chan *ch, void *a, int32_t n, int64_t offset)
- {
- Conv *c;
- Proc *p;
- char *s, *cmds;
- int fd;
- char buf[256];
- USED(offset);
- s = a;
- switch(TYPE(ch->qid)) {
- default:
- error(Eperm);
- case Qcmd:
- case Qtopdir:
- case Qconvdir:
- return devdirread(ch, a, n, 0, 0, cmdgen);
- case Qctl:
- sprint(up->genbuf, "%ld", CONV(ch->qid));
- return readstr(offset, s, n, up->genbuf);
- case Qalloc:
- c = cmd.conv[CONV(ch->qid)];
- p = c->p;
- snprint(buf, sizeof(buf), "%#p %#p %#p %#p %#p %#p %#p %#p",
- p->seg[TSEG]->base, p->seg[TSEG]->top,
- p->seg[DSEG]->base, p->seg[DSEG]->top,
- p->seg[BSEG]->base, p->seg[BSEG]->top,
- p->seg[SSEG]->base, p->seg[SSEG]->top);
- return readstr(offset, s, n, buf);
- case Qexec:
- c = cmd.conv[CONV(ch->qid)];
- snprint(up->genbuf, sizeof(up->genbuf), "%ld", c->esz);
- return readstr(offset, s, n, up->genbuf);
- case Qstatus:
- c = cmd.conv[CONV(ch->qid)];
- cmds = "";
- if(c->cmd != nil)
- cmds = c->cmd->f[1];
- snprint(up->genbuf, sizeof(up->genbuf), "cmd/%d %d %s %q %q\n",
- c->x, c->inuse, c->state, c->dir, cmds);
- return readstr(offset, s, n, up->genbuf);
- case Qdata:
- case Qstderr:
- fd = 1;
- if(TYPE(ch->qid) == Qstderr)
- fd = 2;
- c = cmd.conv[CONV(ch->qid)];
- qlock(&c->l);
- if(c->fd[fd] == -1){
- qunlock(&c->l);
- return 0;
- }
- qunlock(&c->l);
- // osenter();
- // n = read(c->fd[fd], a, n);
- // osleave();
- // if(n < 0)
- // oserror();
- return n;
- case Qwait:
- c = cmd.conv[CONV(ch->qid)];
- return qread(c->waitq, a, n);
- }
- }
- static int
- cmdstarted(void *a)
- {
- Conv *c;
- c = a;
- return c->child != nil || c->error != nil || strcmp(c->state, "Execute") != 0;
- }
- enum
- {
- CMdir,
- CMstart,
- CMexec,
- CMkill,
- CMnice,
- CMkillonclose
- };
- static
- Cmdtab cmdtab[] = {
- CMdir, "dir", 2,
- CMstart, "start", 0,
- CMexec, "exec", 0,
- CMkill, "kill", 1,
- CMnice, "nice", 0,
- CMkillonclose, "killonclose", 0,
- };
- static int32_t
- cmdwrite(Chan *ch, void *a, int32_t n, int64_t offset)
- {
- int i, r;
- Conv *c;
- Segment *s;
- Cmdbuf *cb;
- Cmdtab *ct;
- USED(offset);
- switch(TYPE(ch->qid)) {
- default:
- error(Eperm);
- case Qctl:
- c = cmd.conv[CONV(ch->qid)];
- cb = parsecmd(a, n);
- if(waserror()){
- free(cb);
- nexterror();
- }
- ct = lookupcmd(cb, cmdtab, nelem(cmdtab));
- switch(ct->index){
- case CMdir:
- kstrdup(&c->dir, cb->f[1]);
- break;
- case CMstart:
- // so what do we do with this?
- // we need to do the process.
- if(cb->nf < 2)
- error(Ebadctl);
- c = cmd.conv[CONV(ch->qid)];
- s = c->p->seg[TSEG];
- // XXX: set the text name?
- //kstrdup(&c->p->text, cb->f[1]);
- kforkexecac(c->p, atoi(cb->f[2]), nil, cb->f+3);
- break;
- case CMexec:
- poperror(); /* cb */
- qlock(&c->l);
- if(waserror()){
- qunlock(&c->l);
- free(cb);
- nexterror();
- }
- if(c->child != nil || c->cmd != nil)
- error(Einuse);
- for(i = 0; i < nelem(c->fd); i++)
- if(c->fd[i] != -1)
- error(Einuse);
- if(cb->nf < 1)
- error(Etoosmall);
- // kproc("cmdproc", cmdproc, c, 0); /* cmdproc held back until unlock below */
- free(c->cmd);
- c->cmd = cb; /* don't free cb */
- c->state = "Execute";
- poperror();
- qunlock(&c->l);
- while(waserror())
- ;
- // Sleep(&c->startr, cmdstarted, c);
- poperror();
- if(c->error)
- error(c->error);
- return n; /* avoid free(cb) below */
- }
- poperror();
- free(cb);
- break;
- case Qexec:
- c = cmd.conv[CONV(ch->qid)];
- s = c->p->seg[TSEG];
- if(s->base+offset+n > s->top)
- error(Etoobig);
- memmove((void*)(s->base + offset), a, n);
- if(offset+n > c->esz)
- c->esz = offset+n;
- // XXX: can this every not be n?
- return n;
- case Qdata:
- c = cmd.conv[CONV(ch->qid)];
- qlock(&c->l);
- if(c->fd[0] == -1){
- qunlock(&c->l);
- error(Ehungup);
- }
- qunlock(&c->l);
- // osenter();
- // r = write(c->fd[0], a, n);
- // osleave();
- if(r == 0)
- error(Ehungup);
- if(r < 0) {
- /* XXX perhaps should kill writer "write on closed pipe" here, 2nd time around? */
- // oserror();
- }
- return r;
- }
- return n;
- }
- static int32_t
- cmdwstat(Chan *c, uint8_t *dp, int32_t n)
- {
- Dir *d;
- Conv *cv;
- switch(TYPE(c->qid)){
- default:
- error(Eperm);
- case Qctl:
- case Qdata:
- case Qstderr:
- d = malloc(sizeof(*d)+n);
- if(d == nil)
- error(Enomem);
- if(waserror()){
- free(d);
- nexterror();
- }
- n = convM2D(dp, n, d, (char*)&d[1]);
- if(n == 0)
- error(Eshortstat);
- cv = cmd.conv[CONV(c->qid)];
- if(!iseve() && strcmp(up->user, cv->owner) != 0)
- error(Eperm);
- if(!emptystr(d->uid))
- kstrdup(&cv->owner, d->uid);
- if(d->mode != ~0UL)
- cv->perm = d->mode & 0777;
- poperror();
- free(d);
- break;
- }
- return n;
- }
- static Conv*
- cmdclone(char *user)
- {
- Conv *c, **pp, **ep;
- int i;
- c = nil;
- ep = &cmd.conv[cmd.maxconv];
- for(pp = cmd.conv; pp < ep; pp++) {
- c = *pp;
- if(c == nil) {
- c = malloc(sizeof(Conv));
- if(c == nil)
- error(Enomem);
- qlock(&c->l);
- c->inuse = 1;
- c->x = pp - cmd.conv;
- cmd.nc++;
- *pp = c;
- break;
- }
- if(canqlock(&c->l)){
- if(c->inuse == 0 && c->child == nil)
- break;
- qunlock(&c->l);
- }
- }
- if(pp >= ep)
- return nil;
- c->inuse = 1;
- kstrdup(&c->owner, user);
- kstrdup(&c->dir, "FIXME");
- c->perm = 0660;
- c->state = "Closed";
- c->esz = 0;
- for(i=0; i<nelem(c->fd); i++)
- c->fd[i] = -1;
- // XXX: this should go somewhere else.
- c->p = setupseg(0);
- qunlock(&c->l);
- return c;
- }
- static void
- cmdproc(void *a)
- {
- Conv *c;
- int n;
- char status[ERRMAX];
- void *t;
- c = a;
- qlock(&c->l);
- if(Debug)
- print("f[0]=%q f[1]=%q\n", c->cmd->f[0], c->cmd->f[1]);
- if(waserror()){
- if(Debug)
- print("failed: %q\n", up->errstr);
- kstrdup(&c->error, up->errstr);
- c->state = "Done";
- qunlock(&c->l);
- // Wakeup(&c->startr);
- pexit("cmdproc", 0);
- }
- // t = oscmd(c->cmd->f+1, c->nice, c->dir, c->fd);
- // if(t == nil)
- // oserror();
- c->child = t; /* to allow oscmdkill */
- poperror();
- qunlock(&c->l);
- // Wakeup(&c->startr);
- if(Debug)
- print("started\n");
-
- // while(waserror())
- // oscmdkill(t);
- // osenter();
- mwait(&c->p->ac->icc->fn);
- // n = oscmdwait(t, status, sizeof(status));
- // osleave();
- if(n < 0){
- // oserrstr(up->genbuf, sizeof(up->genbuf));
- n = snprint(status, sizeof(status), "0 0 0 0 %q", up->genbuf);
- }
- qlock(&c->l);
- c->child = nil;
- // oscmdfree(t);
- if(Debug){
- status[n]=0;
- print("done %d %d %d: %q\n", c->fd[0], c->fd[1], c->fd[2], status);
- }
- if(c->inuse > 0){
- c->state = "Done";
- if(c->waitq != nil)
- qproduce(c->waitq, status, n);
- }else
- closeconv(c);
- qunlock(&c->l);
- pexit("", 0);
- }
- Dev cmddevtab = {
- 'C',
- "cmd",
- devreset,
- cmdinit,
- devshutdown,
- cmdattach,
- cmdwalk,
- cmdstat,
- cmdopen,
- devcreate,
- cmdclose,
- cmdread,
- devbread,
- cmdwrite,
- devbwrite,
- devremove,
- cmdwstat
- };
|