summaryrefslogtreecommitdiff
path: root/sys/src/9/port/devproc.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/port/devproc.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/port/devproc.c')
-rwxr-xr-xsys/src/9/port/devproc.c1592
1 files changed, 1592 insertions, 0 deletions
diff --git a/sys/src/9/port/devproc.c b/sys/src/9/port/devproc.c
new file mode 100755
index 000000000..d1ee87d5f
--- /dev/null
+++ b/sys/src/9/port/devproc.c
@@ -0,0 +1,1592 @@
+#include "u.h"
+#include <trace.h>
+#include "tos.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,
+ Qproc,
+ Qregs,
+ Qsegment,
+ Qstatus,
+ Qtext,
+ Qwait,
+ Qprofile,
+ Qsyscall,
+};
+
+enum
+{
+ CMclose,
+ CMclosefiles,
+ CMfixedpri,
+ CMhang,
+ CMkill,
+ CMnohang,
+ CMnoswap,
+ CMpri,
+ CMprivate,
+ CMprofile,
+ CMstart,
+ CMstartstop,
+ CMstartsyscall,
+ CMstop,
+ CMwaitstop,
+ CMwired,
+ CMtrace,
+ /* real time */
+ CMperiod,
+ CMdeadline,
+ CMcost,
+ CMsporadic,
+ CMdeadlinenotes,
+ CMadmit,
+ CMextra,
+ CMexpel,
+ CMevent,
+};
+
+enum{
+ Nevents = 0x4000,
+ Emask = Nevents - 1,
+};
+
+#define STATSIZE (2*KNAMELEN+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,
+ "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,
+};
+
+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,
+ 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", };
+
+/*
+ * Qids are, in path:
+ * 4 bits of file type (qids above)
+ * 23 bits of process slot number + 1
+ * in vers,
+ * 32 bits of pid, for consistency checking
+ * If notepg, c->pgrpid.path is pgrp slot, .vers is noteid.
+ */
+#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)
+
+void procctlreq(Proc*, char*, int);
+int procctlmemio(Proc*, ulong, int, void*, int);
+Chan* proctext(Chan*, Proc*);
+Segment* txt2data(Proc*, Segment*);
+int procstopped(void*);
+void mntscan(Mntwalk*, Proc*);
+
+static Traceevent *tevents;
+static Lock tlock;
+static int topens;
+static int tproduced, tconsumed;
+void (*proctrace)(Proc*, int, vlong);
+
+extern int unfair;
+
+static void
+profclock(Ureg *ur, Timer *)
+{
+ Tos *tos;
+
+ if(up == 0 || up->state != Running)
+ return;
+
+ /* user profiling clock */
+ if(userureg(ur)){
+ tos = (Tos*)(USTKTOP-sizeof(Tos));
+ tos->clock += TK2MS(1);
+ segclock(ur->pc);
+ }
+}
+
+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, 0444, 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;
+ sprint(up->genbuf, "%lud", pid);
+ /*
+ * String comparison is done in devwalk so name must match its formatted 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->user, DMDIR|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, 0444, 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;
+
+ 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 && q->profile) {
+ len = (q->top-q->base)>>LRESPROF;
+ len *= sizeof(*q->profile);
+ }
+ break;
+ }
+
+ mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
+ devdir(c, qid, tab->name, len, p->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
+procinit(void)
+{
+ if(conf.nproc >= (1<<(16-QSHIFT))-1)
+ print("warning: too many procs for devproc\n");
+ addclock0link((void (*)(void))profclock, 113); /* Relative prime to HZ */
+}
+
+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->user, "none") != 0)
+ return;
+ if(iseve())
+ return;
+ error(Eperm);
+}
+
+static Chan*
+procopen(Chan *c, int omode)
+{
+ Proc *p;
+ Pgrp *pg;
+ Chan *tc;
+ int pid;
+
+ if(c->qid.type & QTDIR)
+ return devopen(c, omode, 0, 0, procgen);
+
+ if(QID(c->qid) == Qtrace){
+ if (omode != OREAD)
+ error(Eperm);
+ lock(&tlock);
+ if (waserror()){
+ unlock(&tlock);
+ nexterror();
+ }
+ if (topens > 0)
+ error("already open");
+ topens++;
+ 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(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+ }
+
+ p = proctab(SLOT(c->qid));
+ qlock(&p->debug);
+ if(waserror()){
+ qunlock(&p->debug);
+ nexterror();
+ }
+ pid = PID(c->qid);
+ if(p->pid != pid)
+ error(Eprocdied);
+
+ omode = openmode(omode);
+
+ switch(QID(c->qid)){
+ case Qtext:
+ if(omode != OREAD)
+ error(Eperm);
+ tc = proctext(c, p);
+ tc->offset = 0;
+ qunlock(&p->debug);
+ poperror();
+ return tc;
+
+ case Qproc:
+ case Qkregs:
+ case Qsegment:
+ case Qprofile:
+ case Qfd:
+ if(omode != OREAD)
+ error(Eperm);
+ break;
+
+ case Qnote:
+ if(p->privatemem)
+ error(Eperm);
+ break;
+
+ case Qmem:
+ case Qctl:
+ if(p->privatemem)
+ error(Eperm);
+ nonone(p);
+ break;
+
+ case Qargs:
+ case Qnoteid:
+ case Qstatus:
+ case Qwait:
+ case Qregs:
+ case Qfpregs:
+ case Qsyscall:
+ nonone(p);
+ break;
+
+ case Qns:
+ if(omode != OREAD)
+ error(Eperm);
+ c->aux = malloc(sizeof(Mntwalk));
+ break;
+
+ case Qnotepg:
+ nonone(p);
+ pg = p->pgrp;
+ if(pg == nil)
+ error(Eprocdied);
+ if(omode!=OWRITE || pg->pgrpid == 1)
+ error(Eperm);
+ c->pgrpid.path = pg->pgrpid+1;
+ c->pgrpid.vers = p->noteid;
+ break;
+
+ default:
+ pprint("procopen %#lux\n", QID(c->qid));
+ error(Egreg);
+ }
+
+ /* Affix pid to qid */
+ if(p->state != Dead)
+ c->qid.vers = p->pid;
+
+ /* make sure the process slot didn't get reallocated while we were playing */
+ coherence();
+ if(p->pid != pid)
+ error(Eprocdied);
+
+ tc = devopen(c, omode, 0, 0, procgen);
+ qunlock(&p->debug);
+ poperror();
+
+ return tc;
+}
+
+static int
+procwstat(Chan *c, uchar *db, int n)
+{
+ Proc *p;
+ Dir *d;
+
+ if(c->qid.type&QTDIR)
+ error(Eperm);
+
+ if(QID(c->qid) == Qtrace)
+ return devwstat(c, db, n);
+
+ p = proctab(SLOT(c->qid));
+ nonone(p);
+ d = nil;
+ if(waserror()){
+ free(d);
+ qunlock(&p->debug);
+ nexterror();
+ }
+ qlock(&p->debug);
+
+ if(p->pid != PID(c->qid))
+ error(Eprocdied);
+
+ if(strcmp(up->user, p->user) != 0 && strcmp(up->user, eve) != 0)
+ error(Eperm);
+
+ d = smalloc(sizeof(Dir)+n);
+ n = convM2D(db, n, &d[0], (char*)&d[1]);
+ if(n == 0)
+ error(Eshortstat);
+ if(!emptystr(d->uid) && strcmp(d->uid, p->user) != 0){
+ if(strcmp(up->user, eve) != 0)
+ error(Eperm);
+ else
+ kstrdup(&p->user, d->uid);
+ }
+ /* p->procmode determines default mode for files in /proc */
+ if(d->mode != ~0UL)
+ p->procmode = d->mode&0777;
+
+ poperror();
+ free(d);
+ qunlock(&p->debug);
+ return n;
+}
+
+
+static long
+procoffset(long offset, char *va, int *np)
+{
+ if(offset > 0) {
+ offset -= *np;
+ if(offset < 0) {
+ memmove(va, va+*np+offset, -offset);
+ *np = -offset;
+ }
+ else
+ *np = 0;
+ }
+ return offset;
+}
+
+static int
+procqidwidth(Chan *c)
+{
+ char buf[32];
+
+ return sprint(buf, "%lud", c->qid.vers);
+}
+
+int
+procfdprint(Chan *c, int fd, int w, char *s, int ns)
+{
+ int n;
+
+ if(w == 0)
+ w = procqidwidth(c);
+ n = 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, w, c->qid.vers, c->qid.type,
+ c->iounit, c->offset, c->path->s);
+ return n;
+}
+
+static int
+procfds(Proc *p, char *va, int count, long offset)
+{
+ Fgrp *f;
+ Chan *c;
+ char buf[256];
+ int n, i, w, ww;
+ char *a;
+
+ /* print to buf to avoid holding fgrp lock while writing to user space */
+ if(count > sizeof buf)
+ count = sizeof buf;
+ a = buf;
+
+ qlock(&p->debug);
+ f = p->fgrp;
+ if(f == nil){
+ qunlock(&p->debug);
+ return 0;
+ }
+ lock(f);
+ if(waserror()){
+ unlock(f);
+ qunlock(&p->debug);
+ nexterror();
+ }
+
+ n = readstr(0, a, count, p->dot->path->s);
+ n += snprint(a+n, count-n, "\n");
+ offset = procoffset(offset, a, &n);
+ /* compute width of qid.path */
+ w = 0;
+ for(i = 0; i <= f->maxfd; i++) {
+ c = f->fd[i];
+ if(c == nil)
+ continue;
+ ww = procqidwidth(c);
+ if(ww > w)
+ w = ww;
+ }
+ for(i = 0; i <= f->maxfd; i++) {
+ c = f->fd[i];
+ if(c == nil)
+ continue;
+ n += procfdprint(c, i, w, a+n, count-n);
+ offset = procoffset(offset, a, &n);
+ }
+ unlock(f);
+ qunlock(&p->debug);
+ poperror();
+
+ /* copy result to user space, now that locks are released */
+ memmove(va, buf, n);
+
+ return n;
+}
+
+static void
+procclose(Chan * c)
+{
+ if(QID(c->qid) == Qtrace){
+ lock(&tlock);
+ if(topens > 0)
+ topens--;
+ if(topens == 0)
+ proctrace = nil;
+ unlock(&tlock);
+ }
+ if(QID(c->qid) == Qns && c->aux != 0)
+ free(c->aux);
+}
+
+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
+procargs(Proc *p, char *buf, int nbuf)
+{
+ int j, k, m;
+ char *a;
+ int n;
+
+ a = p->args;
+ if(p->setargs){
+ snprint(buf, nbuf, "%s [%s]", p->text, p->args);
+ return strlen(buf);
+ }
+ 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;
+}
+
+static int
+eventsavailable(void *)
+{
+ return tproduced > tconsumed;
+}
+
+static long
+procread(Chan *c, void *va, long n, vlong off)
+{
+ /* NSEG*32 was too small for worst cases */
+ char *a, flag[10], *sps, *srv, statbuf[NSEG*64];
+ int i, j, m, navail, ne, pid, rsize;
+ long l;
+ uchar *rptr;
+ ulong offset;
+ Confmem *cm;
+ Mntwalk *mw;
+ Proc *p;
+ Segment *sg, *s;
+ Ureg kur;
+ Waitq *wq;
+
+ a = va;
+ offset = off;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, a, n, 0, 0, procgen);
+
+ if(QID(c->qid) == Qtrace){
+ 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 Qargs:
+ qlock(&p->debug);
+ j = procargs(p, up->genbuf, sizeof up->genbuf);
+ qunlock(&p->debug);
+ if(offset >= j)
+ return 0;
+ if(offset+n > j)
+ n = j-offset;
+ memmove(a, &up->genbuf[offset], n);
+ return n;
+ case Qsyscall:
+ if(!p->syscalltrace)
+ return 0;
+ n = readstr(offset, a, n, p->syscalltrace);
+ return n;
+
+ case Qmem:
+ if(offset < KZERO)
+ return procctlmemio(p, offset, n, va, 1);
+
+ if(!iseve())
+ error(Eperm);
+
+ /* validate kernel addresses */
+ if(offset < (ulong)end) {
+ if(offset+n > (ulong)end)
+ n = (ulong)end - offset;
+ memmove(a, (char*)offset, n);
+ return n;
+ }
+ for(i=0; i<nelem(conf.mem); i++){
+ cm = &conf.mem[i];
+ /* klimit-1 because klimit might be zero! */
+ if(cm->kbase <= offset && offset <= cm->klimit-1){
+ if(offset+n >= cm->klimit-1)
+ n = cm->klimit - offset;
+ memmove(a, (char*)offset, n);
+ return n;
+ }
+ }
+ error(Ebadarg);
+
+ case Qprofile:
+ s = p->seg[TSEG];
+ if(s == 0 || s->profile == 0)
+ error("profile is off");
+ i = (s->top-s->base)>>LRESPROF;
+ i *= sizeof(*s->profile);
+ if(offset >= i)
+ return 0;
+ if(offset+n > i)
+ n = i - offset;
+ memmove(a, ((char*)s->profile)+offset, n);
+ return n;
+
+ case Qnote:
+ qlock(&p->debug);
+ if(waserror()){
+ qunlock(&p->debug);
+ nexterror();
+ }
+ if(p->pid != PID(c->qid))
+ error(Eprocdied);
+ if(n < 1) /* must accept at least the '\0' */
+ error(Etoosmall);
+ if(p->nnote == 0)
+ n = 0;
+ else {
+ m = strlen(p->note[0].msg) + 1;
+ if(m > n)
+ m = n;
+ memmove(va, p->note[0].msg, m);
+ ((char*)va)[m-1] = '\0';
+ p->nnote--;
+ memmove(p->note, p->note+1, p->nnote*sizeof(Note));
+ n = m;
+ }
+ if(p->nnote == 0)
+ p->notepending = 0;
+ poperror();
+ qunlock(&p->debug);
+ return n;
+
+ case Qproc:
+ if(offset >= sizeof(Proc))
+ return 0;
+ if(offset+n > sizeof(Proc))
+ n = sizeof(Proc) - offset;
+ memmove(a, ((char*)p)+offset, n);
+ return n;
+
+ 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:
+ rptr = (uchar*)&p->fpsave;
+ rsize = sizeof(FPsave);
+ regread:
+ if(rptr == 0)
+ error(Enoreg);
+ if(offset >= rsize)
+ return 0;
+ if(offset+n > rsize)
+ n = rsize - offset;
+ memmove(a, rptr+offset, n);
+ return n;
+
+ case Qstatus:
+ if(offset >= STATSIZE)
+ return 0;
+ if(offset+n > STATSIZE)
+ n = STATSIZE - offset;
+
+ sps = p->psstate;
+ if(sps == 0)
+ sps = statename[p->state];
+ memset(statbuf, ' ', sizeof statbuf);
+ memmove(statbuf+0*KNAMELEN, p->text, strlen(p->text));
+ memmove(statbuf+1*KNAMELEN, p->user, strlen(p->user));
+ memmove(statbuf+2*KNAMELEN, sps, strlen(sps));
+ j = 2*KNAMELEN + 12;
+
+ for(i = 0; i < 6; i++) {
+ l = p->time[i];
+ if(i == TReal)
+ l = MACHP(0)->ticks - l;
+ l = TK2MS(l);
+ readnum(0, statbuf+j+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
+ }
+ /* ignore stack, which is mostly non-existent */
+ l = 0;
+ for(i=1; i<NSEG; i++){
+ s = p->seg[i];
+ if(s)
+ l += s->top - s->base;
+ }
+ readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, l>>10, NUMSIZE);
+ readnum(0, statbuf+j+NUMSIZE*7, NUMSIZE, p->basepri, NUMSIZE);
+ readnum(0, statbuf+j+NUMSIZE*8, NUMSIZE, p->priority, NUMSIZE);
+ memmove(a, statbuf+offset, n);
+ return n;
+
+ case Qsegment:
+ j = 0;
+ for(i = 0; i < NSEG; i++) {
+ sg = p->seg[i];
+ if(sg == 0)
+ continue;
+ j += sprint(statbuf+j, "%-6s %c%c %.8lux %.8lux %4ld\n",
+ sname[sg->type&SG_TYPE],
+ sg->type&SG_RONLY ? 'R' : ' ',
+ sg->profile ? 'P' : ' ',
+ sg->base, sg->top, sg->ref);
+ }
+ if(offset >= j)
+ return 0;
+ if(offset+n > j)
+ n = j-offset;
+ if(n == 0 && offset == 0)
+ exhausted("segments");
+ memmove(a, &statbuf[offset], n);
+ return n;
+
+ case Qwait:
+ if(!canqlock(&p->qwaitr))
+ error(Einuse);
+
+ if(waserror()) {
+ qunlock(&p->qwaitr);
+ nexterror();
+ }
+
+ lock(&p->exl);
+ if(up == p && p->nchild == 0 && p->waitq == 0) {
+ unlock(&p->exl);
+ error(Enochild);
+ }
+ pid = p->pid;
+ while(p->waitq == 0) {
+ unlock(&p->exl);
+ sleep(&p->waitr, haswaitq, p);
+ if(p->pid != pid)
+ error(Eprocdied);
+ lock(&p->exl);
+ }
+ wq = p->waitq;
+ p->waitq = wq->next;
+ p->nwait--;
+ unlock(&p->exl);
+
+ qunlock(&p->qwaitr);
+ poperror();
+ n = snprint(a, n, "%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);
+ return n;
+
+ case Qns:
+ qlock(&p->debug);
+ if(waserror()){
+ qunlock(&p->debug);
+ nexterror();
+ }
+ if(p->pgrp == nil || p->pid != PID(c->qid))
+ error(Eprocdied);
+ mw = c->aux;
+ if(mw->cddone){
+ qunlock(&p->debug);
+ poperror();
+ return 0;
+ }
+ mntscan(mw, p);
+ if(mw->mh == 0){
+ mw->cddone = 1;
+ i = snprint(a, n, "cd %s\n", p->dot->path->s);
+ qunlock(&p->debug);
+ poperror();
+ return i;
+ }
+ int2flag(mw->cm->mflag, flag);
+ if(strcmp(mw->cm->to->path->s, "#M") == 0){
+ srv = srvname(mw->cm->to->mchan);
+ i = snprint(a, n, "mount %s %s %s %s\n", flag,
+ srv==nil? mw->cm->to->mchan->path->s : srv,
+ mw->mh->from->path->s, mw->cm->spec? mw->cm->spec : "");
+ free(srv);
+ }else
+ i = snprint(a, n, "bind %s %s %s\n", flag,
+ mw->cm->to->path->s, mw->mh->from->path->s);
+ qunlock(&p->debug);
+ poperror();
+ return i;
+
+ case Qnoteid:
+ return readnum(offset, va, n, p->noteid, NUMSIZE);
+ case Qfd:
+ return procfds(p, va, n, offset);
+ }
+ error(Egreg);
+ return 0; /* not reached */
+}
+
+void
+mntscan(Mntwalk *mw, Proc *p)
+{
+ Pgrp *pg;
+ Mount *t;
+ Mhead *f;
+ int nxt, i;
+ ulong last, bestmid;
+
+ pg = p->pgrp;
+ rlock(&pg->ns);
+
+ nxt = 0;
+ bestmid = ~0;
+
+ last = 0;
+ if(mw->mh)
+ last = mw->cm->mountid;
+
+ for(i = 0; i < MNTHASH; i++) {
+ for(f = pg->mnthash[i]; f; f = f->hash) {
+ for(t = f->mount; t; t = t->next) {
+ if(mw->mh == 0 ||
+ (t->mountid > last && t->mountid < bestmid)) {
+ mw->cm = t;
+ mw->mh = f;
+ bestmid = mw->cm->mountid;
+ nxt = 1;
+ }
+ }
+ }
+ }
+ if(nxt == 0)
+ mw->mh = 0;
+
+ runlock(&pg->ns);
+}
+
+static long
+procwrite(Chan *c, void *va, long n, vlong off)
+{
+ int id, m;
+ Proc *p, *t, *et;
+ char *a, *arg, buf[ERRMAX];
+ ulong offset = off;
+
+ a = va;
+ if(c->qid.type & QTDIR)
+ error(Eisdir);
+
+ p = proctab(SLOT(c->qid));
+
+ /* Use the remembered noteid in the channel rather
+ * than the process pgrpid
+ */
+ if(QID(c->qid) == Qnotepg) {
+ pgrpnote(NOTEID(c->pgrpid), va, n, NUser);
+ return n;
+ }
+
+ qlock(&p->debug);
+ if(waserror()){
+ qunlock(&p->debug);
+ nexterror();
+ }
+ if(p->pid != PID(c->qid))
+ error(Eprocdied);
+
+ switch(QID(c->qid)){
+ case Qargs:
+ if(n == 0)
+ error(Eshort);
+ if(n >= ERRMAX)
+ error(Etoobig);
+ arg = malloc(n+1);
+ if(arg == nil)
+ error(Enomem);
+ memmove(arg, va, n);
+ m = n;
+ if(arg[m-1] != 0)
+ arg[m++] = 0;
+ free(p->args);
+ p->nargs = m;
+ p->args = arg;
+ p->setargs = 1;
+ break;
+
+ case Qmem:
+ if(p->state != Stopped)
+ error(Ebadctl);
+
+ n = procctlmemio(p, offset, n, va, 0);
+ break;
+
+ case Qregs:
+ if(offset >= sizeof(Ureg))
+ n = 0;
+ else if(offset+n > sizeof(Ureg))
+ n = sizeof(Ureg) - offset;
+ if(p->dbgreg == 0)
+ 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;
+ memmove((uchar*)&p->fpsave+offset, va, n);
+ break;
+
+ case Qctl:
+ procctlreq(p, va, n);
+ break;
+
+ case Qnote:
+ if(p->kp)
+ error(Eperm);
+ if(n >= ERRMAX-1)
+ error(Etoobig);
+ memmove(buf, va, n);
+ buf[n] = 0;
+ if(!postnote(p, 0, buf, NUser))
+ error("note not posted");
+ break;
+ case Qnoteid:
+ id = atoi(a);
+ if(id == p->pid) {
+ p->noteid = id;
+ break;
+ }
+ t = proctab(0);
+ for(et = t+conf.nproc; t < et; t++) {
+ if(t->state == Dead)
+ continue;
+ if(id == t->noteid) {
+ if(strcmp(p->user, t->user) != 0)
+ error(Eperm);
+ p->noteid = id;
+ break;
+ }
+ }
+ if(p->noteid != id)
+ error(Ebadarg);
+ break;
+ default:
+ pprint("unknown qid in procwrite\n");
+ error(Egreg);
+ }
+ poperror();
+ qunlock(&p->debug);
+ return n;
+}
+
+Dev procdevtab = {
+ 'p',
+ "proc",
+
+ devreset,
+ procinit,
+ devshutdown,
+ procattach,
+ procwalk,
+ procstat,
+ procopen,
+ devcreate,
+ procclose,
+ procread,
+ devbread,
+ procwrite,
+ devbwrite,
+ devremove,
+ procwstat,
+};
+
+Chan*
+proctext(Chan *c, Proc *p)
+{
+ Chan *tc;
+ Image *i;
+ Segment *s;
+
+ s = p->seg[TSEG];
+ if(s == 0)
+ error(Enonexist);
+ if(p->state==Dead)
+ error(Eprocdied);
+
+ lock(s);
+ i = s->image;
+ if(i == 0) {
+ unlock(s);
+ error(Eprocdied);
+ }
+ unlock(s);
+
+ lock(i);
+ if(waserror()) {
+ unlock(i);
+ nexterror();
+ }
+
+ tc = i->c;
+ if(tc == 0)
+ error(Eprocdied);
+
+ if(incref(tc) == 1 || (tc->flag&COPEN) == 0 || tc->mode!=OREAD) {
+ cclose(tc);
+ error(Eprocdied);
+ }
+
+ if(p->pid != PID(c->qid))
+ error(Eprocdied);
+
+ unlock(i);
+ poperror();
+
+ return tc;
+}
+
+void
+procstopwait(Proc *p, int ctl)
+{
+ int pid;
+
+ if(p->pdbg)
+ error(Einuse);
+ if(procstopped(p) || p->state == Broken)
+ return;
+
+ if(ctl != 0)
+ p->procctl = ctl;
+ p->pdbg = up;
+ pid = p->pid;
+ qunlock(&p->debug);
+ up->psstate = "Stopwait";
+ if(waserror()) {
+ p->pdbg = 0;
+ qlock(&p->debug);
+ nexterror();
+ }
+ sleep(&up->sleep, procstopped, p);
+ poperror();
+ qlock(&p->debug);
+ if(p->pid != pid)
+ error(Eprocdied);
+}
+
+static void
+procctlcloseone(Proc *p, Fgrp *f, int fd)
+{
+ Chan *c;
+
+ c = f->fd[fd];
+ if(c == nil)
+ return;
+ f->fd[fd] = nil;
+ unlock(f);
+ qunlock(&p->debug);
+ cclose(c);
+ qlock(&p->debug);
+ lock(f);
+}
+
+void
+procctlclosefiles(Proc *p, int all, int fd)
+{
+ int i;
+ Fgrp *f;
+
+ f = p->fgrp;
+ if(f == nil)
+ error(Eprocdied);
+
+ lock(f);
+ f->ref++;
+ if(all)
+ for(i = 0; i < f->maxfd; i++)
+ procctlcloseone(p, f, i);
+ else
+ procctlcloseone(p, f, 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;
+}
+
+void
+procctlreq(Proc *p, char *va, int n)
+{
+ Segment *s;
+ int npc, pri;
+ Cmdbuf *cb;
+ Cmdtab *ct;
+ vlong time;
+ char *e;
+ void (*pt)(Proc*, int, vlong);
+
+ if(p->kp) /* no ctl requests to kprocs */
+ error(Eperm);
+
+ 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 == 0 || (s->type&SG_TYPE) != SG_TEXT)
+ error(Ebadctl);
+ if(s->profile != 0)
+ free(s->profile);
+ npc = (s->top-s->base)>>LRESPROF;
+ s->profile = malloc(npc*sizeof(*s->profile));
+ if(s->profile == 0)
+ error(Enomem);
+ 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;
+ /* 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 == 0)
+ 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)
+ edfstop(p);
+ break;
+ case CMevent:
+ pt = proctrace;
+ if(up->trace && pt)
+ pt(up, SUser, 0);
+ break;
+ }
+
+ poperror();
+ free(cb);
+}
+
+int
+procstopped(void *a)
+{
+ Proc *p = a;
+ return p->state == Stopped;
+}
+
+int
+procctlmemio(Proc *p, ulong offset, int n, void *va, int read)
+{
+ KMap *k;
+ Pte *pte;
+ Page *pg;
+ Segment *s;
+ ulong soff, l;
+ char *a = va, *b;
+
+ for(;;) {
+ s = seg(p, offset, 1);
+ if(s == 0)
+ error(Ebadarg);
+
+ if(offset+n >= s->top)
+ n = s->top-offset;
+
+ if(!read && (s->type&SG_TYPE) == SG_TEXT)
+ s = txt2data(p, s);
+
+ s->steal++;
+ soff = offset-s->base;
+ if(waserror()) {
+ s->steal--;
+ nexterror();
+ }
+ if(fixfault(s, offset, read, 0) == 0)
+ break;
+ poperror();
+ s->steal--;
+ }
+ poperror();
+ pte = s->map[soff/PTEMAPMEM];
+ if(pte == 0)
+ panic("procctlmemio");
+ pg = pte->pages[(soff&(PTEMAPMEM-1))/BY2PG];
+ if(pagedout(pg))
+ panic("procctlmemio1");
+
+ l = BY2PG - (offset&(BY2PG-1));
+ if(n > l)
+ n = l;
+
+ k = kmap(pg);
+ if(waserror()) {
+ s->steal--;
+ kunmap(k);
+ nexterror();
+ }
+ b = (char*)VA(k);
+ b += offset&(BY2PG-1);
+ if(read == 1)
+ memmove(a, b, n); /* This can fault */
+ else
+ memmove(b, a, n);
+ kunmap(k);
+ poperror();
+
+ /* Ensure the process sees text page changes */
+ if(s->flushme)
+ memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
+
+ s->steal--;
+
+ if(read == 0)
+ p->newtlb = 1;
+
+ return n;
+}
+
+Segment*
+txt2data(Proc *p, Segment *s)
+{
+ int i;
+ Segment *ps;
+
+ ps = newseg(SG_DATA, s->base, s->size);
+ ps->image = s->image;
+ incref(ps->image);
+ ps->fstart = s->fstart;
+ ps->flen = s->flen;
+ ps->flushme = 1;
+
+ qlock(&p->seglock);
+ for(i = 0; i < NSEG; i++)
+ if(p->seg[i] == s)
+ break;
+ if(i == NSEG)
+ panic("segment gone");
+
+ qunlock(&s->lk);
+ putseg(s);
+ qlock(&ps->lk);
+ p->seg[i] = ps;
+ qunlock(&p->seglock);
+
+ return ps;
+}
+
+Segment*
+data2txt(Segment *s)
+{
+ Segment *ps;
+
+ ps = newseg(SG_TEXT, s->base, s->size);
+ ps->image = s->image;
+ incref(ps->image);
+ ps->fstart = s->fstart;
+ ps->flen = s->flen;
+ ps->flushme = 1;
+
+ return ps;
+}