jehanne/sys/src/kern/port/devproc.c

1630 lines
30 KiB
C

/*
* This file is part of Jehanne.
*
* Copyright (C) 2015-2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include <trace.h>
#include "ureg.h"
extern long write_working_dir(Proc* p, void *va, long n, int64_t off);
extern long read_working_dir(Proc* p, void *va, long n, int64_t off);
/* We can have up to 32 files in proc/n sice we dedicate 5 bits in Qid
* to it (see QSHIFT)
*/
enum
{
Qdir,
Qtrace,
Qargs,
Qctl,
Qfd,
Qfpregs,
Qkregs,
Qmem,
Qnote,
Qnoteid,
Qnotepg,
Qns,
Qproc,
Qppid,
Qregs,
Qsegment,
Qstatus,
Qtext,
Qwait,
Qprofile,
Qsyscall,
Qwdir,
};
enum
{
CMclose,
CMclosefiles,
CMfixedpri,
CMhang,
CMkill,
CMnohang,
CMnoswap,
CMpri,
CMprivate,
CMprofile,
CMstart,
CMstartstop,
CMstartsyscall,
CMstop,
CMwaitstop,
CMwired,
CMtrace,
};
enum{
Nevents = 0x4000,
Emask = Nevents - 1,
};
#define STATSIZE (2*KNAMELEN+NUMSIZE+9*NUMSIZE + 1)
/* In Plan 9 status, fd, and ns were left fully readable (0444)
* because of their use in debugging, particularly on shared servers.
*
* In Jehanne the process owner and the host owner can read
* status and fd, but not others (0440).
* TODO: allow per process stats and permissions.
*/
Dirtab procdir[] =
{
"args", {Qargs}, 0, 0660,
"ctl", {Qctl}, 0, 0000,
"fd", {Qfd}, 0, 0440,
"fpregs", {Qfpregs}, 0, 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, 0640,
"proc", {Qproc}, 0, 0400,
"ppid", {Qppid}, 0, 0400,
"regs", {Qregs}, sizeof(Ureg), 0000,
"segment", {Qsegment}, 0, 0444,
"status", {Qstatus}, STATSIZE, 0444,
"text", {Qtext}, 0, 0000,
"wait", {Qwait}, 0, 0400,
"syscall", {Qsyscall}, 0, 0400,
"wdir", {Qwdir}, 0, 0640,
};
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,
CMstart, "start", 1,
CMstartstop, "startstop", 1,
CMstartsyscall, "startsyscall", 1,
CMstop, "stop", 1,
CMwaitstop, "waitstop", 1,
CMwired, "wired", 2,
CMtrace, "trace", 0,
};
/*
* Qids are, in path:
* 5 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) ((((uint32_t)(q).path)&0x0000001F)>>0)
#define SLOT(q) (((((uint32_t)(q).path)&0x07FFFFFE0)>>QSHIFT)-1)
#define PID(q) ((q).vers)
#define NOTEID(q) ((q).vers)
static void procctlreq(Proc*, char*, int);
static int procctlmemio(Proc*, uintptr_t, int, void*, int);
static Chan* proctext(Chan*, Proc*);
static int procstopped(void*);
static void mntscan(Mntwalk*, Proc*);
static Traceevent *tevents;
static Lock tlock;
static int topens;
static int tproduced, tconsumed;
static void
profclock(Ureg *ur, Timer * _1)
{
if(up == nil || up->state != Running)
return;
}
static int
procgen(Chan *c, char *name, Dirtab *tab, int _1, int s, Dir *dp)
{
Qid qid;
Proc *p;
char *ename;
int pid;
uint32_t 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){
jehanne_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 = jehanne_strtol(name, &ename, 10);
if(pid<=0 || ename[0]!='\0')
return -1;
s = psindex(pid);
if(s < 0)
return -1;
}
else if(--s >= procalloc.nproc)
return -1;
if((p = psincref(s)) == nil || (pid = p->pid) == 0)
return 0;
jehanne_sprint(up->genbuf, "%d", pid);
/*
* String comparison is done in devwalk so
* name must match its formatted pid.
*/
if(name != nil && jehanne_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);
psdecref(p);
return 1;
}
if(c->qid.path == Qtrace){
jehanne_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 */
if((p = psincref(SLOT(c->qid))) == nil)
return -1;
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;
}
switch(QID(tab->qid)){
case Qwdir:
/* file length might be relevant to the caller to
* malloc enough space in the buffer
*/
if(p->dot)
len = 1 + jehanne_strlen(p->dot->path->s);
break;
}
mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
devdir(c, qid, tab->name, len, p->user, perm, dp);
psdecref(p);
return 1;
}
static void
_proctrace(Proc* p, int etype, int64_t ts, int64_t _1)
{
Traceevent *te;
if (p->trace == 0 || topens == 0 ||
tproduced - tconsumed >= Nevents)
return;
te = &tevents[tproduced&Emask];
te->pid = p->pid;
te->etype = (Tevent)etype;
if (ts == 0)
te->time = todget(nil);
else
te->time = ts;
tproduced++;
}
static void
procinit(void)
{
if(procalloc.nproc >= (1<<(16-QSHIFT))-1)
jehanne_print("warning: too many procs for devproc\n");
addclock0link((void (*)(void))profclock, 113); /* Relative prime to HZ */
}
static Chan*
procattach(Chan *c, Chan *ac, char *spec, int flags)
{
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 long
procstat(Chan *c, uint8_t *db, long 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(jehanne_strcmp(up->user, "none") != 0)
return;
if(isevegroup())
return;
error(Eperm);
}
static Chan*
procopen(Chan *c, unsigned long 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*)jehanne_malloc(sizeof(Traceevent) * Nevents);
if(tevents == nil)
error(Enomem);
tproduced = tconsumed = 0;
}
proctrace = _proctrace;
poperror();
unlock(&tlock);
c->mode = openmode(omode);
c->flag |= COPEN;
c->offset = 0;
return c;
}
if((p = psincref(SLOT(c->qid))) == nil)
error(Eprocdied);
qlock(&p->debug);
if(waserror()){
qunlock(&p->debug);
psdecref(p);
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;
poperror();
qunlock(&p->debug);
psdecref(p);
return tc;
case Qproc:
case Qppid:
case Qkregs:
case Qprofile:
case Qfd:
if(omode != OREAD && omode != OSTAT)
error(Eperm);
break;
case Qsegment:
if(omode != OREAD && omode != OSTAT)
error(Eperm);
break;
case Qnote:
if(p->privatemem)
error(Eperm);
if(p->state < Ready && omode != OREAD && omode != OSTAT)
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:
nonone(p);
break;
case Qsyscall:
nonone(p);
if(p->syscallq != nil)
error(Einuse);
c->aux = qopen(1024, 1, nil, nil);
p->syscallq = c->aux;
break;
case Qns:
if(omode == OREAD){
c->aux = jehanne_malloc(sizeof(Mntwalk));
} else if(omode == OWRITE){
if(up->pgrp->noattach || strcmp(up->user, p->user) != 0)
error(Eperm);
} else {
error(Eperm);
}
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;
case Qwdir:
if(p == up) /* self write is always allowed */
break;
if(omode > ORDWR)
error(Eperm);
if(jehanne_strcmp(up->user, p->user) != 0 /* process owner can read/write */
|| !iseve() /* host owner can read */
|| (omode&OWRITE) != 0)
error(Eperm);
break;
default:
poperror();
qunlock(&p->debug);
psdecref(p);
pprint("procopen %#llux\n", c->qid.path);
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);
poperror();
qunlock(&p->debug);
psdecref(p);
return tc;
}
static long
procwstat(Chan *c, uint8_t *db, long n)
{
Proc *p;
Dir *d;
if(c->qid.type & QTDIR)
error(Eperm);
if(QID(c->qid) == Qtrace)
return devwstat(c, db, n);
if((p = psincref(SLOT(c->qid))) == nil)
error(Eprocdied);
nonone(p);
d = nil;
qlock(&p->debug);
if(waserror()){
qunlock(&p->debug);
psdecref(p);
jehanne_free(d);
nexterror();
}
if(p->pid != PID(c->qid))
error(Eprocdied);
if(jehanne_strcmp(up->user, p->user) != 0 && jehanne_strcmp(up->user, eve) != 0)
error(Eperm);
d = smalloc(sizeof(Dir)+n);
n = jehanne_convM2D(db, n, &d[0], (char*)&d[1]);
if(n == 0)
error(Eshortstat);
if(!emptystr(d->uid) && jehanne_strcmp(d->uid, p->user) != 0){
if(jehanne_strcmp(up->user, eve) != 0)
error(Eperm);
else
kstrdup(&p->user, d->uid);
}
if(d->mode != (uint32_t)~0UL)
p->procmode = d->mode&0777;
poperror();
qunlock(&p->debug);
psdecref(p);
jehanne_free(d);
return n;
}
static long
procoffset(long offset, char *va, int *np)
{
if(offset > 0) {
offset -= *np;
if(offset < 0) {
jehanne_memmove(va, va+*np+offset, -offset);
*np = -offset;
}
else
*np = 0;
}
return offset;
}
static int
procqidwidth(Chan *c)
{
char buf[32];
return jehanne_sprint(buf, "%lud", c->qid.vers);
}
static int
_procfdprint(Chan *c, int fd, int w, char *s, int ns, char *modestr)
{
int n;
char *flags;
if((c->flag&(OCEXEC|ORCLOSE)) == (OCEXEC|ORCLOSE))
flags = "ED";
else if(c->flag&OCEXEC)
flags = "E ";
else if(c->flag&ORCLOSE)
flags = "D ";
else
flags = " ";
if(w == 0)
w = procqidwidth(c);
n = jehanne_snprint(s, ns, "%3d %.2s%s %C %4ud (%.16llux %*lud %.2ux) %5ld %8lld %s\n",
fd,
&modestr[(c->mode&3)<<1],
flags,
c->dev->dc, c->devno,
c->qid.path, w, c->qid.vers, c->qid.type,
c->iounit, c->offset, c->path->s);
return n;
}
int
procfdprint(Chan *c, int fd, int w, char *s, int ns)
{
return _procfdprint(c, fd, w, s, ns, " s r wrw");
}
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, *modestr;
/* 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->l);
if(waserror()){
unlock(&f->l);
qunlock(&p->debug);
nexterror();
}
n = readstr(0, a, count, p->dot->path->s);
n += jehanne_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;
modestr = " s r wrw";
if(p->blockingfd == i){
if(p->scallnr == SysPread)
modestr = " s R wRw";
else
modestr = " s r WrW";
}
n += _procfdprint(c, i, w, a+n, count-n, modestr);
offset = procoffset(offset, a, &n);
}
poperror();
unlock(&f->l);
qunlock(&p->debug);
/* copy result to user space, now that locks are released */
jehanne_memmove(va, buf, n);
return n;
}
static void
procclose(Chan * c)
{
Proc *p;
switch(QID(c->qid)){
case Qtrace:
lock(&tlock);
if(topens > 0)
topens--;
if(topens == 0)
proctrace = nil;
unlock(&tlock);
break;
case Qns:
if(c->aux != nil)
jehanne_free(c->aux);
break;
case Qsyscall:
if((p = psincref(SLOT(c->qid))) != nil){
qlock(&p->debug);
if(p->pid == PID(c->qid))
p->syscallq = nil;
qunlock(&p->debug);
psdecref(p);
}
if(c->aux != nil)
qfree(c->aux);
break;
}
}
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';
*s = '\0';
}
static int
procargs(Proc *p, char *buf, int nbuf)
{
int i;
char **args, **margs, *e;
ProcSegment *s;
PagePointer page = 0;
char *pbase, *marg;
uintptr_t argaddr, ptop;
args = p->args;
if(args == nil)
return 0;
if(p->setargs){
jehanne_snprint(buf, nbuf, "%s [%s]", p->text, args[0]);
return jehanne_strlen(buf);
}
e = buf + nbuf;
/* since we did not created a copy of args on exec, we have
* to do a little black magic
*/
rlock(&p->seglock); /* enough to avoid problems since
* the stack is never shared
*/
s = p->seg[SSEG];
/* note that neither segment_page nor page_kmap increase the
* page's ref count, but this should be safe in this very specific
* case (top of the stack with seglock rlocked)
*/
page = segment_page(s, (uintptr_t)args);
if(page == 0){
buf = jehanne_seprint(buf, e, "cannot print args for %s %d: stack gone", p->text, p->pid);
goto ArgsPrinted;
}
pbase = page_kmap(page);
ptop = (uintptr_t)(pbase + PGSZ);
margs = (char**)(pbase + (((uintptr_t)args)&(PGSZ-1)));
if(((uintptr_t)&margs[p->nargs - 1]) > ptop){
buf = jehanne_seprint(buf, e, "%s: too many arguments", p->text);
goto DoneWithMappedPage;
}
for(i = 0; i < p->nargs; ++i){
if(buf >= e)
break;
argaddr = (uintptr_t)margs[i];
if(argaddr < (uintptr_t)args || argaddr >= s->top){
buf = jehanne_seprint(buf, e, i?" %#p":"%#p", argaddr);
} else if(argaddr - (uintptr_t)args > PGSZ){
buf = jehanne_seprint(buf, e, " ...");
break;
} else {
marg = pbase + (argaddr&(PGSZ-1));
buf = jehanne_seprint(buf, e, i?" %q":"%q", marg);
}
}
DoneWithMappedPage:
page_kunmap(page, &pbase);
ArgsPrinted:
runlock(&p->seglock);
return buf - (e - nbuf);
}
static int
eventsavailable(void * _1)
{
return tproduced > tconsumed;
}
static long
procread(Chan *c, void *va, long n, int64_t off)
{
Proc *p;
int64_t l;
int32_t r;
Waitq *wq;
Ureg kur;
uint8_t *rptr;
Mntwalk *mw;
ProcSegment *sg, *s;
int i, j, navail, ne, pid, rsize;
char flag[10], *a, *sps, *srv, statbuf[NSEG*STATSIZE];
uintptr_t offset;
uintmem paddr, plimit, psize;
uint64_t u;
if(c->qid.type & QTDIR)
return devdirread(c, va, n, 0, 0, procgen);
a = va;
offset = off;
if(QID(c->qid) == Qtrace){
if(!eventsavailable(nil))
return 0;
rptr = va;
navail = tproduced - tconsumed;
if(navail > n / sizeof(Traceevent))
navail = n / sizeof(Traceevent);
while(navail > 0) {
if((tconsumed & Emask) + navail > Nevents)
ne = Nevents - (tconsumed & Emask);
else
ne = navail;
i = ne * sizeof(Traceevent);
jehanne_memmove(rptr, &tevents[tconsumed & Emask], i);
tconsumed += ne;
rptr += i;
navail -= ne;
}
return rptr - (uint8_t*)va;
}
if(QID(c->qid) == Qsyscall && qcanread(c->aux)){
/* whatever the process status, the reader can read
* pending syscall records
*/
return qread(c->aux, va, n);
}
if((p = psincref(SLOT(c->qid))) == nil)
error(Eprocdied);
if(p->pid != PID(c->qid)){
psdecref(p);
error(Eprocdied);
}
switch(QID(c->qid)){
default:
psdecref(p);
break;
case Qargs:
qlock(&p->debug);
j = procargs(p, up->genbuf, sizeof up->genbuf);
qunlock(&p->debug);
psdecref(p);
if(offset >= j)
return 0;
if(offset+n > j)
n = j-offset;
jehanne_memmove(va, &up->genbuf[offset], n);
return n;
case Qsyscall:
psdecref(p);
return qread(c->aux, va, n);
case Qmem:
if(!iskaddr(offset)
|| (offset >= USTKTOP-USTKSIZE && offset < USTKTOP)){
r = procctlmemio(p, offset, n, va, 1);
psdecref(p);
return r;
}
if(!iseve()){
psdecref(p);
error(Eperm);
}
/* validate kernel addresses */
if(offset < PTR2UINT(end)) {
if(offset+n > PTR2UINT(end))
n = PTR2UINT(end) - offset;
jehanne_memmove(va, UINT2PTR(offset), n);
psdecref(p);
return n;
}
paddr = PADDR(UINT2PTR(offset));
if(!ismapped(&rmapram, paddr, &psize)){
psdecref(p);
error(Ebadarg);
}
plimit = paddr + psize;
/* plimit-1 because plimit might be zero (address space top) */
if(paddr+n >= plimit-1)
n = plimit - paddr;
jehanne_memmove(va, UINT2PTR(offset), n);
psdecref(p);
return n;
case Qnote:
qlock(&p->debug);
if(waserror()){
qunlock(&p->debug);
psdecref(p);
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 {
i = jehanne_strlen(p->note[0].msg) + 1;
if(i < n)
n = i;
rptr = va;
jehanne_memmove(rptr, p->note[0].msg, n);
rptr[n-1] = '\0';
if(--p->nnote == 0)
p->notepending = 0;
jehanne_memmove(p->note, p->note+1, p->nnote*sizeof(Note));
}
poperror();
qunlock(&p->debug);
psdecref(p);
return n;
case Qproc:
if(offset >= sizeof(Proc)){
psdecref(p);
return 0;
}
if(offset+n > sizeof(Proc))
n = sizeof(Proc) - offset;
jehanne_memmove(va, ((char*)p)+offset, n);
psdecref(p);
return n;
case Qregs:
rptr = (uint8_t*)p->dbgreg;
rsize = sizeof(Ureg);
goto regread;
case Qkregs:
memset(&kur, 0, sizeof(Ureg));
setkernur(&kur, p);
rptr = (uint8_t*)&kur;
rsize = sizeof(Ureg);
goto regread;
case Qfpregs:
rptr = (uint8_t*)&p->fpsave;
rsize = sizeof(FPsave);
regread:
if(rptr == nil){
psdecref(p);
error(Enoreg);
}
if(offset >= rsize){
psdecref(p);
return 0;
}
if(offset+n > rsize)
n = rsize - offset;
memmove(a, rptr+offset, n);
psdecref(p);
return n;
case Qstatus:
if(offset >= sizeof statbuf){
psdecref(p);
return 0;
}
if(offset+n > sizeof statbuf)
n = (sizeof statbuf) - offset;
sps = p->psstate;
if(sps == 0)
sps = statename[p->state];
jehanne_memset(statbuf, ' ', sizeof statbuf);
jehanne_sprint(statbuf, "%-*.*s%-*.*s%-12.11s",
KNAMELEN, KNAMELEN-1, p->text,
KNAMELEN, KNAMELEN-1, p->user,
sps);
j = 2*KNAMELEN + 12;
for(i = 0; i < 6; i++) {
l = p->time[i];
if(i == TReal)
l = sys->ticks - l;
l = TK2MS(l);
readnum(0, statbuf+j, NUMSIZE, l, NUMSIZE);
j += NUMSIZE;
}
/* ignore stack, which is mostly non-existent */
u = 0;
for(i=1; i<NSEG; i++){
s = p->seg[i];
if(s)
u += s->top - s->base;
}
readnum(0, statbuf+j, NUMSIZE, u>>10, NUMSIZE);
j += NUMSIZE;
readnum(0, statbuf+j, NUMSIZE, p->basepri, NUMSIZE);
j += NUMSIZE;
readnum(0, statbuf+j, NUMSIZE, p->priority, NUMSIZE);
j += NUMSIZE;
statbuf[j++] = '\n';
if(offset+n > j)
n = j-offset;
jehanne_memmove(va, statbuf+offset, n);
psdecref(p);
return n;
case Qsegment:
j = 0;
for(i = 0; i < NSEG; i++) {
sg = p->seg[i];
if(sg == 0)
continue;
j += jehanne_sprint(statbuf+j, "%-6s %c%c %p %p %4d\n",
segment_name(sg),
!(sg->type&SgWrite) ? 'R' : ' ',
(sg->type&SgExecute) ? 'x' : ' ',
' ', // sg->profile ? 'P' : ' ', // here was profiling
sg->base, sg->top, sg->r.ref);
}
psdecref(p);
if(offset >= j)
return 0;
if(offset+n > j)
n = j-offset;
if(n == 0 && offset == 0)
exhausted("segments");
jehanne_memmove(va, &statbuf[offset], n);
return n;
case Qwait:
if(!canqlock(&p->qwaitr)){
psdecref(p);
error(Einuse);
}
if(waserror()) {
qunlock(&p->qwaitr);
psdecref(p);
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);
poperror();
qunlock(&p->qwaitr);
psdecref(p);
n = jehanne_snprint(va, n, "%d %lud %lud %lud %q",
wq->w.pid,
wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal],
wq->w.msg);
jehanne_free(wq);
return n;
case Qns:
qlock(&p->debug);
if(waserror()){
qunlock(&p->debug);
psdecref(p);
nexterror();
}
if(p->pgrp == nil || p->pid != PID(c->qid))
error(Eprocdied);
mw = c->aux;
if(mw->cddone){
poperror();
qunlock(&p->debug);
psdecref(p);
return 0;
}
mntscan(mw, p);
if(mw->mh == 0){
mw->cddone = 1;
i = jehanne_snprint(va, n, "cd %s\n", p->dot->path->s);
poperror();
qunlock(&p->debug);
psdecref(p);
return i;
}
int2flag(mw->cm->mflag, flag);
if(jehanne_strcmp(mw->cm->to->path->s, "#9") == 0){
srv = srvname(mw->cm->to->mchan);
i = jehanne_snprint(va, 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 : "");
jehanne_free(srv);
}else
i = jehanne_snprint(va, n, "bind %s %s %s\n", flag,
mw->cm->to->path->s, mw->mh->from->path->s);
poperror();
qunlock(&p->debug);
psdecref(p);
return i;
case Qnoteid:
r = readnum(offset, va, n, p->noteid, NUMSIZE);
psdecref(p);
return r;
case Qppid:
r = readnum(offset, va, n, p->parentpid, NUMSIZE);
psdecref(p);
return r;
case Qfd:
r = procfds(p, va, n, offset);
psdecref(p);
return r;
case Qwdir:
r = read_working_dir(p, va, n, off);
psdecref(p);
return r;
}
error(Egreg);
return 0; /* not reached */
}
static void
mntscan(Mntwalk *mw, Proc *p)
{
Pgrp *pg;
Mount *t;
Mhead *f;
int best, i, last, nxt;
pg = p->pgrp;
rlock(&pg->ns);
nxt = 0;
best = (int)(~0U>>1); /* largest 2's complement int */
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 < best)) {
mw->cm = t;
mw->mh = f;
best = mw->cm->mountid;
nxt = 1;
}
}
}
}
if(nxt == 0)
mw->mh = 0;
runlock(&pg->ns);
}
static long
procwrite(Chan *c, void *va, long n, int64_t off)
{
Proc *p, *t;
Pgrp *opg;
int i, id, l;
char **args, buf[ERRMAX];
uintptr_t offset;
if(c->qid.type & QTDIR)
error(Eisdir);
/* 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;
}
if((p = psincref(SLOT(c->qid))) == nil)
error(Eprocdied);
qlock(&p->debug);
if(waserror()){
qunlock(&p->debug);
psdecref(p);
nexterror();
}
if(p->pid != PID(c->qid))
error(Eprocdied);
offset = off;
switch(QID(c->qid)){
case Qargs:
if(n == 0)
error(Eshort);
if(n >= ERRMAX)
error(Etoobig);
jehanne_memmove(buf, va, n);
args = jehanne_malloc(sizeof(char*)+n+1);
if(args == nil)
error(Enomem);
args[0] = ((char*)args)+sizeof(char*);
jehanne_memmove(args[0], buf, n);
l = n;
if(args[0][l-1] != 0)
args[0][l++] = 0;
if(p->setargs) /* setargs == 0 => args in stack from sysexec */
jehanne_free(p->args);
p->nargs = l;
p->args = args;
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 == 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;
memmove((uint8_t*)&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);
jehanne_memmove(buf, va, n);
buf[n] = 0;
if(!postnote(p, 0, buf, NUser))
error("note not posted");
break;
case Qnoteid:
id = jehanne_atoi(va);
if(id == p->pid) {
p->noteid = id;
break;
}
for(i = 0; (t = psincref(i)) != nil; i++){
if(t->state == Dead || t->noteid != id){
psdecref(t);
continue;
}
if(jehanne_strcmp(p->user, t->user) != 0){
psdecref(t);
error(Eperm);
}
psdecref(t);
p->noteid = id;
break;
}
if(p->noteid != id)
error(Ebadarg);
break;
case Qwdir:
n = write_working_dir(p, va, n, off);
break;
case Qns:
if(strcmp("clone", va) != 0)
error(Ebadarg);
opg = up->pgrp;
up->pgrp = newpgrp();
pgrpcpy(up->pgrp, p->pgrp);
/* inherit noattach */
up->pgrp->noattach = p->pgrp->noattach;
/* inherit dot */
if(up->dot != nil)
cclose(up->dot);
up->dot = p->dot;
incref(&up->dot->r);
closepgrp(opg);
break;
default:
poperror();
qunlock(&p->debug);
psdecref(p);
pprint("unknown qid %#llux in procwrite\n", c->qid.path);
error(Egreg);
}
poperror();
qunlock(&p->debug);
psdecref(p);
return n;
}
static void
procremove(Chan* c)
{
Proc *p;
int pid;
if((p = psincref(SLOT(c->qid))) == nil)
error(Eprocdied);
qlock(&p->debug);
if(waserror()){
qunlock(&p->debug);
psdecref(p);
nexterror();
}
pid = PID(c->qid);
if(p->pid != pid)
error(Eprocdied);
switch(QID(c->qid)){
case Qppid:
errorl(nil, p->parentpid);
break;
case Qnoteid:
errorl(nil, p->noteid);
break;
default:
error(Eperm);
}
}
Dev procdevtab = {
'p',
"proc",
devreset,
procinit,
devshutdown,
procattach,
procwalk,
procstat,
procopen,
devcreate,
procclose,
procread,
devbread,
procwrite,
devbwrite,
procremove,
procwstat,
};
static Chan*
proctext(Chan *c, Proc *p)
{
Chan *tc;
ProcSegment *s;
if(p->state==Dead)
error(Eprocdied);
rlock(&p->seglock);
s = p->seg[TSEG];
if(s == 0){
runlock(&p->seglock);
error(Enonexist);
}
tc = image_chan(s->image);
runlock(&p->seglock);
if(tc == nil)
error(Eprocdied);
if(p->pid != PID(c->qid))
error(Eprocdied);
return tc;
}
void
procstopwait(Proc *p, int ctl)
{
int pid;
if(p->pdbg)
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);
up->psstate = "Stopwait";
if(waserror()) {
qlock(&p->debug);
p->pdbg = 0;
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->l);
qunlock(&p->debug);
cclose(c);
qlock(&p->debug);
lock(&f->l);
}
void
procctlclosefiles(Proc *p, int all, int fd)
{
int i;
Fgrp *f;
f = p->fgrp;
if(f == nil)
error(Eprocdied);
lock(&f->l);
incref(&f->r);
if(all)
for(i = 0; i < f->maxfd; i++)
procctlcloseone(p, f, i);
else
procctlcloseone(p, f, fd);
unlock(&f->l);
closefgrp(f);
}
static void
procctlreq(Proc *p, char *va, int n)
{
int pri;
Cmdbuf *cb;
Cmdtab *ct;
if(p->kp) /* no ctl requests to kprocs */
error(Eperm);
cb = parsecmd(va, n);
if(waserror()){
jehanne_free(cb);
nexterror();
}
ct = lookupcmd(cb, proccmd, nelem(proccmd));
switch(ct->index){
case CMclose:
procctlclosefiles(p, 0, jehanne_atoi(cb->f[1]));
break;
case CMclosefiles:
procctlclosefiles(p, 1, 0);
break;
case CMhang:
p->hang = 1;
break;
case CMkill:
prockill(p, Proc_exitme, "sys: killed");
break;
case CMnohang:
p->hang = 0;
break;
case CMnoswap:
/* obsolete */
break;
case CMpri:
pri = jehanne_atoi(cb->f[1]);
if(pri > PriNormal && !iseve())
error(Eperm);
procpriority(p, pri, 0);
break;
case CMfixedpri:
pri = jehanne_atoi(cb->f[1]);
if(pri > PriNormal && !iseve())
error(Eperm);
procpriority(p, pri, 1);
break;
case CMprivate:
p->privatemem = 1;
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, jehanne_atoi(cb->f[1]));
break;
case CMtrace:
switch(cb->nf){
case 1:
p->trace ^= 1;
break;
case 2:
p->trace = (jehanne_atoi(cb->f[1]) != 0);
break;
default:
error("args");
}
break;
}
poperror();
jehanne_free(cb);
}
static int
procstopped(void *a)
{
Proc *p = a;
return p->state == Stopped;
}
static int
procctlmemio(Proc *p, uintptr_t offset, int n, void *va, int read)
{
char *k;
PagePointer page;
ProcSegment *s;
uint8_t *b;
uintptr_t l, mmuphys = 0;
s = proc_segment(p, offset);
if(s == nil)
error(Ebadarg);
if(offset+n >= s->top)
n = s->top-offset;
if(!segment_fault(&mmuphys, &offset, s, read ? FaultRead : FaultWrite))
error(Ebadarg);
page = segment_page(s, (uintptr_t)offset);
if(page == 0)
error(Ebadarg);
k = page_kmap(page);
l = PGSZ - (offset&(PGSZ-1));
if(n > l)
n = l;
if(waserror()) {
page_kunmap(page, &k);
nexterror();
}
b = (uint8_t*)k;
b += offset&(PGSZ-1);
if(read == 1){
/* caller is reading: the destination buffer is at va
* and it can fault if not yet loaded
*/
jehanne_memmove(va, b, n);
} else {
/* caller is writing: the source buffer is at va */
jehanne_memmove(b, va, n);
}
poperror();
page_kunmap(page, &k);
if(read == 0)
p->newtlb = 1;
return n;
}