ref: 7c69c6d7f62c06ced83e85eba0f04deea14f73c8
parent: 748142a97c32cd48a8df7e354b40d9638e27ae38
author: cinap_lenrek <[email protected]>
date: Sat Nov 23 19:21:28 EST 2019
cmd(3): port the os command execution device from inferno (win32 only so far) the devcmd device (/mnt/term/cmd) allows execution of commands on the os, with access stdin/stdout/stderr of the executing command.
--- a/kern/Makefile
+++ b/kern/Makefile
@@ -10,6 +10,7 @@
dev.$O\
devaudio.$O\
devaudio-$(AUDIO).$O\
+ devcmd.$O\
devcons.$O\
devdraw.$O\
devfs-$(OS).$O\
@@ -16,7 +17,7 @@
devip.$O\
devip-$(OS).$O\
devkbd.$O\
- devlfd.$O\
+ devlfd-$(OS).$O\
devmnt.$O\
devmouse.$O\
devpipe.$O\
--- /dev/null
+++ b/kern/devcmd.c
@@ -1,0 +1,700 @@
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Qtopdir, /* top level directory */
+ Qcmd,
+ Qclonus,
+ Qconvdir,
+ Qconvbase,
+ Qdata = Qconvbase,
+ Qstderr,
+ Qctl,
+ 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;
+ Chan* fd[3]; /* stdin, stdout, and stderr */
+ int count[3]; /* number of readers on stdin/stdout/stderr */
+ int perm;
+ char* owner;
+ char* state;
+ Cmdbuf* cmd;
+ char* dir;
+ QLock l; /* protects state changes */
+ Queue* waitq;
+ void* child;
+ char* error; /* on start up */
+ int nice;
+ short killonclose;
+ short killed;
+ Rendez startr;
+};
+
+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 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 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 int
+cmdstat(Chan *c, uchar *db, int 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 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, ".");
+ 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] != nil){
+ cclose(c->fd[fd]);
+ c->fd[fd] = nil;
+ }
+}
+
+static void
+cmdclose(Chan *c)
+{
+ Conv *cc;
+ int r;
+
+ if((c->flag & COPEN) == 0)
+ return;
+
+ switch(TYPE(c->qid)) {
+ case Qctl:
+ 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)){
+ iprint("cmdclose killing (killonclose %d)\n", cc->killonclose);
+ oscmdkill(cc->child);
+ cc->killed = 1;
+ }
+ }else if(r == 0)
+ closeconv(cc);
+
+ qunlock(&cc->l);
+ break;
+ }
+}
+
+static long
+cmdread(Chan *ch, void *a, long n, vlong offset)
+{
+ Conv *c;
+ char *p, *cmds;
+ int fd;
+
+ USED(offset);
+
+ p = 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, p, 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, p, 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);
+ ch = c->fd[fd];
+ if(ch == nil){
+ qunlock(&c->l);
+ return 0;
+ }
+ incref(&ch->ref);
+ qunlock(&c->l);
+ if(waserror()){
+ cclose(ch);
+ nexterror();
+ }
+ n = devtab[ch->type]->read(ch, a, n, 0);
+ if(n < 0)
+ oserror();
+ poperror();
+ cclose(ch);
+ 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,
+ CMexec,
+ CMkill,
+ CMnice,
+ CMkillonclose
+};
+
+static
+Cmdtab cmdtab[] = {
+ CMdir, "dir", 2,
+ CMexec, "exec", 0,
+ CMkill, "kill", 1,
+ CMnice, "nice", 0,
+ CMkillonclose, "killonclose", 0,
+};
+
+static long
+cmdwrite(Chan *ch, void *a, long n, vlong offset)
+{
+ int i, r;
+ Conv *c;
+ 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 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] != nil)
+ error(Einuse);
+ if(cb->nf < 1)
+ error(Etoosmall);
+ kproc("cmdproc", cmdproc, c); /* 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 */
+ case CMkill:
+ qlock(&c->l);
+ if(waserror()){
+ qunlock(&c->l);
+ nexterror();
+ }
+ if(c->child == nil)
+ error("not started");
+ if(oscmdkill(c->child) < 0)
+ oserror();
+ poperror();
+ qunlock(&c->l);
+ break;
+ case CMnice:
+ c->nice = cb->nf > 1? atoi(cb->f[1]): 1;
+ break;
+ case CMkillonclose:
+ c->killonclose = 1;
+ break;
+ }
+ poperror();
+ free(cb);
+ break;
+ case Qdata:
+ c = cmd.conv[CONV(ch->qid)];
+ qlock(&c->l);
+ ch = c->fd[0];
+ if(ch == nil){
+ qunlock(&c->l);
+ error(Ehungup);
+ }
+ incref(&ch->ref);
+ qunlock(&c->l);
+ if(waserror()){
+ cclose(ch);
+ nexterror();
+ }
+ r = devtab[ch->type]->write(ch, a, n, 0);
+ if(r == 0)
+ error(Ehungup);
+ if(r < 0) {
+ /* XXX perhaps should kill writer "write on closed pipe" here, 2nd time around? */
+ oserror();
+ }
+ poperror();
+ cclose(ch);
+ return r;
+ }
+ return n;
+}
+
+static int
+cmdwstat(Chan *c, uchar *dp, int 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, ".");
+ c->perm = 0660;
+ c->state = "Closed";
+ for(i=0; i<nelem(c->fd); i++)
+ c->fd[i] = nil;
+
+ 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";
+ wakeup(&c->startr);
+ qunlock(&c->l);
+ 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()){
+ iprint("XXX %s\n", up->errstr);
+ oscmdkill(t);
+ }
+ n = oscmdwait(t, status, sizeof(status));
+ if(n < 0){
+ oserrstr();
+ n = snprint(status, sizeof(status), "0 0 0 0 %q", up->errstr);
+ }
+ qlock(&c->l);
+ c->child = nil;
+ oscmdfree(t);
+ if(Debug){
+ status[n]=0;
+ print("done %s %s %s: %q\n", chanpath(c->fd[0]), chanpath(c->fd[1]), chanpath(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
+};
--- /dev/null
+++ b/kern/devlfd-posix.c
@@ -1,0 +1,115 @@
+#include "u.h"
+#include <errno.h>
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#undef read
+#undef write
+
+Chan*
+lfdchan(void *fd)
+{
+ Chan *c;
+
+ c = newchan();
+ c->type = devno('L', 0);
+ c->aux = fd;
+ c->path = newpath("fd");
+ c->mode = ORDWR;
+ c->qid.type = 0;
+ c->qid.path = 0;
+ c->qid.vers = 0;
+ c->dev = 0;
+ c->offset = 0;
+ return c;
+}
+
+static Chan*
+lfdattach(char *x)
+{
+ USED(x);
+
+ error(Egreg);
+ return nil;
+}
+
+static Walkqid*
+lfdwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ USED(c);
+ USED(nc);
+ USED(name);
+ USED(nname);
+ error(Egreg);
+ return nil;
+}
+
+static int
+lfdstat(Chan *c, uchar *dp, int n)
+{
+ USED(c);
+ USED(dp);
+ USED(n);
+ error(Egreg);
+ return -1;
+}
+
+static Chan*
+lfdopen(Chan *c, int omode)
+{
+ USED(c);
+ USED(omode);
+ error(Egreg);
+ return nil;
+}
+
+static void
+lfdclose(Chan *c)
+{
+ close((int)(uintptr)c->aux);
+}
+
+static long
+lfdread(Chan *c, void *buf, long n, vlong off)
+{
+ USED(off); /* can't pread on pipes */
+
+ n = read((int)(uintptr)c->aux, buf, n);
+ if(n < 0)
+ oserror();
+ return n;
+}
+
+static long
+lfdwrite(Chan *c, void *buf, long n, vlong off)
+{
+ USED(off); /* can't pread on pipes */
+
+ n = write((int)(uintptr)c->aux, buf, n);
+ if(n < 0)
+ oserror();
+ return n;
+}
+
+Dev lfddevtab = {
+ 'L',
+ "lfd",
+
+ devreset,
+ devinit,
+ devshutdown,
+ lfdattach,
+ lfdwalk,
+ lfdstat,
+ lfdopen,
+ devcreate,
+ lfdclose,
+ lfdread,
+ devbread,
+ lfdwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- /dev/null
+++ b/kern/devlfd-win32.c
@@ -1,0 +1,113 @@
+#include <windows.h>
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+Chan*
+lfdchan(void *fd)
+{
+ Chan *c;
+
+ c = newchan();
+ c->type = devno('L', 0);
+ c->aux = fd;
+ c->path = newpath("fd");
+ c->mode = ORDWR;
+ c->qid.type = 0;
+ c->qid.path = 0;
+ c->qid.vers = 0;
+ c->dev = 0;
+ c->offset = 0;
+ return c;
+}
+
+static Chan*
+lfdattach(char *x)
+{
+ USED(x);
+
+ error(Egreg);
+ return nil;
+}
+
+static Walkqid*
+lfdwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ USED(c);
+ USED(nc);
+ USED(name);
+ USED(nname);
+
+ error(Egreg);
+ return nil;
+}
+
+static int
+lfdstat(Chan *c, uchar *dp, int n)
+{
+ USED(c);
+ USED(dp);
+ USED(n);
+ error(Egreg);
+ return -1;
+}
+
+static Chan*
+lfdopen(Chan *c, int omode)
+{
+ USED(c);
+ USED(omode);
+ error(Egreg);
+ return nil;
+}
+
+static void
+lfdclose(Chan *c)
+{
+ CloseHandle((HANDLE)c->aux);
+}
+
+static long
+lfdread(Chan *c, void *buf, long n, vlong off)
+{
+ DWORD r;
+
+ USED(off); /* can't pread on pipes */
+ if(!ReadFile((HANDLE)c->aux, buf, (DWORD)n, &r, NULL))
+ oserror();
+ return r;
+}
+
+static long
+lfdwrite(Chan *c, void *buf, long n, vlong off)
+{
+ DWORD r;
+
+ USED(off); /* can't pread on pipes */
+ if(!WriteFile((HANDLE)c->aux, buf, (DWORD)n, &r, NULL))
+ oserror();
+ return r;
+}
+
+Dev lfddevtab = {
+ 'L',
+ "lfd",
+
+ devreset,
+ devinit,
+ devshutdown,
+ lfdattach,
+ lfdwalk,
+ lfdstat,
+ lfdopen,
+ devcreate,
+ lfdclose,
+ lfdread,
+ devbread,
+ lfdwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- a/kern/devlfd.c
+++ /dev/null
@@ -1,126 +1,0 @@
-#include "u.h"
-#include <errno.h>
-#include "lib.h"
-#include "dat.h"
-#include "fns.h"
-#include "error.h"
-
-#undef pread
-#undef pwrite
-
-Chan*
-lfdchan(int fd)
-{
- Chan *c;
-
- c = newchan();
- c->type = devno('L', 0);
- c->aux = (void*)(uintptr)fd;
- c->path = newpath("fd");
- c->mode = ORDWR;
- c->qid.type = 0;
- c->qid.path = 0;
- c->qid.vers = 0;
- c->dev = 0;
- c->offset = 0;
- return c;
-}
-
-int
-lfdfd(int fd)
-{
- return newfd(lfdchan(fd));
-}
-
-static Chan*
-lfdattach(char *x)
-{
- USED(x);
-
- error(Egreg);
- return nil;
-}
-
-static Walkqid*
-lfdwalk(Chan *c, Chan *nc, char **name, int nname)
-{
- USED(c);
- USED(nc);
- USED(name);
- USED(nname);
-
- error(Egreg);
- return nil;
-}
-
-static int
-lfdstat(Chan *c, uchar *dp, int n)
-{
- USED(c);
- USED(dp);
- USED(n);
- error(Egreg);
- return -1;
-}
-
-static Chan*
-lfdopen(Chan *c, int omode)
-{
- USED(c);
- USED(omode);
-
- error(Egreg);
- return nil;
-}
-
-static void
-lfdclose(Chan *c)
-{
- close((int)(uintptr)c->aux);
-}
-
-static long
-lfdread(Chan *c, void *buf, long n, vlong off)
-{
- USED(off); /* can't pread on pipes */
- n = read((int)(uintptr)c->aux, buf, n);
- if(n < 0){
- iprint("error %d\n", errno);
- oserror();
- }
- return n;
-}
-
-static long
-lfdwrite(Chan *c, void *buf, long n, vlong off)
-{
- USED(off); /* can't pread on pipes */
-
- n = write((int)(uintptr)c->aux, buf, n);
- if(n < 0){
- iprint("error %d\n", errno);
- oserror();
- }
- return n;
-}
-
-Dev lfddevtab = {
- 'L',
- "lfd",
-
- devreset,
- devinit,
- devshutdown,
- lfdattach,
- lfdwalk,
- lfdstat,
- lfdopen,
- devcreate,
- lfdclose,
- lfdread,
- devbread,
- lfdwrite,
- devbwrite,
- devremove,
- devwstat,
-};
--- a/kern/devtab.c
+++ b/kern/devtab.c
@@ -17,6 +17,7 @@
extern Dev lfddevtab;
extern Dev audiodevtab;
extern Dev kbddevtab;
+extern Dev cmddevtab;
Dev *devtab[] = {
&rootdevtab,
@@ -32,6 +33,7 @@
&lfddevtab,
&audiodevtab,
&kbddevtab,
+ &cmddevtab,
0
};
--- a/kern/fns.h
+++ b/kern/fns.h
@@ -84,6 +84,7 @@
void kstrcpy(char*, char*, int);
void kstrdup(char**, char*);
long latin1(Rune*, int);
+Chan* lfdchan(void *);
void lock(Lock*);
void lockinit(void);
void logopen(Log*);
@@ -114,8 +115,13 @@
char* nextelem(char*, char*);
void nexterror(void);
int openmode(ulong);
+void* oscmd(char**, int, char*, Chan**);
+int oscmdwait(void*, char*, int);
+int oscmdkill(void*);
+void oscmdfree(void*);
void oserrstr(void);
void oserror(void);
+void osexit(void);
Block* packblock(Block*);
Block* padblock(Block*, int);
void panic(char*, ...);
--- a/kern/posix.c
+++ b/kern/posix.c
@@ -106,8 +106,19 @@
nexterror();
}
-static void* tramp(void*);
+static void*
+tramp(void *vp)
+{
+ Proc *p;
+ p = vp;
+ if(pthread_setspecific(prdakey, p))
+ panic("cannot setspecific");
+ (*p->fn)(p->arg);
+ pexit("", 0);
+ return 0;
+}
+
void
osproc(Proc *p)
{
@@ -120,19 +131,11 @@
sched_yield();
}
-static void*
-tramp(void *vp)
+void
+osexit(void)
{
- Proc *p;
-
- p = vp;
- if(pthread_setspecific(prdakey, p))
- panic("cannot setspecific");
- (*p->fn)(p->arg);
- /* BUG: leaks Proc */
pthread_setspecific(prdakey, 0);
pthread_exit(0);
- return 0;
}
void
@@ -161,6 +164,41 @@
if(op->nwakeup == op->nsleep)
pthread_cond_signal(&op->cond);
pthread_mutex_unlock(&op->mutex);
+}
+
+void*
+oscmd(char **argv, int nice, char *dir, Chan **fd)
+{
+ USED(argv);
+ USED(nice);
+ USED(dir);
+ USED(fd);
+
+ error("not implemented");
+ return nil;
+}
+
+int
+oscmdwait(void *c, char *status, int nstatus)
+{
+ USED(c);
+ USED(status);
+ USED(nstatus);
+
+ return -1;
+}
+
+int
+oscmdkill(void *c)
+{
+ USED(c);
+ return -1;
+}
+
+void
+oscmdfree(void *c)
+{
+ USED(c);
}
static int randfd;
--- a/kern/procinit.c
+++ b/kern/procinit.c
@@ -4,8 +4,6 @@
#include "fns.h"
#include "error.h"
-Rgrp *thergrp;
-
void
procinit0(void)
{
@@ -51,13 +49,13 @@
p->slash = cclone(up->slash);
p->dot = cclone(up->dot);
p->rgrp = up->rgrp;
- if(p->rgrp)
+ if(p->rgrp != nil)
incref(&p->rgrp->ref);
p->pgrp = up->pgrp;
- if(up->pgrp)
+ if(up->pgrp != nil)
incref(&up->pgrp->ref);
p->fgrp = up->fgrp;
- if(p->fgrp)
+ if(p->fgrp != nil)
incref(&p->fgrp->ref);
strecpy(p->text, p->text+sizeof p->text, name);
@@ -65,3 +63,30 @@
return p->pid;
}
+void
+pexit(char *msg, int freemem)
+{
+ Proc *p = up;
+
+ USED(msg);
+ USED(freemem);
+
+ if(p->pgrp != nil){
+ closepgrp(p->pgrp);
+ p->pgrp = nil;
+ }
+ if(p->rgrp != nil){
+ closergrp(p->rgrp);
+ p->rgrp = nil;
+ }
+ if(p->fgrp != nil){
+ closefgrp(p->fgrp);
+ p->fgrp = nil;
+ }
+
+ cclose(p->dot);
+ cclose(p->slash);
+
+ free(p);
+ osexit();
+}
--- a/kern/win32.c
+++ b/kern/win32.c
@@ -92,7 +92,7 @@
_setproc(p);
op->tid = GetCurrentThreadId();
(*p->fn)(p->arg);
- ExitThread(0);
+ pexit("", 0);
return 0;
}
@@ -108,6 +108,12 @@
}
void
+osexit(void)
+{
+ ExitThread(0);
+}
+
+void
procsleep(void)
{
Proc *p;
@@ -251,14 +257,14 @@
warg = GetCommandLineW();
n = wcslen(warg)*UTFmax+1;
- arg = malloc(n);
- WideCharToMultiByte(CP_UTF8,0,warg,-1,arg,n,0,0);
+ arg = smalloc(n);
+ WideCharToMultiByte(CP_UTF8, 0, warg, -1, arg, n, 0, 0);
/* conservative guess at the number of args */
for(argc=4,p=arg; *p; p++)
if(*p == ' ' || *p == '\t')
argc++;
- argv = malloc(argc*sizeof(char*));
+ argv = smalloc(argc*sizeof(char*));
argc = args(argv, argc, arg);
main(argc, argv);
@@ -266,7 +272,155 @@
return 0;
}
+static char*
+qarg(char *s)
+{
+ char *d, *p;
+ int n, c;
+
+ n = strlen(s);
+ d = p = smalloc(3+2*n);
+ if(s[0] == '"' || (strchr(s, ' ') == nil && strchr(s, '\t') == nil)){
+ memmove(d, s, n+1);
+ return d;
+ }
+ *p++ = '"';
+ while((c = *s++) != 0){
+ if(c == '\\' || c == '"')
+ *p++ = '\\';
+ *p++ = c;
+ }
+ *p++ = '"';
+ *p = 0;
+ return d;
+}
+
+static wchar_t*
+wcmdline(char **argv)
+{
+ wchar_t *s, *w, *e;
+ int n, i;
+ char *q;
+
+ n = 0;
+ for(i = 0; argv[i] != nil; i++){
+ q = qarg(argv[i]);
+ n += strlen(q)+1;
+ free(q);
+ }
+ s = smalloc((n+1)*sizeof(wchar_t));
+ w = s;
+ e = s + n;
+ for(i = 0; argv[i] != nil; i++){
+ if(i != 0)
+ *w++ = L' ';
+ q = qarg(argv[i]);
+ w += MultiByteToWideChar(CP_UTF8, 0, q, strlen(argv[i]), w, e - w);
+ free(q);
+ }
+ *w = 0;
+ return s;
+}
+
+void*
+oscmd(char **argv, int nice, char *dir, Chan **fd)
+{
+ SECURITY_ATTRIBUTES sa;
+ PROCESS_INFORMATION pi;
+ STARTUPINFOW si;
+ HANDLE p[3][2], tmp;
+ wchar_t *wcmd, *wdir;
+ int i;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ for(i = 0; i < 3; i++){
+ if(!CreatePipe(&p[i][i==0], &p[i][i!=0], &sa, 0)
+ || !DuplicateHandle(GetCurrentProcess(), p[i][0], GetCurrentProcess(), &tmp, 0, FALSE, DUPLICATE_SAME_ACCESS)){
+ while(--i >= 0){
+ CloseHandle(p[i][0]);
+ CloseHandle(p[i][1]);
+ }
+ oserror();
+ }
+ CloseHandle(p[i][0]);
+ p[i][0] = tmp;
+ }
+
+ if(waserror()){
+ for(i = 0; i < 3; i++){
+ CloseHandle(p[i][0]);
+ CloseHandle(p[i][1]);
+ }
+ nexterror();
+ }
+
+ memset(&pi, 0, sizeof(pi));
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = p[0][1];
+ si.hStdOutput = p[1][1];
+ si.hStdError = p[2][1];
+ si.lpDesktop = L"";
+
+ i = strlen(dir)+1;
+ wdir = smalloc(i*sizeof(wchar_t));
+ MultiByteToWideChar(CP_UTF8, 0, dir, i, wdir, i);
+
+ wcmd = wcmdline(argv);
+ if(waserror()){
+ free(wcmd);
+ nexterror();
+ }
+
+ if(!CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP|CREATE_NO_WINDOW, NULL, wdir, &si, &pi))
+ oserror();
+
+ poperror();
+ free(wcmd);
+ free(wdir);
+
+ poperror();
+ for(i = 0; i < 3; i++){
+ fd[i] = lfdchan((void*)p[i][0]);
+ CloseHandle(p[i][1]);
+ }
+ CloseHandle(pi.hThread);
+ return (void*)pi.hProcess;
+}
+
+int
+oscmdwait(void *c, char *status, int nstatus)
+{
+ DWORD code = -1;
+ for(;;){
+ if(!GetExitCodeProcess((HANDLE)c, &code))
+ return -1;
+ if(code != STILL_ACTIVE)
+ break;
+ WaitForSingleObject((HANDLE)c, INFINITE);
+ }
+ return snprint(status, nstatus, "%d", (int)code);
+}
+
+int
+oscmdkill(void *c)
+{
+ TerminateProcess((HANDLE)c, 0);
+ return 0;
+}
+
void
+oscmdfree(void *c)
+{
+ CloseHandle((HANDLE)c);
+}
+
+void
oserrstr(void)
{
char *p, *q;
@@ -296,9 +450,7 @@
wchar_t *action, *arg, *cmd, *p;
int m;
- cmd = malloc((n+1)*sizeof(wchar_t));
- if(cmd == nil)
- error("out of memory");
+ cmd = smalloc((n+1)*sizeof(wchar_t));
m = MultiByteToWideChar(CP_UTF8,0,a,n,cmd,n);
while(m > 0 && cmd[m-1] == '\n')
m--;
--- a/main.c
+++ b/main.c
@@ -52,6 +52,7 @@
if(bind("#U", "/root", MREPL) < 0)
panic("bind #U: %r");
bind("#A", "/dev", MAFTER);
+ bind("#C", "/", MAFTER);
if(open("/dev/cons", OREAD) != 0)
panic("open0: %r");