| /* | 
 |  * This file is part of the UCB release of Plan 9. It is subject to the license | 
 |  * terms in the LICENSE file found in the top-level directory of this | 
 |  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
 |  * part of the UCB release of Plan 9, including this file, may be copied, | 
 |  * modified, propagated, or distributed except according to the terms contained | 
 |  * in the LICENSE file. | 
 |  */ | 
 |  | 
 | //#define DEBUG | 
 | /* proc on plan 9 has lots of capabilities, some of which we might | 
 |  * want for akaros: | 
 |  * debug control | 
 |  * event tracing | 
 |  * process control (no need for signal system call, etc.) | 
 |  * textual status | 
 |  * rather than excise code that won't work, I'm bracketing it with | 
 |  * #if 0 until we know we don't want it | 
 |  */ | 
 | #include <assert.h> | 
 | #include <cpio.h> | 
 | #include <error.h> | 
 | #include <kmalloc.h> | 
 | #include <kref.h> | 
 | #include <pmap.h> | 
 | #include <ros/vmm.h> | 
 | #include <slab.h> | 
 | #include <smp.h> | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 | #include <umem.h> | 
 |  | 
 | #include <arch/vmm/vmm.h> | 
 |  | 
 | struct dev procdevtab; | 
 |  | 
 | static char *devname(void) | 
 | { | 
 | 	return procdevtab.name; | 
 | } | 
 |  | 
 | enum { Qdir, | 
 |        Qtrace, | 
 |        Qtracepids, | 
 |        Qself, | 
 |        Qns, | 
 |        Qargs, | 
 |        Qctl, | 
 |        Qfd, | 
 |        Qfpregs, | 
 |        Qkregs, | 
 |        Qmaps, | 
 |        Qmem, | 
 |        Qnote, | 
 |        Qnoteid, | 
 |        Qnotepg, | 
 |        Qproc, | 
 |        Qregs, | 
 |        Quser, | 
 |        Qsegment, | 
 |        Qstatus, | 
 |        Qstrace, | 
 |        Qstrace_traceset, | 
 |        Qvmstatus, | 
 |        Qtext, | 
 |        Qwait, | 
 |        Qprofile, | 
 |        Qsyscall, | 
 |        Qcore, | 
 | }; | 
 |  | 
 | enum { CMclose, | 
 |        CMclosefiles, | 
 |        CMhang, | 
 |        CMstraceme, | 
 |        CMstraceall, | 
 |        CMstrace_drop, | 
 | }; | 
 |  | 
 | enum { Nevents = 0x4000, | 
 |        Emask = Nevents - 1, | 
 |        Ntracedpids = 1024, | 
 |        STATSIZE = 8 + 1 + 10 + 1 + 6 + 2, | 
 | }; | 
 |  | 
 | /* | 
 |  * 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 | 
 |  */ | 
 | struct dirtab procdir[] = { | 
 |     {"args", {Qargs}, 0, 0660}, | 
 |     {"ctl", {Qctl}, 0, 0660}, | 
 |     {"fd", {Qfd}, 0, 0444}, | 
 |     {"fpregs", {Qfpregs}, 0, 0000}, | 
 |     //  {"kregs",   {Qkregs},   sizeof(Ureg),       0600}, | 
 |     {"maps", {Qmaps}, 0, 0000}, | 
 |     {"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}, | 
 |     {"user", {Quser}, 0, 0444}, | 
 |     {"segment", {Qsegment}, 0, 0444}, | 
 |     {"status", {Qstatus}, STATSIZE, 0444}, | 
 |     {"strace", {Qstrace}, 0, 0444}, | 
 |     {"strace_traceset", {Qstrace_traceset}, 0, 0666}, | 
 |     {"vmstatus", {Qvmstatus}, 0, 0444}, | 
 |     {"text", {Qtext}, 0, 0000}, | 
 |     {"wait", {Qwait}, 0, 0400}, | 
 |     {"profile", {Qprofile}, 0, 0400}, | 
 |     {"syscall", {Qsyscall}, 0, 0400}, | 
 |     {"core", {Qcore}, 0, 0444}, | 
 | }; | 
 |  | 
 | static struct cmdtab proccmd[] = { | 
 |     {CMclose, "close", 2},         {CMclosefiles, "closefiles", 0}, | 
 |     {CMhang, "hang", 0},           {CMstraceme, "straceme", 0}, | 
 |     {CMstraceall, "straceall", 0}, {CMstrace_drop, "strace_drop", 2}, | 
 | }; | 
 |  | 
 | /* | 
 |  * struct qids are, in path: | 
 |  *	 5 bits of file type (qids above) (old comment said 4 here) | 
 |  *	23 bits of process slot number + 1 (pid + 1 is stored) | 
 |  *	     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 SLOTBITS 23 /* number of bits in the slot */ | 
 | #define QIDMASK ((1 << QSHIFT) - 1) | 
 | #define SLOTMASK (((1 << SLOTBITS) - 1) << QSHIFT) | 
 |  | 
 | #define QID(q) ((((uint32_t)(q).path) & QIDMASK) >> 0) | 
 | #define SLOT(q) (((((uint32_t)(q).path) & SLOTMASK) >> QSHIFT) - 1) | 
 | #define PID(q) ((q).vers) | 
 | #define NOTEID(q) ((q).vers) | 
 |  | 
 | static void procctlreq(struct proc *, char *, size_t); | 
 | static int procctlmemio(struct proc *, uintptr_t, int, void *, int); | 
 | // static struct chan*   proctext(struct chan*, struct proc*); | 
 | // static Segment* txt2data(struct proc*, Segment*); | 
 | // static int    procstopped(void*); | 
 | static void mntscan(struct mntwalk *, struct proc *); | 
 |  | 
 | // static Traceevent *tevents; | 
 | static char *tpids, *tpidsc, *tpidse; | 
 | static spinlock_t tlock; | 
 | static int topens; | 
 | static int tproduced, tconsumed; | 
 | // static void notrace(struct proc*, int, int64_t); | 
 |  | 
 | // void (*proctrace)(struct proc*, int, int64_t) = notrace; | 
 |  | 
 | #if 0 | 
 | static void profclock(Ureg * ur, Timer *) | 
 | { | 
 | 	Tos *tos; | 
 |  | 
 | 	if (up == NULL || current->state != Running) | 
 | 		return; | 
 |  | 
 | 	/* user profiling clock */ | 
 | 	if (userureg(ur)) { | 
 | 		tos = (Tos *) (USTKTOP - sizeof(Tos)); | 
 | 		tos->clock += TK2MS(1); | 
 | 		segclock(userpc(ur)); | 
 | 	} | 
 | } | 
 | #endif | 
 | static int procgen(struct chan *c, char *name, struct dirtab *tab, int unused, | 
 |                    int s, struct dir *dp) | 
 | { | 
 | 	struct qid qid; | 
 | 	struct proc *p; | 
 | 	char *ename; | 
 |  | 
 | 	int pid; | 
 | 	uint32_t path, perm, len; | 
 | 	if (s == DEVDOTDOT) { | 
 | 		mkqid(&qid, Qdir, 0, QTDIR); | 
 | 		devdir(c, qid, devname(), 0, eve.name, 0555, dp); | 
 | 		return 1; | 
 | 	} | 
 |  | 
 | 	if (c->qid.path == Qdir) { | 
 | 		if (s == 0) { | 
 | 			strlcpy(get_cur_genbuf(), "trace", GENBUF_SZ); | 
 | 			mkqid(&qid, Qtrace, -1, QTFILE); | 
 | 			devdir(c, qid, get_cur_genbuf(), 0, eve.name, 0444, dp); | 
 | 			return 1; | 
 | 		} | 
 | 		if (s == 1) { | 
 | 			strlcpy(get_cur_genbuf(), "tracepids", GENBUF_SZ); | 
 | 			mkqid(&qid, Qtracepids, -1, QTFILE); | 
 | 			devdir(c, qid, get_cur_genbuf(), 0, eve.name, 0444, dp); | 
 | 			return 1; | 
 | 		} | 
 | 		if (s == 2) { | 
 | 			p = current; | 
 | 			strlcpy(get_cur_genbuf(), "self", GENBUF_SZ); | 
 | 			mkqid(&qid, (p->pid + 1) << QSHIFT, p->pid, QTDIR); | 
 | 			devdir(c, qid, get_cur_genbuf(), 0, p->user.name, | 
 | 			       DMDIR | 0555, dp); | 
 | 			return 1; | 
 | 		} | 
 | 		s -= 3; | 
 | 		if (name != NULL) { | 
 | 			/* ignore s and use name to find pid */ | 
 | 			pid = strtol(name, &ename, 10); | 
 | 			if (pid <= 0 || ename[0] != '\0') | 
 | 				return -1; | 
 | 			p = pid2proc(pid); | 
 | 			if (!p) | 
 | 				return -1; | 
 | 			/* Need to update s, so that it's the correct 'index' | 
 | 			 * for our proc (aka, the pid).  We use s later when | 
 | 			 * making the qid. */ | 
 | 			s = pid; | 
 | 		} else { | 
 | 			/* This is a shitty iterator, and the list isn't | 
 | 			 * guaranteed to give you the same ordering twice in a | 
 | 			 * row. (procs come and go). */ | 
 | 			p = pid_nth(s); | 
 | 			if (!p) | 
 | 				return -1; | 
 | 			pid = p->pid; | 
 | 		} | 
 |  | 
 | 		snprintf(get_cur_genbuf(), GENBUF_SZ, "%u", pid); | 
 | 		/* | 
 | 		 * String comparison is done in devwalk so | 
 | 		 * name must match its formatted pid. | 
 | 		 */ | 
 | 		if (name != NULL && strcmp(name, get_cur_genbuf()) != 0) { | 
 | 			printk("pid-name mismatch, name: %s, pid %d\n", name, | 
 | 			       pid); | 
 | 			proc_decref(p); | 
 | 			return -1; | 
 | 		} | 
 | 		mkqid(&qid, (s + 1) << QSHIFT, pid, QTDIR); | 
 | 		devdir(c, qid, get_cur_genbuf(), 0, p->user.name, DMDIR | 0555, | 
 | 		       dp); | 
 | 		proc_decref(p); | 
 | 		return 1; | 
 | 	} | 
 | 	if (c->qid.path == Qtrace) { | 
 | 		strlcpy(get_cur_genbuf(), "trace", GENBUF_SZ); | 
 | 		mkqid(&qid, Qtrace, -1, QTFILE); | 
 | 		devdir(c, qid, get_cur_genbuf(), 0, eve.name, 0444, dp); | 
 | 		return 1; | 
 | 	} | 
 | 	if (c->qid.path == Qtracepids) { | 
 | 		strlcpy(get_cur_genbuf(), "tracepids", GENBUF_SZ); | 
 | 		mkqid(&qid, Qtracepids, -1, QTFILE); | 
 | 		devdir(c, qid, get_cur_genbuf(), 0, eve.name, 0444, dp); | 
 | 		return 1; | 
 | 	} | 
 | 	if (s >= ARRAY_SIZE(procdir)) | 
 | 		return -1; | 
 | 	if (tab) | 
 | 		panic("procgen"); | 
 |  | 
 | 	tab = &procdir[s]; | 
 | 	/* path is everything other than the QID part.  Not sure from the orig | 
 | 	 * code | 
 | 	 * if they wanted just the pid part (SLOTMASK) or everything above QID | 
 | 	 */ | 
 | 	path = c->qid.path & ~QIDMASK; /* slot component */ | 
 | 	if ((p = pid2proc(SLOT(c->qid))) == NULL) | 
 | 		return -1; | 
 | 	perm = 0444 | tab->perm; | 
 | #if 0 | 
 | 	if (perm == 0) | 
 | 		perm = p->procmode; | 
 | 	else	/* just copy read bits */ | 
 | 		perm |= p->procmode & 0444; | 
 | #endif | 
 |  | 
 | 	len = tab->length; | 
 | #if 0 | 
 | 	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; | 
 | 	} | 
 | #endif | 
 |  | 
 | 	mkqid(&qid, path | tab->qid.path, c->qid.vers, QTFILE); | 
 | 	devdir(c, qid, tab->name, len, p->user.name, perm, dp); | 
 | 	proc_decref(p); | 
 | 	return 1; | 
 | } | 
 |  | 
 | #if 0 | 
 | static void notrace(struct proc *, Tevent, int64_t) | 
 | { | 
 | } | 
 |  | 
 | static spinlock_t tlck = SPINLOCK_INITIALIZER_IRQSAVE; | 
 |  | 
 | static void _proctrace(struct proc *p, Tevent etype, int64_t ts) | 
 | { | 
 | 	Traceevent *te; | 
 | 	int tp; | 
 |  | 
 | 	ilock(&tlck); | 
 | 	if (p->trace == 0 || topens == 0 || tproduced - tconsumed >= Nevents) { | 
 | 		iunlock(&tlck); | 
 | 		return; | 
 | 	} | 
 | 	tp = tproduced++; | 
 | 	iunlock(&tlck); | 
 |  | 
 | 	te = &tevents[tp & Emask]; | 
 | 	te->pid = p->pid; | 
 | 	te->etype = etype; | 
 | 	if (ts == 0) | 
 | 		te->time = todget(NULL); | 
 | 	else | 
 | 		te->time = ts; | 
 | 	te->core = m->machno; | 
 | } | 
 |  | 
 | void proctracepid(struct proc *p) | 
 | { | 
 | 	if (p->trace == 1 && proctrace != notrace) { | 
 | 		p->trace = 2; | 
 | 		ilock(&tlck); | 
 | 		tpidsc = seprint(tpidsc, tpidse, "%d %s\n", p->pid, p->text); | 
 | 		iunlock(&tlck); | 
 | 	} | 
 | } | 
 |  | 
 | #endif | 
 | static void procinit(void) | 
 | { | 
 | #if 0 | 
 | 	if (conf.nproc >= (SLOTMASK >> QSHIFT) - 1) | 
 | 		printd("warning: too many procs for devproc\n"); | 
 | 	addclock0link((void (*)(void))profclock, 113);	/* Relative prime to HZ */ | 
 | #endif | 
 | } | 
 |  | 
 | static struct chan *procattach(char *spec) | 
 | { | 
 | 	return devattach(devname(), spec); | 
 | } | 
 |  | 
 | static struct walkqid *procwalk(struct chan *c, struct chan *nc, char **name, | 
 |                                 unsigned int nname) | 
 | { | 
 | 	return devwalk(c, nc, name, nname, 0, 0, procgen); | 
 | } | 
 |  | 
 | static size_t procstat(struct chan *c, uint8_t *db, size_t 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(struct proc *p) | 
 | { | 
 | 	return; | 
 | #if 0 | 
 | 	if (p == up) | 
 | 		return; | 
 | 	if (strcmp(current->user.name, "none") != 0) | 
 | 		return; | 
 | 	if (iseve()) | 
 | 		return; | 
 | 	error(EPERM, ERROR_FIXME); | 
 | #endif | 
 | } | 
 |  | 
 | struct bm_helper { | 
 | 	struct sized_alloc *sza; | 
 | 	size_t buflen; | 
 | }; | 
 |  | 
 | static void get_needed_sz_cb(struct vm_region *vmr, void *arg) | 
 | { | 
 | 	struct bm_helper *bmh = (struct bm_helper *)arg; | 
 |  | 
 | 	/* ballpark estimate of a line */ | 
 | 	bmh->buflen += 150; | 
 | } | 
 |  | 
 | static void build_maps_cb(struct vm_region *vmr, void *arg) | 
 | { | 
 | 	struct bm_helper *bmh = (struct bm_helper *)arg; | 
 | 	struct sized_alloc *sza = bmh->sza; | 
 | 	size_t old_sofar; | 
 | 	char path_buf[MAX_FILENAME_SZ]; | 
 | 	char *path; | 
 | 	unsigned long inode_nr; | 
 |  | 
 | 	if (vmr_has_file(vmr)) { | 
 | 		path = foc_abs_path(vmr->__vm_foc); | 
 | 		inode_nr = 0; /* TODO: do we care about this? */ | 
 | 	} else { | 
 | 		strlcpy(path_buf, "[heap]", sizeof(path_buf)); | 
 | 		path = path_buf; | 
 | 		inode_nr = 0; | 
 | 	} | 
 |  | 
 | 	old_sofar = sza->sofar; | 
 | 	sza_printf(sza, "%08lx-%08lx %c%c%c%c %08x %02d:%02d %d ", vmr->vm_base, | 
 | 	           vmr->vm_end, vmr->vm_prot & PROT_READ ? 'r' : '-', | 
 | 	           vmr->vm_prot & PROT_WRITE ? 'w' : '-', | 
 | 	           vmr->vm_prot & PROT_EXEC ? 'x' : '-', | 
 | 	           vmr->vm_flags & MAP_PRIVATE ? 'p' : 's', | 
 | 	           vmr_has_file(vmr) ? vmr->vm_foff : 0, | 
 | 	           vmr_has_file(vmr) ? 1 : 0, /* VFS == 1 for major */ | 
 | 	           0, inode_nr); | 
 | 	/* Align the filename to the 74th char, like Linux (73 chars so far) */ | 
 | 	sza_printf(sza, "%*s", 73 - (sza->sofar - old_sofar), ""); | 
 | 	sza_printf(sza, "%s\n", path); | 
 | } | 
 |  | 
 | static struct sized_alloc *build_maps(struct proc *p) | 
 | { | 
 | 	struct bm_helper bmh[1]; | 
 |  | 
 | 	/* Try to figure out the size needed: start with extra space, then add a | 
 | 	 * bit for each VMR */ | 
 | 	bmh->buflen = 150; | 
 | 	enumerate_vmrs(p, get_needed_sz_cb, bmh); | 
 | 	bmh->sza = sized_kzmalloc(bmh->buflen, MEM_WAIT); | 
 | 	enumerate_vmrs(p, build_maps_cb, bmh); | 
 | 	return bmh->sza; | 
 | } | 
 |  | 
 | static struct chan *procopen(struct chan *c, int omode) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	struct proc *p; | 
 | 	struct pgrp *pg; | 
 | 	struct chan *tc; | 
 | 	int pid; | 
 |  | 
 | 	if (c->qid.type & QTDIR) | 
 | 		return devopen(c, omode, 0, 0, procgen); | 
 |  | 
 | 	if (QID(c->qid) == Qtrace) { | 
 | 		error(ENOSYS, ERROR_FIXME); | 
 | #if 0 | 
 | 		if (omode != OREAD) | 
 | 			error(EPERM, ERROR_FIXME); | 
 | 		lock(&tlock); | 
 | 		if (waserror()) { | 
 | 			unlock(&tlock); | 
 | 			nexterror(); | 
 | 		} | 
 | 		if (topens > 0) | 
 | 			error(EFAIL, "already open"); | 
 | 		topens++; | 
 | 		if (tevents == NULL) { | 
 | 			tevents = (Traceevent *) kzmalloc(sizeof(Traceevent) * Nevents, | 
 | 											  MEM_WAIT); | 
 | 			if (tevents == NULL) | 
 | 				error(ENOMEM, ERROR_FIXME); | 
 | 			tpids = kzmalloc(Ntracedpids * 20, MEM_WAIT); | 
 | 			if (tpids == NULL) { | 
 | 				kfree(tpids); | 
 | 				tpids = NULL; | 
 | 				error(ENOMEM, ERROR_FIXME); | 
 | 			} | 
 | 			tpidsc = tpids; | 
 | 			tpidse = tpids + Ntracedpids * 20; | 
 | 			*tpidsc = 0; | 
 | 			tproduced = tconsumed = 0; | 
 | 		} | 
 | 		proctrace = _proctrace; | 
 | 		poperror(); | 
 | 		unlock(&tlock); | 
 |  | 
 | 		c->mode = openmode(omode); | 
 | 		c->flag |= COPEN; | 
 | 		c->offset = 0; | 
 | 		return c; | 
 | #endif | 
 | 	} | 
 | 	if (QID(c->qid) == Qtracepids) { | 
 | 		error(ENOSYS, ERROR_FIXME); | 
 | #if 0 | 
 | 		if (omode != OREAD) | 
 | 			error(EPERM, ERROR_FIXME); | 
 | 		c->mode = openmode(omode); | 
 | 		c->flag |= COPEN; | 
 | 		c->offset = 0; | 
 | 		return c; | 
 | #endif | 
 | 	} | 
 | 	if ((p = pid2proc(SLOT(c->qid))) == NULL) | 
 | 		error(ESRCH, ERROR_FIXME); | 
 | 	// qlock(&p->debug); | 
 | 	if (waserror()) { | 
 | 		// qunlock(&p->debug); | 
 | 		proc_decref(p); | 
 | 		nexterror(); | 
 | 	} | 
 | 	pid = PID(c->qid); | 
 | 	if (p->pid != pid) | 
 | 		error(ESRCH, ERROR_FIXME); | 
 |  | 
 | 	omode = openmode(omode); | 
 |  | 
 | 	switch (QID(c->qid)) { | 
 | 	case Qtext: | 
 | 		error(ENOSYS, ERROR_FIXME); | 
 | 		/* | 
 | 		                        if (omode != OREAD) | 
 | 		                                error(EPERM, ERROR_FIXME); | 
 | 		                        tc = proctext(c, p); | 
 | 		                        tc->offset = 0; | 
 | 		                        poperror(); | 
 | 		                        qunlock(&p->debug); | 
 | 		                        proc_decref(p); | 
 | 		                        cclose(c); | 
 | 		                        return tc; | 
 | 		*/ | 
 | 	case Qproc: | 
 | 	case Qsegment: | 
 | 	case Qprofile: | 
 | 	case Qfd: | 
 | 		if (omode != O_READ) | 
 | 			error(EPERM, ERROR_FIXME); | 
 | 		break; | 
 |  | 
 | 	case Qnote: | 
 | 		//          if (p->privatemem) | 
 | 		error(EPERM, ERROR_FIXME); | 
 | 		break; | 
 |  | 
 | 	case Qmem: | 
 | 		//          if (p->privatemem) | 
 | 		error(EPERM, ERROR_FIXME); | 
 | 		// nonone(p); | 
 | 		break; | 
 |  | 
 | 	case Qargs: | 
 | 	case Qnoteid: | 
 | 	case Qwait: | 
 | 	case Qregs: | 
 | 	case Qfpregs: | 
 | 	case Qkregs: | 
 | 	case Qsyscall: | 
 | 	case Qcore: | 
 | 		nonone(p); | 
 | 		break; | 
 |  | 
 | 	case Qns: | 
 | 		if (omode != O_READ) | 
 | 			error(EPERM, ERROR_FIXME); | 
 | 		c->aux = kzmalloc(sizeof(struct mntwalk), MEM_WAIT); | 
 | 		break; | 
 | 	case Quser: | 
 | 	case Qstatus: | 
 | 	case Qvmstatus: | 
 | 	case Qctl: | 
 | 		break; | 
 |  | 
 | 	case Qstrace: | 
 | 		if (!p->strace) | 
 | 			error(ENOENT, "Process does not have tracing enabled"); | 
 | 		spin_lock(&p->strace->lock); | 
 | 		if (p->strace->tracing) { | 
 | 			spin_unlock(&p->strace->lock); | 
 | 			error(EBUSY, "Process is already being traced"); | 
 | 		} | 
 | 		/* It's not critical that we reopen before setting tracing, but | 
 | 		 * it's a little cleaner (concurrent syscalls could be trying to | 
 | 		 * use the queue before it was reopened, and they'd throw). */ | 
 | 		qreopen(p->strace->q); | 
 | 		p->strace->tracing = TRUE; | 
 | 		spin_unlock(&p->strace->lock); | 
 | 		/* the ref we are upping is the one we put in __proc_free, which | 
 | 		 * is the one we got from CMstrace{on,me}.  We have a ref on p, | 
 | 		 * so we know we won't free until we decref the proc. */ | 
 | 		kref_get(&p->strace->users, 1); | 
 | 		c->aux = p->strace; | 
 | 		break; | 
 | 	case Qstrace_traceset: | 
 | 		if (!p->strace) | 
 | 			error(ENOENT, "Process does not have tracing enabled"); | 
 | 		kref_get(&p->strace->users, 1); | 
 | 		c->aux = p->strace; | 
 | 		break; | 
 | 	case Qmaps: | 
 | 		c->aux = build_maps(p); | 
 | 		break; | 
 | 	case Qnotepg: | 
 | 		error(ENOSYS, ERROR_FIXME); | 
 | #if 0 | 
 | 			nonone(p); | 
 | 			pg = p->pgrp; | 
 | 			if (pg == NULL) | 
 | 				error(ESRCH, ERROR_FIXME); | 
 | 			if (omode != OWRITE || pg->pgrpid == 1) | 
 | 				error(EPERM, ERROR_FIXME); | 
 | 			c->pgrpid.path = pg->pgrpid + 1; | 
 | 			c->pgrpid.vers = p->noteid; | 
 | #endif | 
 | 		break; | 
 |  | 
 | 	default: | 
 | 		printk("procopen %#llux\n", c->qid.path); | 
 | 		error(EINVAL, ERROR_FIXME); | 
 | 	} | 
 |  | 
 | 	/* 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(); | 
 | 	/* TODO: think about what we really want here.  In akaros, we wouldn't | 
 | 	 * have our pid changed like that. */ | 
 | 	if (p->pid != pid) | 
 | 		error(ESRCH, ERROR_FIXME); | 
 |  | 
 | 	tc = devopen(c, omode, 0, 0, procgen); | 
 | 	poperror(); | 
 | 	// qunlock(&p->debug); | 
 | 	proc_decref(p); | 
 | 	return tc; | 
 | } | 
 |  | 
 | static size_t procwstat(struct chan *c, uint8_t *db, size_t n) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	error(ENOSYS, ERROR_FIXME); | 
 | #if 0 | 
 | 	struct proc *p; | 
 | 	struct dir *d; | 
 |  | 
 | 	if (c->qid.type & QTDIR) | 
 | 		error(EPERM, ERROR_FIXME); | 
 |  | 
 | 	if (QID(c->qid) == Qtrace) | 
 | 		return devwstat(c, db, n); | 
 |  | 
 | 	if ((p = pid2proc(SLOT(c->qid))) == NULL) | 
 | 		error(ESRCH, ERROR_FIXME); | 
 | 	nonone(p); | 
 | 	d = NULL; | 
 | 	qlock(&p->debug); | 
 | 	if (waserror()) { | 
 | 		qunlock(&p->debug); | 
 | 		proc_decref(p); | 
 | 		kfree(d); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	if (p->pid != PID(c->qid)) | 
 | 		error(ESRCH, ERROR_FIXME); | 
 |  | 
 | 	if (strcmp(current->user.name, p->user.name) != 0 && !iseve()) | 
 | 		error(EPERM, ERROR_FIXME); | 
 |  | 
 | 	d = kzmalloc(sizeof(struct dir) + n, MEM_WAIT); | 
 | 	n = convM2D(db, n, &d[0], (char *)&d[1]); | 
 | 	if (n == 0) | 
 | 		error(ENOENT, ERROR_FIXME); | 
 | 	if (!emptystr(d->uid) && strcmp(d->uid, p->user.name) != 0) { | 
 | 		if (!iseve()) | 
 | 			error(EPERM, ERROR_FIXME); | 
 | 		else | 
 | 			proc_set_username(p, d->uid); | 
 | 	} | 
 | 	if (d->mode != -1) | 
 | 		p->procmode = d->mode & 0777; | 
 |  | 
 | 	poperror(); | 
 | 	qunlock(&p->debug); | 
 | 	proc_decref(p); | 
 | 	kfree(d); | 
 |  | 
 | 	return n; | 
 | #endif | 
 | } | 
 |  | 
 | #if 0 | 
 | 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(struct chan *c) | 
 | { | 
 | 	char buf[32]; | 
 |  | 
 | 	return sprint(buf, "%lu", c->qid.vers); | 
 | } | 
 |  | 
 | int procfdprint(struct 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 %4ud (%.16llux %*lud %.2ux) %5ld %8lld %s\n", fd, | 
 | 				&"r w rw"[(c->mode & 3) << 1], c->dev->dc, c->devno, | 
 | 				c->qid.path, w, c->qid.vers, c->qid.type, c->iounit, c->offset, | 
 | 				c->name->s); | 
 | 	return n; | 
 | } | 
 |  | 
 | static int procfds(struct proc *p, char *va, int count, long offset) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	struct fgrp *f; | 
 | 	struct 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 == NULL) { | 
 | 		qunlock(&p->debug); | 
 | 		return 0; | 
 | 	} | 
 | 	lock(f); | 
 | 	if (waserror()) { | 
 | 		unlock(f); | 
 | 		qunlock(&p->debug); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	n = readstr(0, a, count, p->dot->name->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 == NULL) | 
 | 			continue; | 
 | 		ww = procqidwidth(c); | 
 | 		if (ww > w) | 
 | 			w = ww; | 
 | 	} | 
 | 	for (i = 0; i <= f->maxfd; i++) { | 
 | 		c = f->fd[i]; | 
 | 		if (c == NULL) | 
 | 			continue; | 
 | 		n += procfdprint(c, i, w, a + n, count - n); | 
 | 		offset = procoffset(offset, a, &n); | 
 | 	} | 
 | 	poperror(); | 
 | 	unlock(f); | 
 | 	qunlock(&p->debug); | 
 |  | 
 | 	/* copy result to user space, now that locks are released */ | 
 | 	memmove(va, buf, n); | 
 |  | 
 | 	return n; | 
 | } | 
 | #endif | 
 | static void procclose(struct chan *c) | 
 | { | 
 | 	if (QID(c->qid) == Qtrace) { | 
 | 		spin_lock(&tlock); | 
 | 		if (topens > 0) | 
 | 			topens--; | 
 | 		/* ?? | 
 | 		   if(topens == 0) | 
 | 		   proctrace = notrace; | 
 | 		 */ | 
 | 		spin_unlock(&tlock); | 
 | 	} | 
 | 	if (QID(c->qid) == Qsyscall) { | 
 | 		if (c->aux) | 
 | 			qclose(c->aux); | 
 | 		c->aux = NULL; | 
 | 	} | 
 | 	if (QID(c->qid) == Qns && c->aux != 0) | 
 | 		kfree(c->aux); | 
 | 	if (QID(c->qid) == Qmaps && c->aux != 0) | 
 | 		kfree(c->aux); | 
 | 	if (QID(c->qid) == Qstrace && c->aux != 0) { | 
 | 		struct strace *s = c->aux; | 
 |  | 
 | 		assert(c->flag & COPEN); /* only way aux should have been set */ | 
 | 		s->tracing = FALSE; | 
 | 		qhangup(s->q, NULL); | 
 | 		kref_put(&s->users); | 
 | 		c->aux = NULL; | 
 | 	} | 
 | 	if (QID(c->qid) == Qstrace_traceset && c->aux != 0) { | 
 | 		struct strace *s = c->aux; | 
 |  | 
 | 		assert(c->flag & COPEN); | 
 | 		kref_put(&s->users); | 
 | 		c->aux = NULL; | 
 | 	} | 
 | } | 
 |  | 
 | 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'; | 
 | } | 
 |  | 
 | #if 0 | 
 | static char *argcpy(char *s, char *p) | 
 | { | 
 | 	char *t, *tp, *te; | 
 | 	int n; | 
 |  | 
 | 	n = p - s; | 
 | 	if (n > 128) | 
 | 		n = 128; | 
 | 	if (n <= 0) { | 
 | 		t = kzmalloc(1, MEM_WAIT); | 
 | 		*t = 0; | 
 | 		return t; | 
 | 	} | 
 | 	t = kzmalloc(n, MEM_WAIT); | 
 | 	tp = t; | 
 | 	te = t + n; | 
 |  | 
 | 	while (tp + 1 < te) { | 
 | 		for (p--; p > s && p[-1] != 0; p--) ; | 
 | 		tp = seprint(tp, te, "%q ", p); | 
 | 		if (p == s) | 
 | 			break; | 
 | 	} | 
 | 	if (*tp == ' ') | 
 | 		*tp = 0; | 
 | 	return t; | 
 | } | 
 |  | 
 | static int procargs(struct proc *p, char *buf, int nbuf) | 
 | { | 
 | 	char *s; | 
 |  | 
 | 	if (p->setargs == 0) { | 
 | 		s = argcpy(p->args, p->args + p->nargs); | 
 | 		kfree(p->args); | 
 | 		p->nargs = strlen(s); | 
 | 		p->args = s; | 
 | 		p->setargs = 1; | 
 | 	} | 
 | 	return snprint(buf, nbuf, "%s", p->args); | 
 | } | 
 |  | 
 | static int eventsavailable(void *) | 
 | { | 
 | 	return tproduced > tconsumed; | 
 | } | 
 | #endif | 
 |  | 
 | static size_t procread(struct chan *c, void *va, size_t n, off64_t off) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	struct proc *p; | 
 | 	long l, r; | 
 | 	int i, j, navail, pid, rsize; | 
 | 	char flag[10], *sps, *srv; | 
 | 	uintptr_t offset, u; | 
 | 	int tesz; | 
 | 	uint8_t *rptr; | 
 | 	struct mntwalk *mw; | 
 | 	struct strace *s; | 
 | 	struct sized_alloc *sza; | 
 |  | 
 | 	if (c->qid.type & QTDIR) { | 
 | 		int nn; | 
 | 		printd("procread: dir\n"); | 
 | 		nn = devdirread(c, va, n, 0, 0, procgen); | 
 | 		printd("procread: %d\n", nn); | 
 | 		return nn; | 
 | 	} | 
 |  | 
 | 	offset = off; | 
 | 	/* Some shit in proc doesn't need to grab the reference.  For strace, we | 
 | 	 * already have the chan open, and all we want to do is read the queue, | 
 | 	 * which exists because of our kref on it. */ | 
 | 	switch (QID(c->qid)) { | 
 | 	case Qstrace: | 
 | 		s = c->aux; | 
 | 		n = qread(s->q, va, n); | 
 | 		return n; | 
 | 	case Qstrace_traceset: | 
 | 		s = c->aux; | 
 | 		return readmem(offset, va, n, s->trace_set, | 
 | 		               bitmap_size(MAX_SYSCALL_NR)); | 
 | 	} | 
 |  | 
 | 	if ((p = pid2proc(SLOT(c->qid))) == NULL) | 
 | 		error(ESRCH, "%d: no such process", SLOT(c->qid)); | 
 | 	if (p->pid != PID(c->qid)) { | 
 | 		proc_decref(p); | 
 | 		error(ESRCH, "weird: p->pid is %d, PID(c->qid) is %d: mismatch", | 
 | 		      p->pid, PID(c->qid)); | 
 | 	} | 
 | 	switch (QID(c->qid)) { | 
 | 	default: | 
 | 		proc_decref(p); | 
 | 		break; | 
 | 	case Quser: { | 
 | 		int i; | 
 |  | 
 | 		i = readstr(off, va, n, p->user.name); | 
 | 		proc_decref(p); | 
 | 		return i; | 
 | 	} | 
 | 	case Qstatus: { | 
 | 		/* the old code grew the stack and was hideous. | 
 | 		 * status is not a high frequency operation; just malloc. */ | 
 | 		char *buf = kmalloc(4096, MEM_WAIT); | 
 | 		char *s = buf, *e = buf + 4096; | 
 | 		int i; | 
 |  | 
 | 		s = seprintf(s, e, "%8d %-*s %-10s %6d", p->pid, | 
 | 		             PROC_PROGNAME_SZ, p->progname, | 
 | 		             procstate2str(p->state), p->ppid); | 
 | 		if (p->strace) | 
 | 			s = seprintf(s, e, " %d trace users %d traced procs", | 
 | 			             kref_refcnt(&p->strace->users), | 
 | 			             kref_refcnt(&p->strace->procs)); | 
 | 		proc_decref(p); | 
 | 		i = readstr(off, va, n, buf); | 
 | 		kfree(buf); | 
 | 		return i; | 
 | 	} | 
 |  | 
 | 	case Qvmstatus: { | 
 | 		size_t buflen = 50 * 65 + 2; | 
 | 		char *buf = kmalloc(buflen, MEM_WAIT); | 
 | 		int i, offset; | 
 | 		offset = 0; | 
 | 		offset += snprintf(buf + offset, buflen - offset, "{\n"); | 
 | 		for (i = 0; i < 65; i++) { | 
 | 			if (p->vmm.vmexits[i] != 0) { | 
 | 				offset += | 
 | 				    snprintf(buf + offset, buflen - offset, | 
 | 				             "\"%s\":\"%lld\",\n", | 
 | 				             VMX_EXIT_REASON_NAMES[i], | 
 | 				             p->vmm.vmexits[i]); | 
 | 			} | 
 | 		} | 
 | 		offset += snprintf(buf + offset, buflen - offset, "}\n"); | 
 | 		proc_decref(p); | 
 | 		n = readstr(off, va, n, buf); | 
 | 		kfree(buf); | 
 | 		return n; | 
 | 	} | 
 | 	case Qns: | 
 | 		// qlock(&p->debug); | 
 | 		if (waserror()) { | 
 | 			// qunlock(&p->debug); | 
 | 			proc_decref(p); | 
 | 			nexterror(); | 
 | 		} | 
 | 		if (p->pgrp == NULL || p->pid != PID(c->qid)) | 
 | 			error(ESRCH, ERROR_FIXME); | 
 | 		mw = c->aux; | 
 | 		if (mw->cddone) { | 
 | 			poperror(); | 
 | 			// qunlock(&p->debug); | 
 | 			proc_decref(p); | 
 | 			return 0; | 
 | 		} | 
 | 		mntscan(mw, p); | 
 | 		if (mw->mh == 0) { | 
 | 			mw->cddone = 1; | 
 | 			i = snprintf(va, n, "cd %s\n", p->dot->name->s); | 
 | 			poperror(); | 
 | 			// qunlock(&p->debug); | 
 | 			proc_decref(p); | 
 | 			return i; | 
 | 		} | 
 | 		int2flag(mw->cm->mflag, flag); | 
 | 		if (strcmp(mw->cm->to->name->s, "#mnt") == 0) { | 
 | 			srv = srvname(mw->cm->to->mchan); | 
 | 			i = snprintf(va, n, "mount %s %s %s %s\n", flag, | 
 | 			             srv == NULL ? mw->cm->to->mchan->name->s | 
 | 			                         : srv, | 
 | 			             mw->mh->from->name->s, | 
 | 			             mw->cm->spec ? mw->cm->spec : ""); | 
 | 			kfree(srv); | 
 | 		} else | 
 | 			i = snprintf(va, n, "bind %s %s %s\n", flag, | 
 | 			             mw->cm->to->name->s, | 
 | 			             mw->mh->from->name->s); | 
 | 		poperror(); | 
 | 		// qunlock(&p->debug); | 
 | 		proc_decref(p); | 
 | 		return i; | 
 | 	case Qmaps: | 
 | 		sza = c->aux; | 
 | 		i = readstr(off, va, n, sza->buf); | 
 | 		proc_decref(p); | 
 | 		return i; | 
 | 	} | 
 | 	error(EINVAL, "QID %d did not match any QIDs for #proc", QID(c->qid)); | 
 | 	return 0; /* not reached */ | 
 | } | 
 |  | 
 | static void mntscan(struct mntwalk *mw, struct proc *p) | 
 | { | 
 | 	struct pgrp *pg; | 
 | 	struct mount *t; | 
 | 	struct 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 size_t procwrite(struct chan *c, void *va, size_t n, off64_t off) | 
 | { | 
 | 	ERRSTACK(2); | 
 |  | 
 | 	struct proc *p, *t; | 
 | 	int i, id, l; | 
 | 	char *args; | 
 | 	uintptr_t offset = off; | 
 | 	struct strace *s; | 
 |  | 
 | 	if (c->qid.type & QTDIR) | 
 | 		error(EISDIR, ERROR_FIXME); | 
 |  | 
 | 	if ((p = pid2proc(SLOT(c->qid))) == NULL) | 
 | 		error(ESRCH, ERROR_FIXME); | 
 |  | 
 | 	if (waserror()) { | 
 | 		proc_decref(p); | 
 | 		nexterror(); | 
 | 	} | 
 | 	if (p->pid != PID(c->qid)) | 
 | 		error(ESRCH, ERROR_FIXME); | 
 |  | 
 | 	offset = off; | 
 |  | 
 | 	switch (QID(c->qid)) { | 
 | #if 0 | 
 | 		case Qargs: | 
 | 			if (n == 0) | 
 | 				error(EINVAL, ERROR_FIXME); | 
 | 			if (n >= sizeof buf - strlen(p->text) - 1) | 
 | 				error(E2BIG, ERROR_FIXME); | 
 | 			l = snprintf(buf, sizeof buf, "%s [%s]", p->text, (char *)va); | 
 | 			args = kzmalloc(l + 1, MEM_WAIT); | 
 | 			if (args == NULL) | 
 | 				error(ENOMEM, ERROR_FIXME); | 
 | 			memmove(args, buf, l); | 
 | 			args[l] = 0; | 
 | 			kfree(p->args); | 
 | 			p->nargs = l; | 
 | 			p->args = args; | 
 | 			p->setargs = 1; | 
 | 			break; | 
 |  | 
 | 		case Qmem: | 
 | 			if (p->state != Stopped) | 
 | 				error(EINVAL, ERROR_FIXME); | 
 |  | 
 | 			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(ENODATA, ERROR_FIXME); | 
 | 			setregisters(p->dbgreg, (char *)(p->dbgreg) + offset, va, n); | 
 | 			break; | 
 |  | 
 | 		case Qfpregs: | 
 | 			n = fpudevprocio(p, va, n, offset, 1); | 
 | 			break; | 
 | #endif | 
 | 	case Qctl: | 
 | 		procctlreq(p, va, n); | 
 | 		break; | 
 | 	case Qstrace_traceset: | 
 | 		s = c->aux; | 
 | 		if (n + offset > bitmap_size(MAX_SYSCALL_NR)) | 
 | 			error(EINVAL, | 
 | 			      "strace_traceset: Short write (%llu at off %llu)", | 
 | 			      n, offset); | 
 | 		if (memcpy_from_user(current, (void *)s->trace_set + offset, va, | 
 | 		                     n)) | 
 | 			error(EFAULT, "strace_traceset: Bad addr (%p + %llu)", | 
 | 			      va, n); | 
 | 		break; | 
 | 	default: | 
 | 		error(EFAIL, "unknown qid %#llux in procwrite\n", c->qid.path); | 
 | 	} | 
 | 	poperror(); | 
 | 	proc_decref(p); | 
 | 	return n; | 
 | } | 
 |  | 
 | struct dev procdevtab __devtab = { | 
 |     .name = "proc", | 
 |  | 
 |     .reset = devreset, | 
 |     .init = procinit, | 
 |     .shutdown = devshutdown, | 
 |     .attach = procattach, | 
 |     .walk = procwalk, | 
 |     .stat = procstat, | 
 |     .open = procopen, | 
 |     .create = devcreate, | 
 |     .close = procclose, | 
 |     .read = procread, | 
 |     .bread = devbread, | 
 |     .write = procwrite, | 
 |     .bwrite = devbwrite, | 
 |     .remove = devremove, | 
 |     .wstat = procwstat, | 
 |     .power = devpower, | 
 |     .chaninfo = devchaninfo, | 
 | }; | 
 |  | 
 | #if 0 | 
 | static struct chan *proctext(struct chan *c, struct proc *p) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	struct chan *tc; | 
 | 	Image *i; | 
 | 	Segment *s; | 
 |  | 
 | 	s = p->seg[TSEG]; | 
 | 	if (s == 0) | 
 | 		error(ENOENT, ERROR_FIXME); | 
 | 	if (p->state == Dead) | 
 | 		error(ESRCH, ERROR_FIXME); | 
 |  | 
 | 	lock(s); | 
 | 	i = s->image; | 
 | 	if (i == 0) { | 
 | 		unlock(s); | 
 | 		error(ESRCH, ERROR_FIXME); | 
 | 	} | 
 | 	unlock(s); | 
 |  | 
 | 	lock(i); | 
 | 	if (waserror()) { | 
 | 		unlock(i); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	tc = i->c; | 
 | 	if (tc == 0) | 
 | 		error(ESRCH, ERROR_FIXME); | 
 |  | 
 | 	/* TODO: what do you want here?  you can't get a kref and have the new val | 
 | 	 * be 1.  Here is the old code: if (kref_get(&tc->ref, 1) == 1 || ... ) */ | 
 | 	if (kref_refcnt(&tc->ref, 1) == 1 || (tc->flag & COPEN) == 0 | 
 | 		|| tc->mode != OREAD) { | 
 | 		cclose(tc); | 
 | 		error(ESRCH, ERROR_FIXME); | 
 | 	} | 
 |  | 
 | 	if (p->pid != PID(c->qid)) { | 
 | 		cclose(tc); | 
 | 		error(ESRCH, ERROR_FIXME); | 
 | 	} | 
 |  | 
 | 	poperror(); | 
 | 	unlock(i); | 
 |  | 
 | 	return tc; | 
 | } | 
 |  | 
 | /* TODO: this will fail at compile time, since we don't have a proc-wide rendez, | 
 |  * among other things, and we'll need to rewrite this for akaros */ | 
 | void procstopwait(struct proc *p, int ctl) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	int pid; | 
 |  | 
 | 	if (p->pdbg) | 
 | 		error(EBUSY, ERROR_FIXME); | 
 | 	if (procstopped(p) || p->state == Broken) | 
 | 		return; | 
 |  | 
 | 	if (ctl != 0) | 
 | 		p->procctl = ctl; | 
 | 	p->pdbg = up; | 
 | 	pid = p->pid; | 
 | 	qunlock(&p->debug); | 
 | 	current->psstate = "Stopwait"; | 
 | 	if (waserror()) { | 
 | 		p->pdbg = 0; | 
 | 		qlock(&p->debug); | 
 | 		nexterror(); | 
 | 	} | 
 | 	rendez_sleep(¤t->sleep, procstopped, p); | 
 | 	poperror(); | 
 | 	qlock(&p->debug); | 
 | 	if (p->pid != pid) | 
 | 		error(ESRCH, ERROR_FIXME); | 
 | } | 
 |  | 
 | #endif | 
 | static void procctlcloseone(struct proc *p, int fd) | 
 | { | 
 | 	// TODO: resolve this and sys_close | 
 | 	sysclose(fd); | 
 | 	return; | 
 | } | 
 |  | 
 | void procctlclosefiles(struct proc *p, int all, int fd) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	if (all) | 
 | 		for (i = 0; i < NR_FILE_DESC_MAX; i++) | 
 | 			procctlcloseone(p, i); | 
 | 	else | 
 | 		procctlcloseone(p, fd); | 
 | } | 
 |  | 
 | static void strace_shutdown(struct kref *a) | 
 | { | 
 | 	struct strace *strace = container_of(a, struct strace, procs); | 
 | 	static const char base_msg[] = "# Traced ~%lu syscs, Dropped %lu"; | 
 | 	size_t msg_len = NUMSIZE64 * 2 + sizeof(base_msg); | 
 | 	char *msg = kmalloc(msg_len, 0); | 
 |  | 
 | 	if (msg) | 
 | 		snprintf(msg, msg_len, base_msg, strace->appx_nr_sysc, | 
 | 		         atomic_read(&strace->nr_drops)); | 
 | 	qhangup(strace->q, msg); | 
 | 	kfree(msg); | 
 | } | 
 |  | 
 | static void strace_release(struct kref *a) | 
 | { | 
 | 	struct strace *strace = container_of(a, struct strace, users); | 
 |  | 
 | 	qfree(strace->q); | 
 | 	kfree(strace); | 
 | } | 
 |  | 
 | static void procctlreq(struct proc *p, char *va, size_t n) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	int8_t irq_state = 0; | 
 | 	int npc, pri, core; | 
 | 	struct cmdbuf *cb; | 
 | 	struct cmdtab *ct; | 
 | 	int64_t time; | 
 | 	char *e; | 
 | 	struct strace *strace; | 
 |  | 
 | 	cb = parsecmd(va, n); | 
 | 	if (waserror()) { | 
 | 		kfree(cb); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	ct = lookupcmd(cb, proccmd, ARRAY_SIZE(proccmd)); | 
 |  | 
 | 	switch (ct->index) { | 
 | 	case CMstraceall: | 
 | 	case CMstraceme: | 
 | 	case CMstrace_drop: | 
 | 		/* common allocation.  if we inherited, we might have one | 
 | 		 * already */ | 
 | 		if (!p->strace) { | 
 | 			strace = kzmalloc(sizeof(*p->strace), MEM_WAIT); | 
 | 			spinlock_init(&strace->lock); | 
 | 			bitmap_set(strace->trace_set, 0, MAX_SYSCALL_NR); | 
 | 			strace->q = qopen(65536, Qmsg, NULL, NULL); | 
 | 			/* The queue is reopened and hungup whenever we open the | 
 | 			 * Qstrace file.  This hangup might not be necessary, | 
 | 			 * but is safer. */ | 
 | 			qhangup(strace->q, NULL); | 
 | 			/* both of these refs are put when the proc is freed. | 
 | 			 * procs is for every process that has this p->strace. | 
 | 			 * users is procs + every user (e.g. from open()). | 
 | 			 * | 
 | 			 * it is possible to kref_put the procs kref in | 
 | 			 * proc_destroy, which would make strace's job easier | 
 | 			 * (no need to do an async wait on the child), and we | 
 | 			 * wouldn't need to decref p in procread(Qstrace).  But | 
 | 			 * the downside is that proc_destroy races | 
 | 			 * with us here with the kref initialization. */ | 
 | 			kref_init(&strace->procs, strace_shutdown, 1); | 
 | 			kref_init(&strace->users, strace_release, 1); | 
 | 			if (!atomic_cas_ptr((void **)&p->strace, 0, strace)) { | 
 | 				/* someone else won the race and installed | 
 | 				 * strace. */ | 
 | 				qfree(strace->q); | 
 | 				kfree(strace); | 
 | 				error(EAGAIN, | 
 | 				      "Concurrent strace init, try again"); | 
 | 			} | 
 | 		} | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	/* actually do the command. */ | 
 | 	switch (ct->index) { | 
 | 	default: | 
 | 		error(EFAIL, "Command not implemented"); | 
 | 		break; | 
 | 	case CMclose: | 
 | 		procctlclosefiles(p, 0, atoi(cb->f[1])); | 
 | 		break; | 
 | 	case CMclosefiles: | 
 | 		procctlclosefiles(p, 1, 0); | 
 | 		break; | 
 | #if 0 | 
 | 		we may want this.Let us pause a proc.case CMhang:p->hang = 1; | 
 | 		break; | 
 | #endif | 
 | 	case CMstraceme: | 
 | 		p->strace->inherit = FALSE; | 
 | 		break; | 
 | 	case CMstraceall: | 
 | 		p->strace->inherit = TRUE; | 
 | 		break; | 
 | 	case CMstrace_drop: | 
 | 		if (!strcmp(cb->f[1], "on")) | 
 | 			p->strace->drop_overflow = TRUE; | 
 | 		else if (!strcmp(cb->f[1], "off")) | 
 | 			p->strace->drop_overflow = FALSE; | 
 | 		else | 
 | 			error(EINVAL, "strace_drop takes on|off %s", cb->f[1]); | 
 | 		break; | 
 | 	} | 
 | 	poperror(); | 
 | 	kfree(cb); | 
 | } | 
 |  | 
 | #if 0 | 
 | static int procstopped(void *a) | 
 | { | 
 | 	struct proc *p = a; | 
 | 	return p->state == Stopped; | 
 | } | 
 |  | 
 | static int | 
 | procctlmemio(struct proc *p, uintptr_t offset, int n, void *va, int read) | 
 | { | 
 | 	KMap *k; | 
 | 	Pte *pte; | 
 | 	Page *pg; | 
 | 	Segment *s; | 
 | 	uintptr_t soff, l;			/* hmmmm */ | 
 | 	uint8_t *b; | 
 | 	uintmem pgsz; | 
 |  | 
 | 	for (;;) { | 
 | 		s = seg(p, offset, 1); | 
 | 		if (s == 0) | 
 | 			error(EINVAL, ERROR_FIXME); | 
 |  | 
 | 		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, s->color) == 0) | 
 | 			break; | 
 | 		poperror(); | 
 | 		s->steal--; | 
 | 	} | 
 | 	poperror(); | 
 | 	pte = s->map[soff / PTEMAPMEM]; | 
 | 	if (pte == 0) | 
 | 		panic("procctlmemio"); | 
 | 	pgsz = m->pgsz[s->pgszi]; | 
 | 	pg = pte->pages[(soff & (PTEMAPMEM - 1)) / pgsz]; | 
 | 	if (pagedout(pg)) | 
 | 		panic("procctlmemio1"); | 
 |  | 
 | 	l = pgsz - (offset & (pgsz - 1)); | 
 | 	if (n > l) | 
 | 		n = l; | 
 |  | 
 | 	k = kmap(pg); | 
 | 	if (waserror()) { | 
 | 		s->steal--; | 
 | 		kunmap(k); | 
 | 		nexterror(); | 
 | 	} | 
 | 	b = (uint8_t *) VA(k); | 
 | 	b += offset & (pgsz - 1); | 
 | 	if (read == 1) | 
 | 		memmove(va, b, n);	/* This can fault */ | 
 | 	else | 
 | 		memmove(b, va, n); | 
 | 	poperror(); | 
 | 	kunmap(k); | 
 |  | 
 | 	/* 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; | 
 | } | 
 |  | 
 | static Segment *txt2data(struct proc *p, Segment * s) | 
 | { | 
 | 	int i; | 
 | 	Segment *ps; | 
 |  | 
 | 	ps = newseg(SG_DATA, s->base, s->size); | 
 | 	ps->image = s->image; | 
 | 	kref_get(&ps->image->ref, 1); | 
 | 	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; | 
 | 	kref_get(&ps->image->ref, 1); | 
 | 	ps->fstart = s->fstart; | 
 | 	ps->flen = s->flen; | 
 | 	ps->flushme = 1; | 
 |  | 
 | 	return ps; | 
 | } | 
 | #endif |