diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /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-x | sys/src/9/port/devproc.c | 1592 |
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; +} |