#include <9pm/u.h>
#include <9pm/libc.h>
#include <9pm/fcall.h>
#include "dat.h"
#include "fns.h"
enum
{
Qdir,
Qargs,
Qctl,
Qfd,
Qnote,
Qnoteid,
Qnotepg,
Qns,
Qproc,
Qstatus,
Qwait,
};
enum
{
CMclose,
CMclosefiles,
CMfixedpri,
CMhang,
CMkill,
CMnohang,
CMpri,
CMprivate,
CMprofile,
CMstart,
CMstartstop,
CMstop,
CMwaitstop,
CMwired,
};
#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, 0440,
"ctl", {Qctl}, 0, 0000,
"fd", {Qfd}, 0, 0444,
"note", {Qnote}, 0, 0000,
"noteid", {Qnoteid}, 0, 0664,
"notepg", {Qnotepg}, 0, 0000,
"ns", {Qns}, 0, 0444,
"proc", {Qproc}, 0, 0400,
"status", {Qstatus}, STATSIZE, 0444,
"wait", {Qwait}, 0, 0400,
};
static
Cmdtab proccmd[] = {
CMclose, "close", 2,
CMclosefiles, "closefiles", 1,
CMfixedpri, "fixedpri", 2,
CMhang, "hang", 1,
CMnohang, "nohang", 1,
CMkill, "kill", 1,
CMpri, "pri", 2,
CMprivate, "private", 1,
CMprofile, "profile", 1,
CMstart, "start", 1,
CMstartstop, "startstop", 1,
CMstop, "stop", 1,
CMwaitstop, "waitstop", 1,
CMwired, "wired", 2,
};
/*
* 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*);
int procstopped(void*);
void mntscan(Mntwalk*, Proc*);
static int
procgen(Chan *c, char *name, Dirtab *tab, int z, int s, Dir *dp)
{
Qid qid;
Proc *p;
char *ename;
ulong pid, path, perm, len;
USED(z);
if(s == DEVDOTDOT){
mkqid(&qid, Qdir, 0, QTDIR);
devdir(c, qid, "#p", 0, eve, 0555, dp);
return 1;
}
if(c->qid.path == Qdir){
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 >= procalloc.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(s >= nelem(procdir))
return -1;
if(tab)
panic("procgen");
tab = &procdir[s];
path = c->qid.path&~(((1<<QSHIFT)-1)); /* slot component */
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;
}
mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
devdir(c, qid, tab->name, len, p->user, perm, dp);
return 1;
}
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);
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 Qproc:
case Qfd:
if(omode != OREAD)
error(Eperm);
break;
case Qargs:
case Qctl:
case Qnote:
case Qnoteid:
case Qstatus:
case Qwait:
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", 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);
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);
}
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->name->s);
return n;
}
static int
procfds(Proc *p, char *va, int count, long offset)
{
Fgrp *f;
Chan *c;
int n, i, w, ww;
qlock(&p->debug);
f = p->fgrp;
if(f == nil){
qunlock(&p->debug);
return 0;
}
lock(&f->lk);
if(waserror()){
unlock(&f->lk);
qunlock(&p->debug);
nexterror();
}
n = readstr(0, va, count, p->dot->name->s);
n += snprint(va+n, count-n, "\n");
offset = procoffset(offset, va, &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, va+n, count-n);
offset = procoffset(offset, va, &n);
}
unlock(&f->lk);
qunlock(&p->debug);
poperror();
return n;
}
static void
procclose(Chan * c)
{
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;
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 long
procread(Chan *c, void *va, long n, vlong off)
{
int m;
long l;
Proc *p;
Waitq *wq;
Mntwalk *mw;
char *a = va, *sps;
int i, j, pid;
char *srv, statbuf[256], flag[10];
ulong offset = off;
if(c->qid.type & QTDIR)
return devdirread(c, a, n, 0, 0, procgen);
p = proctab(SLOT(c->qid));
if(p->pid != PID(c->qid))
error(Eprocdied);
switch(QID(c->qid)){
case Qargs:
j = procargs(p, p->genbuf, sizeof p->genbuf);
if(offset >= j)
return 0;
if(offset+n > j)
n = j-offset;
memmove(a, &p->genbuf[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 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 = machp0->ticks - l;
l = TK2MS(l);
readnum(0, statbuf+j+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
}
readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, p->bss, 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 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);
rendsleep(&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->name->s);
qunlock(&p->debug);
poperror();
return i;
}
int2flag(mw->cm->mflag, flag);
if(strcmp(mw->cm->to->name->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->name->s : srv,
mw->mh->from->name->s, mw->cm->spec? mw->cm->spec : "");
free(srv);
}else
i = snprint(a, n, "bind %s %s %s\n", flag,
mw->cm->to->name->s, mw->mh->from->name->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;
Proc *p, **t, **et;
char *a, 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 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 = procalloc.arena;
for(et = t+procalloc.nproc; t < et; t++) {
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 devproc = {
'p',
"proc",
devreset,
procattach,
procwalk,
procstat,
procopen,
devcreate,
procclose,
procread,
devbread,
procwrite,
devbwrite,
devremove,
procwstat,
};
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();
}
rendsleep(&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->lk);
qunlock(&p->debug);
cclose(c);
qlock(&p->debug);
lock(&f->lk);
}
void
procctlclosefiles(Proc *p, int all, int fd)
{
int i;
Fgrp *f;
f = p->fgrp;
if(f == nil)
error(Eprocdied);
lock(&f->lk);
f->ref.ref++;
if(all)
for(i = 0; i < f->maxfd; i++)
procctlcloseone(p, f, i);
else
procctlcloseone(p, f, fd);
unlock(&f->lk);
closefgrp(f);
}
void
procctlreq(Proc *p, char *va, int n)
{
int i;
Cmdbuf *cb;
Cmdtab *ct;
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 CMfixedpri:
i = atoi(cb->f[1]);
if(i < 0)
i = 0;
if(i >= Nrq)
i = Nrq - 1;
if(i > p->basepri && !iseve())
error(Eperm);
p->basepri = i;
p->fixedpri = 1;
break;
case CMhang:
p->hang = 1;
break;
case CMkill:
switch(p->state) {
case Stopped:
postnote(p, 0, "sys: killed", NExit);
p->procctl = Proc_exitme;
ready(p);
break;
default:
postnote(p, 0, "sys: killed", NExit);
p->procctl = Proc_exitme;
}
break;
case CMnohang:
p->hang = 0;
break;
case CMpri:
i = atoi(cb->f[1]);
if(i < 0)
i = 0;
if(i >= Nrq)
i = Nrq - 1;
if(i > p->basepri && !iseve())
error(Eperm);
p->basepri = i;
p->fixedpri = 0;
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 CMstop:
procstopwait(p, Proc_stopme);
break;
case CMwaitstop:
procstopwait(p, 0);
break;
case CMwired:
error(Ebadctl);
/* procwired(p, atoi(cb->f[1])); */
break;
}
poperror();
free(cb);
}
int
procstopped(void *a)
{
Proc *p = a;
return p->state == Stopped;
}
syntax highlighted by Code2HTML, v. 0.9.1