ref: 25d46a497531211ed517e2c78902d873e95b8ca5
parent: 186de178d5ddf4ed91d9f9410de238fe1d3ec4b9
author: 9ferno <[email protected]>
date: Sun Nov 21 11:50:52 EST 2021
working version of devforth
--- /dev/null
+++ b/os/port/devforth.c
@@ -1,0 +1,520 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+static int debug = 0;
+
+/* TODO
+ add memory, variables, dictionary, return stack, parameter stack
+ */
+enum
+{
+ NForthProc = 256,
+
+ Qtopdir = 0,
+ Qforthdir,
+ Qnew,
+ Qfprocdir,
+ Qctl,
+ Qvars,
+};
+
+/*
+ * Qids are, in path:
+ * 4 bits of file type (qids above)
+ * 23 bits of process slot number (procindex(pid)) + 1; 0 means not attached to forth proc
+ * in vers,
+ * 32 bits of pid, for consistency checking
+ */
+#define QSHIFT 5 /* location in qid of proc slot # */
+
+#define QID(q) ((((u32)(q).path)&0x0000001F)>>0)
+#define SLOT(q) (((((u32)(q).path)&0x07FFFFFE0)>>QSHIFT)-1)
+#define PID(q) ((q).vers)
+#define NOTEID(q) ((q).vers)
+
+/* TODO kproc or mechanism to garbage collect these ForthProc */
+typedef struct ForthProc ForthProc;
+struct ForthProc
+{
+ Proc *p;
+ ForthProc *prev, *next;
+};
+
+int nforthprocs = 0;
+ForthProc *fhead, *ftail;
+static QLock forthlock;
+
+static void
+flock(void)
+{
+ qlock(&forthlock);
+}
+
+static int
+canflock(void)
+{
+ return canqlock(&forthlock);
+}
+
+static void
+funlock(void)
+{
+ qunlock(&forthlock);
+}
+
+extern int kclose(int fd);
+extern int kopen(char *path, int mode);
+extern s32 kread(int fd, void *va, s32 n);
+extern s32 kwrite(int fd, void *va, s32 n);
+extern char* kfd2path(int fd);
+
+extern int forthmain(char *);
+void
+forthentry(void *fmem)
+{
+ int n;
+ char buf[1024];
+
+ up->type = Unknown;
+ print("forthentry pid %d forthmem 0x%zx\n", up->pid, (intptr)fmem);
+ print("forth entry kfd2path(0) %s kfd2path(1) %s\n", kfd2path(0), kfd2path(1));
+/*int fd = kopen(kfd2path(1),OREAD);
+while((n = kread(fd, buf, 1024)) > 0)
+ print("forth entry %d bytes: %s\n", n, buf);
+kclose(fd);*/
+n = forthmain(fmem);
+print("forthentry n %d\n", n);
+ pexit("exit", 0);
+ for(;;){up->state = Dead;
+ sched();}
+}
+
+ForthProc *
+newforthproc(void)
+{
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ ForthProc *f;
+ void *forthmem;
+
+ while((p = newproc()) == nil){
+/* TODO freebroken(); */
+ resrcwait("no procs for kproc");
+ }
+
+ qlock(&p->debug);
+ p->psstate = 0;
+ p->kp = 0;
+
+ p->fpsave = up->fpsave;
+ p->nerrlab = 0;
+
+ kstrdup(&p->env->user, up->env->user);
+ pg = up->env->pgrp;
+ incref(pg);
+ p->env->pgrp = pg;
+
+ fg = up->env->fgrp;
+ incref(fg);
+ p->env->fgrp = fg;
+
+ eg = up->env->egrp;
+ if(eg != nil)
+ incref(eg);
+ p->env->egrp = eg;
+
+ p->nnote = 0;
+ p->notify = nil;
+ p->notified = 0;
+ p->notepending = 0;
+
+ p->procmode = 0640;
+ p->privatemem = 0;
+ p->noswap = 0;
+ p->hang = 0;
+ p->kp = 0;
+
+ f = malloc(sizeof(ForthProc));
+ if(f == nil)
+ panic("newforthproc\n");
+ if(fhead == nil){
+ fhead = ftail = f;
+ }else{
+ ftail->next = f;
+ f->prev = ftail;
+ ftail = f;
+ }
+ f->p = p;
+ forthmem = nil; /*malloc(FORTHHEAPSIZE);;
+ if(forthmem == nil)
+ panic("newforthproc forthmem == nil\n");*/
+ nforthprocs++;
+
+/* p->kpfun = func;
+ p->kparg = arg;
+ kprocchild(p, linkproc);*/
+/* this does all of the above 3 lines */
+ kprocchild(p, forthentry, forthmem);
+
+ strcpy(p->text, "forth");
+
+/* if(kpgrp == nil)
+ kpgrp = newpgrp();
+ p->pgrp = kpgrp;
+ incref(kpgrp);*/
+
+ memset(p->time, 0, sizeof(p->time));
+ p->time[TReal] = MACHP(0)->ticks;
+/* cycles(&p->kentry);
+ p->pcycles = -p->kentry;*/
+
+ qunlock(&p->debug);
+ p->psstate = nil;
+
+ print("newforthproc kfd2path(0) %s kfd2path(1) %s\n", kfd2path(0), kfd2path(1));
+/* int n;
+int fd = kopen(kfd2path(1),OREAD);
+n = kwrite(fd, "junk sent to 1\n", strlen("junk sent to 1\n"));
+ print("sent to forth %d bytes:\n", n);
+kclose(fd);*/
+
+ ready(p);
+ return f;
+}
+
+/*
+ * mostly the same as procgen() of devproc.c
+ * and drawgen() of devdraw.c
+ * TODO get the permissions from Proc.procmode
+ */
+static int
+forthgen(Chan *c, char *name, Dirtab *, int, int s, Dir *dp)
+{
+ Qid q;
+ ForthProc *f;
+ char *ename;
+ u32 pid, path;
+ s32 slot, i, t;
+ Proc *p;
+
+ DBG("forthgen c->path %s name %s s %d c->qid.path 0x%zux "
+ "slot %d qid %d c->qid.vers %d c->qid.type %d 0x%ux\n",
+ chanpath(c), name, s, c->qid.path, SLOT(c->qid),
+ QID(c->qid), c->qid.vers, c->qid.type, c->qid.type);
+ /*
+ * if I do .. from #f or #f/forth
+ */
+ if(s == DEVDOTDOT){
+ switch(QID(c->qid)){
+ case Qtopdir:
+ case Qforthdir:
+ mkqid(&q, Qtopdir, 0, QTDIR);
+ devdir(c, q, "#f", 0, eve, 0555, dp);
+ break;
+ case Qfprocdir:
+ mkqid(&q, Qforthdir, 0, QTDIR);
+ devdir(c, q, "forth", 0, eve, 0555, dp);
+ break;
+ default:
+ panic("drawwalk %llux", c->qid.path);
+ }
+ return 1;
+ }
+
+ /*
+ * Top level directory contains the name of the device.
+ */
+ t = QID(c->qid);
+ switch(t){
+ case Qtopdir:
+ if(s == 0){
+ mkqid(&q, Qforthdir, 0, QTDIR);
+ devdir(c, q, "forth", 0, eve, 0555, dp);
+ return 1;
+ }
+ return -1;
+ }
+
+ /*
+ * Second level contains "new" plus all the forth procs.
+ */
+ switch(t){
+ case Qforthdir:
+ if(s == 0){
+ case Qnew: /* this label is just a comment(?), has no purpose */
+ mkqid(&q, Qnew, 0, QTFILE);
+ devdir(c, q, "new", 0, eve, 0666, dp);
+ return 1;
+ }
+
+ DBG("forthgen Qforthdir name %s s %d nforthprocs %d\n", name, s, nforthprocs);
+ if(nforthprocs == 0)
+ return -1;
+ if(name != nil){
+ /* ignore s and use name to find pid */
+ pid = strtol(name, &ename, 10);
+ if(pid==0 || ename[0]!='\0')
+ return -1;
+ slot = procindex(pid);
+ if(slot < 0)
+ return -1;
+ }else{
+ s = s-1;
+ if(s >= nforthprocs)
+ return -1;
+ i = 0;
+ for(f = fhead; f != nil && i < s; f=f->next, i++)
+ ;
+ if(f == nil || f->p == nil || f->p->pid == 0)
+ return -1;
+ pid = f->p->pid;
+ if(pid==0)
+ return -1;
+ slot = procindex(pid);
+ if(slot < 0)
+ return -1;
+ }
+ DBG("forthgen Qforthdir slot %d\n", slot);
+ /*
+ * String comparison is done in devwalk so name must match its formatted pid
+ */
+ p = proctab(slot);
+ snprint(up->genbuf, sizeof(up->genbuf), "%ud", pid);
+ if(name != nil && strcmp(name, up->genbuf) != 0)
+ return -1;
+ mkqid(&q, ((slot+1)<<QSHIFT)|Qfprocdir, pid, QTDIR);
+ devdir(c, q, up->genbuf, 0, p->env->user, 0555, dp);
+ return 1;
+ }
+
+ /*
+ * Third level.
+ */
+ p = proctab(SLOT(c->qid));
+ path = c->qid.path&~((1<<QSHIFT)-1); /* slot component */
+ switch(s){
+ case 0:
+ mkqid(&q, path|Qctl, c->qid.vers, QTFILE);
+ devdir(c, q, "ctl", 0, p->env->user, 0600, dp);
+ break;
+ case 1:
+ mkqid(&q, path|Qvars, c->qid.vers, QTFILE);
+ devdir(c, q, "vars", 0, p->env->user, 0600, dp);
+ break;
+ default:
+ return -1;
+ }
+ return 1;
+}
+
+static Chan*
+forthattach(char *spec)
+{
+ DBG("forthattach spec %s\n", spec);
+ return devattach('f', spec);
+}
+
+static Walkqid*
+forthwalk(Chan *c, Chan *nc, char **name, s32 nname)
+{
+ DBG("forthwalk c->path %s nc->path %s name[0] %s nname %d\n",
+ chanpath(c), chanpath(nc), name[0], nname);
+ return devwalk(c, nc, name, nname, nil, 0, forthgen);
+}
+
+static int
+forthstat(Chan *c, uchar *db, s32 n)
+{
+ DBG("forthstat c->path %s\n", chanpath(c));
+ return devstat(c, db, n, nil, 0, forthgen);
+}
+
+/*
+ * this is from devproc.c
+ * none can't read or write state on other
+ * processes. This is to contain access of
+ * servers running as none should they be
+ * subverted by, for example, a stack attack.
+ */
+static void
+nonone(Proc *p)
+{
+ if(p == up)
+ return;
+ if(strcmp(up->env->user, "none") != 0)
+ return;
+ if(iseve())
+ return;
+ error(Eperm);
+}
+
+/*
+ Opening the new file creates a forth process. The file
+ descriptor returned from the open(2) will point to the con-
+ trol file, ctl, of the newly created forth process. Reading
+ ctl returns a text string representing the pid.
+ */
+static Chan*
+forthopen(Chan *c, u32 omode0)
+{
+ Proc *p;
+ Chan *tc;
+ u32 pid;
+ s32 slot;
+ int omode;
+ ForthProc *f;
+
+ DBG("forthopen c->path %s omode0 0x%ux\n", chanpath(c), omode0);
+ if(c->qid.type & QTDIR)
+ return devopen(c, omode0, nil, 0, forthgen);
+
+ flock();
+ if(waserror()){
+ funlock();
+ nexterror();
+ }
+ if(QID(c->qid) == Qnew){
+ f = newforthproc();
+ if(f == nil)
+ error(Enodev);
+ slot = procindex(f->p->pid);
+ if(slot < 0)
+ panic("forthopen");
+ mkqid(&c->qid, Qctl|(slot+1)<<QSHIFT, f->p->pid, QTFILE);
+ DBG("forthopen: new proc pid %d\n", f->p->pid);
+ }
+ funlock();
+ poperror();
+
+ p = proctab(SLOT(c->qid));
+ eqlock(&p->debug);
+ if(waserror()){
+ qunlock(&p->debug);
+ nexterror();
+ }
+ pid = PID(c->qid);
+ if(p->pid != pid)
+ error(Eprocdied);
+
+ omode = openmode(omode0);
+
+ switch(QID(c->qid)){
+ case Qnew:
+ break;
+ case Qctl:
+ break;
+ case Qvars:
+ if(p->kp || p->privatemem)
+ error(Eperm);
+ break;
+
+ default:
+ print("forthopen %#ux\n", QID(c->qid));
+ error(Egreg);
+ }
+ nonone(p);
+
+ /* Affix pid to qid */
+ if(pid == 0)
+ error(Eprocdied);
+ c->qid.vers = pid;
+
+ tc = devopen(c, omode, 0, 0, forthgen);
+ if(waserror()){
+ cclose(tc);
+ nexterror();
+ }
+ poperror();
+
+ qunlock(&p->debug);
+ poperror(); /* eqlock */
+ DBG("forthopen returning tc->path %s omode0 0x%ux tc->qid.vers %d\n", chanpath(tc), omode, tc->qid.vers);
+ return tc;
+}
+
+static void
+forthclose(Chan *c)
+{
+ DBG("forthclose c->path %s\n", chanpath(c));
+ /* TODO close the Chan*? */
+ return;
+}
+
+int readdone = 0;
+s32
+forthread(Chan *c, void *a, s32 n, s64)
+{
+ Proc *p;
+
+ DBG("forthread c->path %s\n", chanpath(c));
+ if(c->qid.type & QTDIR)
+ return devdirread(c, a, n, nil, 0, forthgen);
+
+ p = proctab(SLOT(c->qid));
+ if(p->pid != PID(c->qid))
+ error(Eprocdied);
+
+ eqlock(&p->debug);
+ if(waserror()){
+ qunlock(&p->debug);
+ nexterror();
+ }
+ switch(QID(c->qid)){
+ case Qctl:
+ if(readdone == 0){
+ readdone = 1;
+ } else if (readdone == 1){
+ n = 0;
+ break;
+ }
+ n = sprint(a, "%d", p->pid);
+ break;
+ case Qvars: /* TODO */
+ error(Ebadarg);
+ default:
+ print("unknown qid in forthread\n");
+ error(Egreg);
+ }
+
+ qunlock(&p->debug);
+ poperror();
+ DBG("forthread returning n %d bytes\n", n);
+ return n;
+}
+
+static s32
+forthwrite(Chan *c, void *, s32, s64)
+{
+ DBG("forthwrite c->path %s\n", chanpath(c));
+ if(c->qid.type & QTDIR)
+ error(Eisdir);
+
+ return 0;
+}
+
+Dev forthdevtab = {
+ 'f',
+ "forth",
+
+ devreset,
+ devinit,
+ devshutdown,
+ forthattach,
+ forthwalk,
+ forthstat,
+ forthopen,
+ devcreate,
+ forthclose,
+ forthread,
+ devbread,
+ forthwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- /dev/null
+++ b/os/port/devproc.c
@@ -1,0 +1,1690 @@
+#include "u.h"
+#include <trace.h>
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "ureg.h"
+#include "edf.h"
+
+enum
+{
+ Qdir,
+ Qtrace,
+ Qargs,
+ Qctl,
+ Qfd,
+ Qfpregs,
+ Qkregs,
+ Qmem,
+ Qnote,
+ Qnoteid,
+ Qnotepg,
+ Qns,
+ Qppid,
+ Qproc,
+ Qregs,
+ Qsegment,
+ Qstatus,
+ Qtext,
+ Qwait,
+ Qprofile,
+ Qsyscall,
+ Qwatchpt,
+};
+
+enum
+{
+ CMclose,
+ CMclosefiles,
+ CMfixedpri,
+ CMhang,
+ CMkill,
+ CMnohang,
+ CMnoswap,
+ CMpri,
+ CMprivate,
+ CMprofile,
+ CMstart,
+ CMstartstop,
+ CMstartsyscall,
+ CMstop,
+ CMwaitstop,
+ CMwired,
+ CMtrace,
+ CMinterrupt,
+ CMnointerrupt,
+ /* real time */
+ CMperiod,
+ CMdeadline,
+ CMcost,
+ CMsporadic,
+ CMdeadlinenotes,
+ CMadmit,
+ CMextra,
+ CMexpel,
+ CMevent,
+};
+
+enum{
+ Nevents = 0x4000,
+ Emask = Nevents - 1,
+};
+
+#define STATSIZE (2*28+12+9*12)
+/*
+ * Status, fd, and ns are left fully readable (0444) because of their use in debugging,
+ * particularly on shared servers.
+ * Arguably, ns and fd shouldn't be readable; if you'd prefer, change them to 0000
+ */
+Dirtab procdir[] =
+{
+ "args", {Qargs}, 0, 0660,
+ "ctl", {Qctl}, 0, 0000,
+ "fd", {Qfd}, 0, 0444,
+ "fpregs", {Qfpregs}, sizeof(FPsave), 0000,
+ "kregs", {Qkregs}, sizeof(Ureg), 0400,
+ "mem", {Qmem}, 0, 0000,
+ "note", {Qnote}, 0, 0000,
+ "noteid", {Qnoteid}, 0, 0664,
+ "notepg", {Qnotepg}, 0, 0000,
+ "ns", {Qns}, 0, 0444,
+ "ppid", {Qppid}, 0, 0444,
+ "proc", {Qproc}, 0, 0400,
+ "regs", {Qregs}, sizeof(Ureg), 0000,
+ "segment", {Qsegment}, 0, 0444,
+ "status", {Qstatus}, STATSIZE, 0444,
+ "text", {Qtext}, 0, 0000,
+ "wait", {Qwait}, 0, 0400,
+ "profile", {Qprofile}, 0, 0400,
+ "syscall", {Qsyscall}, 0, 0400,
+ "watchpt", {Qwatchpt}, 0, 0600,
+};
+
+static
+Cmdtab proccmd[] = {
+ CMclose, "close", 2,
+ CMclosefiles, "closefiles", 1,
+ CMfixedpri, "fixedpri", 2,
+ CMhang, "hang", 1,
+ CMnohang, "nohang", 1,
+ CMnoswap, "noswap", 1,
+ CMkill, "kill", 1,
+ CMpri, "pri", 2,
+ CMprivate, "private", 1,
+ CMprofile, "profile", 1,
+ CMstart, "start", 1,
+ CMstartstop, "startstop", 1,
+ CMstartsyscall, "startsyscall", 1,
+ CMstop, "stop", 1,
+ CMwaitstop, "waitstop", 1,
+ CMwired, "wired", 2,
+ CMtrace, "trace", 0,
+ CMinterrupt, "interrupt", 1,
+ CMnointerrupt, "nointerrupt", 1,
+ CMperiod, "period", 2,
+ CMdeadline, "deadline", 2,
+ CMcost, "cost", 2,
+ CMsporadic, "sporadic", 1,
+ CMdeadlinenotes, "deadlinenotes", 1,
+ CMadmit, "admit", 1,
+ CMextra, "extra", 1,
+ CMexpel, "expel", 1,
+ CMevent, "event", 1,
+};
+
+/* Segment type from portdat.h */
+static char *sname[]={ "Text", "Data", "Bss", "Stack", "Shared", "Phys", "Fixed", "Sticky" };
+
+/*
+ * Qids are, in path:
+ * 4 bits of file type (qids above)
+ * 23 bits of process slot number (procindex()) + 1
+ * in vers,
+ * 32 bits of pid, for consistency checking
+ */
+#define QSHIFT 5 /* location in qid of proc slot # */
+
+#define QID(q) ((((ulong)(q).path)&0x0000001F)>>0)
+#define SLOT(q) (((((ulong)(q).path)&0x07FFFFFE0)>>QSHIFT)-1)
+#define PID(q) ((q).vers)
+#define NOTEID(q) ((q).vers)
+
+static void procctlreq(Proc*, char*, int);
+static long procctlmemio(Chan*, Proc*, uintptr, void*, long, int);
+static Chan* proctext(Chan*, Proc*);
+static int procstopped(void*);
+
+static Traceevent *tevents;
+static Lock tlock;
+static int topens;
+static int tproduced, tconsumed;
+void (*proctrace)(Proc*, int, vlong);
+
+static int lenwatchpt(Proc *);
+
+static int
+procgen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp)
+{
+ Qid qid;
+ Proc *p;
+ char *ename;
+/* Segment *q;*/
+ ulong pid, path, perm, len;
+
+ if(s == DEVDOTDOT){
+ mkqid(&qid, Qdir, 0, QTDIR);
+ devdir(c, qid, "#p", 0, eve, 0555, dp);
+ return 1;
+ }
+
+ if(c->qid.path == Qdir){
+ if(s == 0){
+ strcpy(up->genbuf, "trace");
+ mkqid(&qid, Qtrace, -1, QTFILE);
+ devdir(c, qid, up->genbuf, 0, eve, 0400, dp);
+ return 1;
+ }
+
+ if(name != nil){
+ /* ignore s and use name to find pid */
+ pid = strtol(name, &ename, 10);
+ if(pid==0 || ename[0]!='\0')
+ return -1;
+ s = procindex(pid);
+ if(s < 0)
+ return -1;
+ }
+ else if(--s >= conf.nproc)
+ return -1;
+
+ p = proctab(s);
+ pid = p->pid;
+ if(pid == 0)
+ return 0;
+ /*
+ * String comparison is done in devwalk so name must match its formatted pid
+ */
+ snprint(up->genbuf, sizeof(up->genbuf), "%lud", pid);
+ if(name != nil && strcmp(name, up->genbuf) != 0)
+ return -1;
+ mkqid(&qid, (s+1)<<QSHIFT, pid, QTDIR);
+ devdir(c, qid, up->genbuf, 0, p->env->user, 0555, dp);
+ return 1;
+ }
+ if(c->qid.path == Qtrace){
+ strcpy(up->genbuf, "trace");
+ mkqid(&qid, Qtrace, -1, QTFILE);
+ devdir(c, qid, up->genbuf, 0, eve, 0400, dp);
+ return 1;
+ }
+ if(s >= nelem(procdir))
+ return -1;
+ if(tab)
+ panic("procgen");
+
+ tab = &procdir[s];
+ path = c->qid.path&~(((1<<QSHIFT)-1)); /* slot component */
+
+ /* p->procmode determines default mode for files in /proc */
+ p = proctab(SLOT(c->qid));
+/* perm = tab->perm;
+ if(perm == 0)
+ perm = p->procmode;
+ else*/ /* just copy read bits */
+/* perm |= p->procmode & 0444;*/
+ perm = tab->perm;
+
+ len = tab->length;
+/* switch(QID(c->qid)) {
+ case Qwait:
+ len = p->nwait; *//* incorrect size, but >0 means there's something to read *//*
+ break;
+ case Qprofile:
+ q = p->seg[TSEG];
+ if(q != nil && q->profile != nil) {
+ len = (q->top-q->base)>>LRESPROF;
+ len *= sizeof(*q->profile);
+ }
+ break;
+ case Qwatchpt:
+ len = lenwatchpt(p);
+ break;
+ }*/
+
+ mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
+ devdir(c, qid, tab->name, len, p->env->user, perm, dp);
+ return 1;
+}
+
+static void
+_proctrace(Proc* p, Tevent etype, vlong ts)
+{
+ Traceevent *te;
+
+ if (p->trace == 0 || topens == 0 ||
+ tproduced - tconsumed >= Nevents)
+ return;
+
+ te = &tevents[tproduced&Emask];
+ te->pid = p->pid;
+ te->etype = etype;
+ if (ts == 0)
+ te->time = todget(nil);
+ else
+ te->time = ts;
+ tproduced++;
+}
+
+static void
+dprocinit(void)
+{
+ if(conf.nproc >= (1<<(16-QSHIFT))-1)
+ print("warning: too many procs for devproc\n");
+}
+
+static Chan*
+procattach(char *spec)
+{
+ return devattach('p', spec);
+}
+
+static Walkqid*
+procwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, 0, 0, procgen);
+}
+
+static int
+procstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, 0, 0, procgen);
+}
+
+/*
+ * none can't read or write state on other
+ * processes. This is to contain access of
+ * servers running as none should they be
+ * subverted by, for example, a stack attack.
+ */
+static void
+nonone(Proc *p)
+{
+ if(p == up)
+ return;
+ if(strcmp(up->env->user, "none") != 0)
+ return;
+ if(iseve())
+ return;
+ error(Eperm);
+}
+
+static void
+changenoteid(Proc *p, ulong noteid)
+{
+ Proc *pp;
+ int i;
+
+ if(noteid <= 0)
+ error(Ebadarg);
+ if(noteid == p->noteid)
+ return;
+ if(noteid == p->pid){
+ setnoteid(p, noteid);
+ return;
+ }
+ for(i = 0; i < conf.nproc; i++){
+ pp = proctab(i);
+ if(pp->noteid != noteid || pp->kp)
+ continue;
+ if(strcmp(pp->env->user, p->env->user) == 0){
+ nonone(pp);
+ setnoteid(p, noteid);
+ return;
+ }
+ }
+ error(Eperm);
+}
+
+static void
+postnotepg(ulong noteid, char *n, int flag)
+{
+ Proc *p;
+ int i;
+
+ for(i = 0; i < conf.nproc; i++){
+ p = proctab(i);
+ if(p == up)
+ continue;
+ if(p->noteid != noteid || p->kp)
+ continue;
+ qlock(&p->debug);
+ if(p->noteid == noteid)
+ postnote(p, 0, n, flag);
+ qunlock(&p->debug);
+ }
+}
+
+static void clearwatchpt(Proc *p);
+
+static Chan*
+procopen(Chan *c, u32 omode0)
+{
+ Proc *p;
+ Chan *tc;
+ int pid;
+ int omode;
+
+ if(c->qid.type & QTDIR)
+ return devopen(c, omode0, 0, 0, procgen);
+
+ if(QID(c->qid) == Qtrace){
+ if (omode0 != OREAD || !iseve())
+ error(Eperm);
+ lock(&tlock);
+ if (waserror()){
+ topens--;
+ unlock(&tlock);
+ nexterror();
+ }
+ if (topens++ > 0)
+ error("already open");
+ if (tevents == nil){
+ tevents = (Traceevent*)malloc(sizeof(Traceevent) * Nevents);
+ if(tevents == nil)
+ error(Enomem);
+ tproduced = tconsumed = 0;
+ }
+ proctrace = _proctrace;
+ unlock(&tlock);
+ poperror();
+
+ c->mode = openmode(omode0);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+ }
+
+ p = proctab(SLOT(c->qid));
+ eqlock(&p->debug);
+ if(waserror()){
+ qunlock(&p->debug);
+ nexterror();
+ }
+ pid = PID(c->qid);
+ if(p->pid != pid)
+ error(Eprocdied);
+
+ omode = openmode(omode0);
+
+ switch(QID(c->qid)){
+ case Qtext:
+ if(omode != OREAD)
+ error(Eperm);
+ nonone(p);
+ qunlock(&p->debug);
+ poperror();
+ tc = proctext(c, p);
+ tc->offset = 0;
+ cclose(c);
+ return tc;
+
+ case Qstatus:
+ case Qppid:
+ case Qproc:
+ case Qkregs:
+ case Qsegment:
+ case Qns:
+ case Qfd:
+ if(omode != OREAD)
+ error(Eperm);
+ break;
+
+ case Qctl:
+ case Qargs:
+ case Qwait:
+ case Qnoteid:
+ if(omode == OREAD)
+ break;
+ case Qnote:
+ if(p->kp)
+ error(Eperm);
+ break;
+
+ case Qnotepg:
+ if(p->kp || omode != OWRITE)
+ error(Eperm);
+ pid = p->noteid;
+ break;
+
+ case Qmem:
+ case Qregs:
+ case Qfpregs:
+ case Qprofile:
+ case Qsyscall:
+ case Qwatchpt:
+ if(p->kp || p->privatemem)
+ error(Eperm);
+ break;
+
+ default:
+ print("procopen %#lux\n", QID(c->qid));
+ error(Egreg);
+ }
+ nonone(p);
+
+ /* Affix pid to qid */
+ if(pid == 0)
+ error(Eprocdied);
+ c->qid.vers = pid;
+
+ tc = devopen(c, omode, 0, 0, procgen);
+ if(waserror()){
+ cclose(tc);
+ nexterror();
+ }
+
+ switch(QID(c->qid)){
+ case Qwatchpt:
+ if((omode0 & OTRUNC) != 0)
+ /* clearwatchpt(p); */
+ break;
+ }
+
+ poperror();
+ qunlock(&p->debug);
+ poperror();
+
+ return tc;
+}
+
+static int
+procwstat(Chan *c, uchar *db, int n)
+{
+ Dir *d;
+ Proc *p;
+
+ if(c->qid.type&QTDIR)
+ error(Eperm);
+
+ switch(QID(c->qid)){
+ case Qnotepg:
+ case Qtrace:
+ return devwstat(c, db, n);
+ }
+
+ d = smalloc(sizeof(Dir)+n);
+ if(waserror()){
+ free(d);
+ nexterror();
+ }
+ n = convM2D(db, n, &d[0], (char*)&d[1]);
+ if(n == 0)
+ error(Eshortstat);
+
+ p = proctab(SLOT(c->qid));
+ eqlock(&p->debug);
+ if(waserror()){
+ qunlock(&p->debug);
+ nexterror();
+ }
+
+ if(p->pid != PID(c->qid))
+ error(Eprocdied);
+
+ nonone(p);
+ if(strcmp(up->env->user, p->env->user) != 0 && !iseve())
+ error(Eperm);
+
+ if(!emptystr(d->uid) && strcmp(d->uid, p->env->user) != 0){
+ if(strcmp(d->uid, "none") != 0 && !iseve())
+ error(Eperm);
+ kstrdup(&p->env->user, d->uid);
+ }
+ /* p->procmode determines default mode for files in /proc */
+ if(d->mode != ~0UL)
+ p->procmode = d->mode&0777;
+
+ qunlock(&p->debug);
+ poperror();
+ poperror();
+ free(d);
+ return n;
+}
+
+static void
+procclose(Chan *c)
+{
+/* Segio *sio;
+
+ if((c->flag & COPEN) == 0)
+ return;
+
+ switch(QID(c->qid)){
+ case Qtrace:
+ lock(&tlock);
+ if(topens > 0)
+ topens--;
+ if(topens == 0)
+ proctrace = nil;
+ unlock(&tlock);
+ return;
+ case Qmem:
+ sio = c->aux;
+ if(sio != nil){
+ c->aux = nil;
+ segio(sio, nil, nil, 0, 0, 0);
+ free(sio);
+ }
+ return;
+ }*/
+}
+
+static int
+procargs(Proc *p, char *buf, int nbuf)
+{
+/* int j, k, m;
+ char *a;
+ int n;
+
+ a = p->args;
+ if(p->setargs)
+ return snprint(buf, nbuf, "%s [%s]", p->text, p->args);
+ n = p->nargs;
+ for(j = 0; j < nbuf - 1; j += m){
+ if(n <= 0)
+ break;
+ if(j != 0)
+ buf[j++] = ' ';
+ m = snprint(buf+j, nbuf-j, "%q", a);
+ k = strlen(a) + 1;
+ a += k;
+ n -= k;
+ }
+ return j; */
+ return 0;
+}
+
+static int
+eventsavailable(void *)
+{
+ return tproduced > tconsumed;
+}
+
+static int
+prochaswaitq(void *x)
+{
+ Chan *c;
+ Proc *p;
+
+ c = (Chan *)x;
+ p = proctab(SLOT(c->qid));
+/* return p->pid != PID(c->qid) || p->waitq != nil; */
+ return 0;
+}
+
+static void
+int2flag(int flag, char *s)
+{
+ if(flag == 0){
+ *s = '\0';
+ return;
+ }
+ *s++ = '-';
+ if(flag & MAFTER)
+ *s++ = 'a';
+ if(flag & MBEFORE)
+ *s++ = 'b';
+ if(flag & MCREATE)
+ *s++ = 'c';
+ if(flag & MCACHE)
+ *s++ = 'C';
+ *s = '\0';
+}
+
+static int
+readns1(Chan *c, Proc *p, char *buf, int nbuf)
+{
+/* Pgrp *pg;
+ Mount *t, *cm;
+ Mhead *f, *mh;
+ ulong minid, bestmid;
+ char flag[10], *srv;
+ int i;
+
+ pg = p->env->pgrp;
+ if(pg == nil || p->dot == nil || p->pid != PID(c->qid))
+ error(Eprocdied);
+
+ bestmid = ~0;
+ minid = c->nrock;
+ if(minid == bestmid)
+ return 0;
+
+ rlock(&pg->ns);
+
+ mh = nil;
+ cm = nil;
+ for(i = 0; i < MNTHASH; i++) {
+ for(f = pg->mnthash[i]; f != nil; f = f->hash) {
+ rlock(&f->lock);
+ for(t = f->mount; t != nil; t = t->next) {
+ if(t->mountid >= minid && t->mountid < bestmid) {
+ bestmid = t->mountid;
+ cm = t;
+ mh = f;
+ }
+ }
+ runlock(&f->lock);
+ }
+ }
+
+ if(bestmid == ~0) {
+ c->nrock = bestmid;
+ i = snprint(buf, nbuf, "cd %q\n", p->dot->path->s);
+ } else {
+ c->nrock = bestmid+1;
+
+ int2flag(cm->mflag, flag);
+ if(strcmp(cm->to->path->s, "#M") == 0){
+ srv = srvname(cm->to->mchan);
+ i = snprint(buf, nbuf, (cm->spec && *cm->spec)?
+ "mount %s %q %q %q\n": "mount %s %q %q\n", flag,
+ srv? srv: cm->to->mchan->path->s, mh->from->path->s, cm->spec);
+ free(srv);
+ }else{
+ i = snprint(buf, nbuf, "bind %s %q %q\n", flag,
+ cm->to->path->s, mh->from->path->s);
+ }
+ }
+
+ runlock(&pg->ns);
+
+ return i;
+*/
+ return 0;
+}
+
+int
+procfdprint(Chan *c, int fd, char *s, int ns)
+{
+/* return snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %lud %.2ux) %5ld %8lld %s\n",
+ fd,
+ &"r w rw"[(c->mode&3)<<1],
+ devtab[c->type]->dc, c->dev,
+ c->qid.path, c->qid.vers, c->qid.type,
+ c->iounit, c->offset, c->path->s);
+*/
+ return 0;
+}
+
+static int
+readfd1(Chan *c, Proc *p, char *buf, int nbuf)
+{
+/* Fgrp *fg;
+ int n, i;
+
+ fg = p->fgrp;
+ if(fg == nil || p->dot == nil || p->pid != PID(c->qid))
+ return 0;
+
+ if(c->nrock == 0){
+ c->nrock = 1;
+ return snprint(buf, nbuf, "%s\n", p->dot->path->s);
+ }
+
+ lock(fg);
+ n = 0;
+ for(;;){
+ i = c->nrock-1;
+ if(i < 0 || i > fg->maxfd)
+ break;
+ c->nrock++;
+ if(fg->fd[i] != nil){
+ n = procfdprint(fg->fd[i], i, buf, nbuf);
+ break;
+ }
+ }
+ unlock(fg);
+
+ return n; */
+ return 0;
+}
+
+/*
+ * setupwatchpts(Proc *p, Watchpt *wp, int nwp) is defined for all arches separately.
+ * It tests whether wp is a valid set of watchpoints and errors out otherwise.
+ * If and only if they are valid, it sets up all watchpoints (clearing any preexisting ones).
+ * This is to make sure that failed writes to watchpt don't touch the existing watchpoints.
+ */
+/*
+static void
+clearwatchpt(Proc *p)
+{
+ setupwatchpts(p, nil, 0);
+ free(p->watchpt);
+ p->watchpt = nil;
+ p->nwatchpt = 0;
+}
+
+static int
+lenwatchpt(Proc *pr)
+{
+ \/* careful, not holding debug lock *\/
+ return pr->nwatchpt * (10 + 4 * sizeof(uintptr));
+}
+
+static int
+readwatchpt(Proc *pr, char *buf, int nbuf)
+{
+ char *p, *e;
+ Watchpt *w;
+
+ p = buf;
+ e = buf + nbuf;
+ \/* careful, length has to match lenwatchpt() *\/
+ for(w = pr->watchpt; w < pr->watchpt + pr->nwatchpt; w++)
+ p = seprint(p, e, sizeof(uintptr) == 8 ? "%c%c%c %#.16p %#.16p\n" : "%c%c%c %#.8p %#.8p\n",
+ (w->type & WATCHRD) != 0 ? 'r' : '-',
+ (w->type & WATCHWR) != 0 ? 'w' : '-',
+ (w->type & WATCHEX) != 0 ? 'x' : '-',
+ (void *) w->addr, (void *) w->len);
+ return p - buf;
+}
+
+static int
+writewatchpt(Proc *pr, char *buf, int nbuf, uvlong offset)
+{
+ char *p, *q, *e;
+ char line[256], *f[4];
+ Watchpt *wp, *wq;
+ int rc, nwp, nwp0;
+ uvlong x;
+
+ p = buf;
+ e = buf + nbuf;
+ if(offset != 0)
+ nwp0 = pr->nwatchpt;
+ else
+ nwp0 = 0;
+ nwp = 0;
+ for(q = p; q < e; q++)
+ nwp += *q == '\n';
+ if(nwp > 65536) error(Egreg);
+ wp = malloc((nwp0+nwp) * sizeof(Watchpt));
+ if(wp == nil) error(Enomem);
+ if(waserror()){
+ free(wp);
+ nexterror();
+ }
+ if(nwp0 > 0)
+ memmove(wp, pr->watchpt, sizeof(Watchpt) * nwp0);
+ for(wq = wp + nwp0; wq < wp + nwp0+nwp;){
+ q = memchr(p, '\n', e - p);
+ if(q == nil)
+ break;
+ if(q - p > sizeof(line) - 1)
+ error("line too long");
+ memmove(line, p, q - p);
+ line[q - p] = 0;
+ p = q + 1;
+
+ rc = tokenize(line, f, nelem(f));
+ if(rc == 0) continue;
+ if(rc != 3)
+ error("wrong number of fields");
+ for(q = f[0]; *q != 0; q++)
+ switch(*q){
+ case 'r': if((wq->type & WATCHRD) != 0) goto tinval; wq->type |= WATCHRD; break;
+ case 'w': if((wq->type & WATCHWR) != 0) goto tinval; wq->type |= WATCHWR; break;
+ case 'x': if((wq->type & WATCHEX) != 0) goto tinval; wq->type |= WATCHEX; break;
+ case '-': break;
+ default: tinval: error("invalid type");
+ }
+ x = strtoull(f[1], &q, 0);
+ if(f[1] == q || *q != 0 || x != (uintptr) x) error("invalid address");
+ wq->addr = x;
+ x = strtoull(f[2], &q, 0);
+ if(f[2] == q || *q != 0 || x > (uintptr)-wq->addr) error("invalid length");
+ wq->len = x;
+ if(wq->addr + wq->len > USTKTOP) error("bad address");
+ wq++;
+ }
+ nwp = wq - (wp + nwp0);
+ if(nwp == 0 && nwp0 == pr->nwatchpt){
+ poperror();
+ free(wp);
+ return p - buf;
+ }
+ setupwatchpts(pr, wp, nwp0 + nwp);
+ poperror();
+ free(pr->watchpt);
+ pr->watchpt = wp;
+ pr->nwatchpt = nwp0 + nwp;
+ return p - buf;
+}
+*/
+/*
+ * userspace can't pass negative file offset for a
+ * 64 bit kernel address, so we use 63 bit and sign
+ * extend to 64 bit.
+ */
+static uintptr
+off2addr(vlong off)
+{
+ off <<= 1;
+ off >>= 1;
+ return off;
+}
+
+static s32
+procread(Chan *c, void *va, s32 n, s64 off)
+{
+ char statbuf[1024], *sps;
+ ulong offset;
+ int i, j, rsize;
+ uchar *rptr;
+ uintptr addr;
+/* Segment *s; */
+ Ureg kur;
+/* Waitq *wq; */
+ Proc *p;
+
+ offset = off;
+ if(c->qid.type & QTDIR)
+ return devdirread(c, va, n, 0, 0, procgen);
+
+ if(QID(c->qid) == Qtrace){
+ int navail, ne;
+
+ if(!eventsavailable(nil))
+ return 0;
+
+ rptr = (uchar*)va;
+ navail = tproduced - tconsumed;
+ if(navail > n / sizeof(Traceevent))
+ navail = n / sizeof(Traceevent);
+ while(navail > 0) {
+ ne = ((tconsumed & Emask) + navail > Nevents)?
+ Nevents - (tconsumed & Emask): navail;
+ memmove(rptr, &tevents[tconsumed & Emask],
+ ne * sizeof(Traceevent));
+
+ tconsumed += ne;
+ rptr += ne * sizeof(Traceevent);
+ navail -= ne;
+ }
+ return rptr - (uchar*)va;
+ }
+
+ p = proctab(SLOT(c->qid));
+ if(p->pid != PID(c->qid))
+ error(Eprocdied);
+
+ switch(QID(c->qid)){
+ case Qmem:
+/* addr = off2addr(off);
+ if(addr < KZERO)
+ return procctlmemio(c, p, addr, va, n, 1);
+
+ if(!iseve() || poolisoverlap(secrmem, (uchar*)addr, n))
+ error(Eperm);
+
+ \/* validate kernel addresses *\/
+ if(addr < (uintptr)end) {
+ if(addr+n > (uintptr)end)
+ n = (uintptr)end - addr;
+ memmove(va, (char*)addr, n);
+ return n;
+ }
+ for(i=0; i<nelem(conf.mem); i++){
+ Confmem *cm = &conf.mem[i];
+ \/* klimit-1 because klimit might be zero! *\/
+ if(cm->kbase <= addr && addr <= cm->klimit-1){
+ if(addr+n >= cm->klimit-1)
+ n = cm->klimit - addr;
+ memmove(va, (char*)addr, n);
+ return n;
+ }
+ }*/
+ error(Ebadarg);
+
+ case Qctl:
+ return readnum(offset, va, n, p->pid, NUMSIZE);
+
+ case Qnoteid:
+ return readnum(offset, va, n, p->noteid, NUMSIZE);
+
+ case Qppid:
+ /* return readnum(offset, va, n, p->parentpid, NUMSIZE); */
+ return readnum(offset, va, n, p->pid, NUMSIZE);
+
+ case Qprofile:
+/* s = p->seg[TSEG];
+ if(s == nil || s->profile == nil)
+ error("profile is off");
+ i = (s->top-s->base)>>LRESPROF;
+ i *= sizeof(s->profile[0]);
+ if(i < 0 || offset >= i)
+ return 0;
+ if(offset+n > i)
+ n = i - offset;
+ memmove(va, ((char*)s->profile)+offset, n);
+ return n;*/
+
+ case Qproc:
+ rptr = (uchar*)p;
+ rsize = sizeof(Proc);
+ goto regread;
+
+ case Qregs:
+ rptr = (uchar*)p->dbgreg;
+ rsize = sizeof(Ureg);
+ goto regread;
+
+ case Qkregs:
+/* memset(&kur, 0, sizeof(Ureg));
+ setkernur(&kur, p);
+ rptr = (uchar*)&kur;
+ rsize = sizeof(Ureg);
+ goto regread;*/
+
+ case Qfpregs:
+ if(p->fpstate != FPinactive)
+ error(Enoreg);
+ rptr = (uchar*)p->fpsave;
+ rsize = sizeof(FPsave);
+ regread:
+ if(rptr == nil)
+ error(Enoreg);
+ if(offset >= rsize)
+ return 0;
+ if(offset+n > rsize)
+ n = rsize - offset;
+ memmove(va, rptr+offset, n);
+ return n;
+
+ case Qstatus:
+ sps = p->psstate;
+ if(sps == nil)
+ sps = statename[p->state];
+ j = snprint(statbuf, sizeof(statbuf),
+ "%-27s %-27s %-11s "
+ /* "%11lud %11lud %11lud "
+ "%11lud %11lud %11lud "
+ "%11lud %11lud %11lud\n",*/
+ "%11ud\n",
+ p->text, p->env->user, sps,
+/* tk2ms(p->time[TUser]),
+ tk2ms(p->time[TSys]),
+ tk2ms(MACHP(0)->ticks - p->time[TReal]),
+ tk2ms(p->time[TCUser]),
+ tk2ms(p->time[TCSys]),
+ tk2ms(p->time[TCReal]),
+ (ulong)(procpagecount(p)*BY2PG/1024),
+ p->basepri,*/ p->priority);
+ statbufread:
+ if(offset >= j)
+ return 0;
+ if(offset+n > j)
+ n = j - offset;
+ memmove(va, statbuf+offset, n);
+ return n;
+
+ case Qsegment:
+/* j = 0;
+ for(i = 0; i < NSEG; i++) {
+ s = p->seg[i];
+ if(s == nil)
+ continue;
+ j += snprint(statbuf+j, sizeof(statbuf)-j,
+ "%-6s %c%c %8p %8p %4ld\n",
+ sname[s->type&SG_TYPE],
+ s->type&SG_FAULT ? 'F' : (s->type&SG_RONLY ? 'R' : ' '),
+ s->profile ? 'P' : ' ',
+ s->base, s->top, s->ref);
+ }
+ goto statbufread;
+*/ return 0;
+
+ case Qwait:
+/* if(!canqlock(&p->qwaitr))
+ error(Einuse);
+
+ if(waserror()) {
+ qunlock(&p->qwaitr);
+ nexterror();
+ }
+
+ lock(&p->exl);
+ while(p->waitq == nil && p->pid == PID(c->qid)) {
+ if(up == p && p->nchild == 0) {
+ unlock(&p->exl);
+ error(Enochild);
+ }
+ unlock(&p->exl);
+ sleep(&p->waitr, prochaswaitq, c);
+ lock(&p->exl);
+ }
+ if(p->pid != PID(c->qid)){
+ unlock(&p->exl);
+ error(Eprocdied);
+ }
+ wq = p->waitq;
+ p->waitq = wq->next;
+ p->nwait--;
+ unlock(&p->exl);
+
+ qunlock(&p->qwaitr);
+ poperror();
+
+ j = snprint(statbuf, sizeof(statbuf), "%d %lud %lud %lud %q",
+ wq->w.pid,
+ wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal],
+ wq->w.msg);
+ free(wq);
+ offset = 0;
+ goto statbufread;
+*/ return 0;
+ }
+
+ eqlock(&p->debug);
+ if(waserror()){
+ qunlock(&p->debug);
+ nexterror();
+ }
+ if(p->pid != PID(c->qid))
+ error(Eprocdied);
+
+ switch(QID(c->qid)){
+ case Qns:
+ case Qfd:
+/* if(offset == 0 || offset != c->mrock)
+ c->nrock = c->mrock = 0;
+ do {
+ if(QID(c->qid) == Qns)
+ j = readns1(c, p, statbuf, sizeof(statbuf));
+ else
+ j = readfd1(c, p, statbuf, sizeof(statbuf));
+ if(j == 0)
+ break;
+ c->mrock += j;
+ } while(c->mrock <= offset);
+ i = c->mrock - offset;
+ qunlock(&p->debug);
+ poperror();
+ if(i <= 0 || i > j)
+ return 0;
+ if(i < n)
+ n = i;
+ offset = j - i;
+ goto statbufread;*/
+ qunlock(&p->debug);
+ poperror();
+ return 0;
+
+ case Qargs:
+ j = procargs(p, statbuf, sizeof(statbuf));
+ qunlock(&p->debug);
+ poperror();
+ goto statbufread;
+
+ case Qwatchpt:
+/* j = readwatchpt(p, statbuf, sizeof(statbuf));*/
+ qunlock(&p->debug);
+ poperror();
+ goto statbufread;
+
+ case Qsyscall:
+ /* if(p->syscalltrace != nil)
+ n = readstr(offset, va, n, p->syscalltrace);
+ else*/
+ n = 0;
+ break;
+
+ case Qnote:
+ /* if(n < 1) \/* must accept at least the '\0' *\/
+ error(Etoosmall);
+ if(p->nnote == 0)
+ n = 0;
+ else {
+ i = strlen(p->note[0].msg) + 1;
+ if(i < n)
+ n = i;
+ memmove(va, p->note[0].msg, n-1);
+ ((char*)va)[n-1] = '\0';
+ if(--p->nnote == 0)
+ p->notepending = 0;
+ memmove(p->note, p->note+1, p->nnote*sizeof(Note));
+ }*/
+ break;
+
+ default:
+ print("unknown qid in procwread\n");
+ error(Egreg);
+ }
+
+ qunlock(&p->debug);
+ poperror();
+ return n;
+}
+
+static s32
+procwrite(Chan *c, void *va, s32 n, s64 off)
+{
+ char buf[ERRMAX];
+ ulong offset;
+ Proc *p;
+
+ offset = off;
+ if(c->qid.type & QTDIR)
+ error(Eisdir);
+
+ /* use the remembered noteid in the channel qid */
+ if(QID(c->qid) == Qnotepg) {
+ if(n >= sizeof(buf))
+ error(Etoobig);
+ memmove(buf, va, n);
+ buf[n] = 0;
+ /*postnotepg(NOTEID(c->qid), buf, NUser);*/
+ return n;
+ }
+
+ p = proctab(SLOT(c->qid));
+ eqlock(&p->debug);
+ if(waserror()){
+ qunlock(&p->debug);
+ nexterror();
+ }
+ if(p->pid != PID(c->qid))
+ error(Eprocdied);
+
+ switch(QID(c->qid)){
+ case Qargs:
+/* if(offset != 0 || n >= sizeof(buf))
+ error(Etoobig);
+ memmove(buf, va, n);
+ buf[n] = 0;
+ kstrdup(&p->args, buf);
+ p->nargs = 0;
+ p->setargs = 1;*/
+ break;
+
+ case Qmem:
+ if(p->state != Stopped)
+ error(Ebadctl);
+ n = procctlmemio(c, p, off2addr(off), va, n, 0);
+ break;
+
+ case Qregs:
+/* if(offset >= sizeof(Ureg))
+ n = 0;
+ else if(offset+n > sizeof(Ureg))
+ n = sizeof(Ureg) - offset;
+ if(p->dbgreg == nil)
+ error(Enoreg);
+ setregisters(p->dbgreg, (char*)(p->dbgreg)+offset, va, n);*/
+ break;
+
+ case Qfpregs:
+ if(offset >= sizeof(FPsave))
+ n = 0;
+ else if(offset+n > sizeof(FPsave))
+ n = sizeof(FPsave) - offset;
+ if(p->fpstate != FPinactive || p->fpsave == nil)
+ error(Enoreg);
+ memmove((uchar*)p->fpsave+offset, va, n);
+ break;
+
+ case Qctl:
+ procctlreq(p, va, n);
+ break;
+
+ case Qnote:
+/* if(n >= sizeof(buf))
+ error(Etoobig);
+ memmove(buf, va, n);
+ buf[n] = 0;
+ if(!postnote(p, 0, buf, NUser))
+ error("note not posted");*/
+ break;
+
+ case Qnoteid:
+/* if(offset != 0 || n >= sizeof(buf))
+ error(Etoobig);
+ memmove(buf, va, n);
+ buf[n] = 0;
+ changenoteid(p, atoi(buf));*/
+ break;
+
+ case Qwatchpt:
+/* writewatchpt(p, va, n, off);*/
+ break;
+
+ default:
+ print("unknown qid in procwrite\n");
+ error(Egreg);
+ }
+ poperror();
+ qunlock(&p->debug);
+ return n;
+}
+
+Dev procdevtab = {
+ 'o',
+ "proc",
+
+ devreset,
+ dprocinit,
+ devshutdown,
+ procattach,
+ procwalk,
+ procstat,
+ procopen,
+ devcreate,
+ procclose,
+ procread,
+ devbread,
+ procwrite,
+ devbwrite,
+ devremove,
+ procwstat,
+};
+
+static Chan*
+proctext(Chan *c, Proc *p)
+{
+ Chan *tc;
+/* Image *i;
+ Segment *s;
+
+ eqlock(&p->seglock);
+ if(waserror()){
+ qunlock(&p->seglock);
+ nexterror();
+ }
+ if(p->state == Dead || p->pid != PID(c->qid))
+ error(Eprocdied);
+ if((s = p->seg[TSEG]) == nil)
+ error(Enonexist);
+ if((i = s->image) == nil)
+ error(Enonexist);
+ lock(i);
+ tc = i->c;
+ if(i->notext || tc == nil || (tc->flag&COPEN) == 0 || tc->mode != OREAD){
+ unlock(i);
+ error(Enonexist);
+ }
+ incref(tc);
+ unlock(i);
+ qunlock(&p->seglock);
+ poperror();
+
+ return tc;
+*/ return nil;
+}
+
+static void
+procstopwait(Proc *p, int ctl)
+{
+ char *state;
+ int pid;
+
+/* if(p->pdbg != nil)
+ error(Einuse);
+ if(procstopped(p) || p->state == Broken)
+ return;
+ pid = p->pid;
+ if(pid == 0)
+ error(Eprocdied);
+ if(ctl != 0)
+ p->procctl = ctl;
+ if(p == up)
+ return;
+ p->pdbg = up;
+ qunlock(&p->debug);
+ state = up->psstate;
+ up->psstate = "Stopwait";
+ if(waserror()) {
+ up->psstate = state;
+ qlock(&p->debug);
+ if(p->pdbg == up)
+ p->pdbg = nil;
+ nexterror();
+ }
+ sleep(&up->sleep, procstopped, p);
+ poperror();
+ up->psstate = state;
+ qlock(&p->debug);
+ if(p->pid != pid)
+ error(Eprocdied);
+*/
+}
+
+static void
+procctlclosefiles(Proc *p, int all, int fd)
+{
+ Fgrp *f;
+ Chan *c;
+
+ if(fd < 0)
+ error(Ebadfd);
+ f = p->env->fgrp;
+ if(f == nil)
+ error(Eprocdied);
+
+ incref(f);
+ lock(f);
+ while(fd <= f->maxfd){
+ c = f->fd[fd];
+ if(c != nil){
+ f->fd[fd] = nil;
+ unlock(f);
+ qunlock(&p->debug);
+ cclose(c);
+ qlock(&p->debug);
+ lock(f);
+ }
+ if(!all)
+ break;
+ fd++;
+ }
+ unlock(f);
+ closefgrp(f);
+}
+
+static char *
+parsetime(vlong *rt, char *s)
+{
+ uvlong ticks;
+ ulong l;
+ char *e, *p;
+ static int p10[] = {100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};
+
+ if (s == nil)
+ return("missing value");
+ ticks=strtoul(s, &e, 10);
+ if (*e == '.'){
+ p = e+1;
+ l = strtoul(p, &e, 10);
+ if(e-p > nelem(p10))
+ return "too many digits after decimal point";
+ if(e-p == 0)
+ return "ill-formed number";
+ l *= p10[e-p-1];
+ }else
+ l = 0;
+ if (*e == '\0' || strcmp(e, "s") == 0){
+ ticks = 1000000000 * ticks + l;
+ }else if (strcmp(e, "ms") == 0){
+ ticks = 1000000 * ticks + l/1000;
+ }else if (strcmp(e, "µs") == 0 || strcmp(e, "us") == 0){
+ ticks = 1000 * ticks + l/1000000;
+ }else if (strcmp(e, "ns") != 0)
+ return "unrecognized unit";
+ *rt = ticks;
+ return nil;
+}
+
+static void
+procctlreq(Proc *p, char *va, int n)
+{
+/* Segment *s;
+ uintptr npc;
+ int pri;
+ Cmdbuf *cb;
+ Cmdtab *ct;
+ vlong time;
+ char *e;
+ void (*pt)(Proc*, int, vlong);
+
+ cb = parsecmd(va, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+
+ ct = lookupcmd(cb, proccmd, nelem(proccmd));
+
+ switch(ct->index){
+ case CMclose:
+ procctlclosefiles(p, 0, atoi(cb->f[1]));
+ break;
+ case CMclosefiles:
+ procctlclosefiles(p, 1, 0);
+ break;
+ case CMhang:
+ p->hang = 1;
+ break;
+ case CMkill:
+ switch(p->state) {
+ case Broken:
+ unbreak(p);
+ break;
+ case Stopped:
+ p->procctl = Proc_exitme;
+ postnote(p, 0, "sys: killed", NExit);
+ ready(p);
+ break;
+ default:
+ p->procctl = Proc_exitme;
+ postnote(p, 0, "sys: killed", NExit);
+ }
+ break;
+ case CMnohang:
+ p->hang = 0;
+ break;
+ case CMnoswap:
+ p->noswap = 1;
+ break;
+ case CMpri:
+ pri = atoi(cb->f[1]);
+ if(pri > PriNormal && !iseve())
+ error(Eperm);
+ procpriority(p, pri, 0);
+ break;
+ case CMfixedpri:
+ pri = atoi(cb->f[1]);
+ if(pri > PriNormal && !iseve())
+ error(Eperm);
+ procpriority(p, pri, 1);
+ break;
+ case CMprivate:
+ p->privatemem = 1;
+ break;
+ case CMprofile:
+ s = p->seg[TSEG];
+ if(s == nil || (s->type&SG_TYPE) != SG_TEXT) \/* won't expand *\/
+ error(Egreg);
+ eqlock(s);
+ npc = (s->top-s->base)>>LRESPROF;
+ if(s->profile == nil){
+ s->profile = malloc(npc*sizeof(*s->profile));
+ if(s->profile == nil){
+ qunlock(s);
+ error(Enomem);
+ }
+ } else {
+ memset(s->profile, 0, npc*sizeof(*s->profile));
+ }
+ qunlock(s);
+ break;
+ case CMstart:
+ if(p->state != Stopped)
+ error(Ebadctl);
+ ready(p);
+ break;
+ case CMstartstop:
+ if(p->state != Stopped)
+ error(Ebadctl);
+ p->procctl = Proc_traceme;
+ ready(p);
+ procstopwait(p, Proc_traceme);
+ break;
+ case CMstartsyscall:
+ if(p->state != Stopped)
+ error(Ebadctl);
+ p->procctl = Proc_tracesyscall;
+ ready(p);
+ procstopwait(p, Proc_tracesyscall);
+ break;
+ case CMstop:
+ procstopwait(p, Proc_stopme);
+ break;
+ case CMwaitstop:
+ procstopwait(p, 0);
+ break;
+ case CMwired:
+ procwired(p, atoi(cb->f[1]));
+ break;
+ case CMtrace:
+ switch(cb->nf){
+ case 1:
+ p->trace ^= 1;
+ break;
+ case 2:
+ p->trace = (atoi(cb->f[1]) != 0);
+ break;
+ default:
+ error("args");
+ }
+ break;
+ case CMinterrupt:
+ postnote(p, 0, nil, NUser);
+ break;
+ case CMnointerrupt:
+ if(p->nnote == 0)
+ p->notepending = 0;
+ else
+ error("notes pending");
+ break;
+ \/* real time *\/
+ case CMperiod:
+ if(p->edf == nil)
+ edfinit(p);
+ if(e=parsetime(&time, cb->f[1])) \/* time in ns *\/
+ error(e);
+ edfstop(p);
+ p->edf->T = time/1000; \/* Edf times are in µs *\/
+ break;
+ case CMdeadline:
+ if(p->edf == nil)
+ edfinit(p);
+ if(e=parsetime(&time, cb->f[1]))
+ error(e);
+ edfstop(p);
+ p->edf->D = time/1000;
+ break;
+ case CMcost:
+ if(p->edf == nil)
+ edfinit(p);
+ if(e=parsetime(&time, cb->f[1]))
+ error(e);
+ edfstop(p);
+ p->edf->C = time/1000;
+ break;
+ case CMsporadic:
+ if(p->edf == nil)
+ edfinit(p);
+ p->edf->flags |= Sporadic;
+ break;
+ case CMdeadlinenotes:
+ if(p->edf == nil)
+ edfinit(p);
+ p->edf->flags |= Sendnotes;
+ break;
+ case CMadmit:
+ if(p->edf == nil)
+ error("edf params");
+ if(e = edfadmit(p))
+ error(e);
+ break;
+ case CMextra:
+ if(p->edf == nil)
+ edfinit(p);
+ p->edf->flags |= Extratime;
+ break;
+ case CMexpel:
+ if(p->edf != nil)
+ edfstop(p);
+ break;
+ case CMevent:
+ pt = proctrace;
+ if(up->trace && pt != nil)
+ pt(up, SUser, 0);
+ break;
+ }
+
+ poperror();
+ free(cb);*/
+}
+
+static int
+procstopped(void *a)
+{
+ return ((Proc*)a)->state == Stopped;
+}
+
+static long
+procctlmemio(Chan *c, Proc *p, uintptr offset, void *a, long n, int read)
+{
+/* Segio *sio;
+ Segment *s;
+ int i;
+
+ s = seg(p, offset, 0);
+ if(s == nil)
+ error(Ebadarg);
+ eqlock(&p->seglock);
+ if(waserror()) {
+ qunlock(&p->seglock);
+ nexterror();
+ }
+ if(p->state == Dead || p->pid != PID(c->qid))
+ error(Eprocdied);
+
+ for(i = 0; i < NSEG; i++) {
+ if(p->seg[i] == s)
+ break;
+ }
+ if(i == NSEG)
+ error(Egreg); \/* segment gone *\/
+
+ eqlock(s);
+ if(waserror()){
+ qunlock(s);
+ nexterror();
+ }
+ if(!read && (s->type&SG_TYPE) == SG_TEXT) {
+ s = txt2data(s);
+ p->seg[i] = s;
+ }
+ offset -= s->base;
+ incref(s); \/* for us while we copy *\/
+ qunlock(s);
+ poperror();
+
+ sio = c->aux;
+ if(sio == nil){
+ sio = smalloc(sizeof(Segio));
+ c->aux = sio;
+ }
+
+ qunlock(&p->seglock);
+ poperror();
+
+ if(waserror()) {
+ putseg(s);
+ nexterror();
+ }
+ n = segio(sio, s, a, n, offset, read);
+ putseg(s);
+ poperror();
+
+ if(!read)
+ p->newtlb = 1;
+
+ return n;*/
+ return 0;
+}