4 #include "../port/lib.h"
8 #include "../port/error.h"
75 #define STATSIZE (2*KNAMELEN+12+9*12)
77 * Status, fd, and ns are left fully readable (0444) because of their use in debugging,
78 * particularly on shared servers.
79 * Arguably, ns and fd shouldn't be readable; if you'd prefer, change them to 0000
83 "args", {Qargs}, 0, 0660,
84 "ctl", {Qctl}, 0, 0000,
86 "fpregs", {Qfpregs}, sizeof(FPsave), 0000,
87 "kregs", {Qkregs}, sizeof(Ureg), 0400,
88 "mem", {Qmem}, 0, 0000,
89 "note", {Qnote}, 0, 0000,
90 "noteid", {Qnoteid}, 0, 0664,
91 "notepg", {Qnotepg}, 0, 0000,
93 "ppid", {Qppid}, 0, 0444,
94 "proc", {Qproc}, 0, 0400,
95 "regs", {Qregs}, sizeof(Ureg), 0000,
96 "segment", {Qsegment}, 0, 0444,
97 "status", {Qstatus}, STATSIZE, 0444,
98 "text", {Qtext}, 0, 0000,
99 "wait", {Qwait}, 0, 0400,
100 "profile", {Qprofile}, 0, 0400,
101 "syscall", {Qsyscall}, 0, 0400,
107 CMclosefiles, "closefiles", 1,
108 CMfixedpri, "fixedpri", 2,
110 CMnohang, "nohang", 1,
111 CMnoswap, "noswap", 1,
114 CMprivate, "private", 1,
115 CMprofile, "profile", 1,
117 CMstartstop, "startstop", 1,
118 CMstartsyscall, "startsyscall", 1,
120 CMwaitstop, "waitstop", 1,
123 CMinterrupt, "interrupt", 1,
124 CMnointerrupt, "nointerrupt", 1,
125 CMperiod, "period", 2,
126 CMdeadline, "deadline", 2,
128 CMsporadic, "sporadic", 1,
129 CMdeadlinenotes, "deadlinenotes", 1,
136 /* Segment type from portdat.h */
137 static char *sname[]={ "Text", "Data", "Bss", "Stack", "Shared", "Phys", };
141 * 4 bits of file type (qids above)
142 * 23 bits of process slot number + 1
144 * 32 bits of pid, for consistency checking
145 * If notepg, c->pgrpid.path is pgrp slot, .vers is noteid.
147 #define QSHIFT 5 /* location in qid of proc slot # */
149 #define QID(q) ((((ulong)(q).path)&0x0000001F)>>0)
150 #define SLOT(q) (((((ulong)(q).path)&0x07FFFFFE0)>>QSHIFT)-1)
151 #define PID(q) ((q).vers)
152 #define NOTEID(q) ((q).vers)
154 void procctlreq(Proc*, char*, int);
155 int procctlmemio(Proc*, ulong, int, void*, int);
156 Chan* proctext(Chan*, Proc*);
157 Segment* txt2data(Proc*, Segment*);
158 int procstopped(void*);
159 void mntscan(Mntwalk*, Proc*);
161 static Traceevent *tevents;
164 static int tproduced, tconsumed;
165 void (*proctrace)(Proc*, int, vlong);
170 profclock(Ureg *ur, Timer *)
174 if(up == 0 || up->state != Running)
177 /* user profiling clock */
179 tos = (Tos*)(USTKTOP-sizeof(Tos));
180 tos->clock += TK2MS(1);
186 procgen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp)
192 ulong pid, path, perm, len;
195 mkqid(&qid, Qdir, 0, QTDIR);
196 devdir(c, qid, "#p", 0, eve, 0555, dp);
200 if(c->qid.path == Qdir){
202 strcpy(up->genbuf, "trace");
203 mkqid(&qid, Qtrace, -1, QTFILE);
204 devdir(c, qid, up->genbuf, 0, eve, 0444, dp);
209 /* ignore s and use name to find pid */
210 pid = strtol(name, &ename, 10);
211 if(pid==0 || ename[0]!='\0')
217 else if(--s >= conf.nproc)
224 sprint(up->genbuf, "%lud", pid);
226 * String comparison is done in devwalk so name must match its formatted pid
228 if(name != nil && strcmp(name, up->genbuf) != 0)
230 mkqid(&qid, (s+1)<<QSHIFT, pid, QTDIR);
231 devdir(c, qid, up->genbuf, 0, p->user, DMDIR|0555, dp);
234 if(c->qid.path == Qtrace){
235 strcpy(up->genbuf, "trace");
236 mkqid(&qid, Qtrace, -1, QTFILE);
237 devdir(c, qid, up->genbuf, 0, eve, 0444, dp);
240 if(s >= nelem(procdir))
246 path = c->qid.path&~(((1<<QSHIFT)-1)); /* slot component */
248 /* p->procmode determines default mode for files in /proc */
249 p = proctab(SLOT(c->qid));
253 else /* just copy read bits */
254 perm |= p->procmode & 0444;
257 switch(QID(c->qid)) {
259 len = p->nwait; /* incorrect size, but >0 means there's something to read */
263 if(q && q->profile) {
264 len = (q->top-q->base)>>LRESPROF;
265 len *= sizeof(*q->profile);
270 mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
271 devdir(c, qid, tab->name, len, p->user, perm, dp);
276 _proctrace(Proc* p, Tevent etype, vlong ts)
280 if (p->trace == 0 || topens == 0 ||
281 tproduced - tconsumed >= Nevents)
284 te = &tevents[tproduced&Emask];
288 te->time = todget(nil);
297 if(conf.nproc >= (1<<(16-QSHIFT))-1)
298 print("warning: too many procs for devproc\n");
299 addclock0link((void (*)(void))profclock, 113); /* Relative prime to HZ */
303 procattach(char *spec)
305 return devattach('p', spec);
309 procwalk(Chan *c, Chan *nc, char **name, int nname)
311 return devwalk(c, nc, name, nname, 0, 0, procgen);
315 procstat(Chan *c, uchar *db, int n)
317 return devstat(c, db, n, 0, 0, procgen);
321 * none can't read or write state on other
322 * processes. This is to contain access of
323 * servers running as none should they be
324 * subverted by, for example, a stack attack.
331 if(strcmp(up->user, "none") != 0)
339 procopen(Chan *c, int omode)
346 if(c->qid.type & QTDIR)
347 return devopen(c, omode, 0, 0, procgen);
349 if(QID(c->qid) == Qtrace){
359 error("already open");
361 tevents = (Traceevent*)malloc(sizeof(Traceevent) * Nevents);
364 tproduced = tconsumed = 0;
366 proctrace = _proctrace;
370 c->mode = openmode(omode);
376 p = proctab(SLOT(c->qid));
386 omode = openmode(omode);
434 c->aux = smalloc(sizeof(Mntwalk));
444 c->pgrpid.path = pg->pgrpid+1;
445 c->pgrpid.vers = p->noteid;
449 pprint("procopen %#lux\n", QID(c->qid));
453 /* Affix pid to qid */
455 c->qid.vers = p->pid;
457 /* make sure the process slot didn't get reallocated while we were playing */
462 tc = devopen(c, omode, 0, 0, procgen);
470 procwstat(Chan *c, uchar *db, int n)
475 if(c->qid.type&QTDIR)
478 if(QID(c->qid) == Qtrace)
479 return devwstat(c, db, n);
481 p = proctab(SLOT(c->qid));
492 if(p->pid != PID(c->qid))
495 if(strcmp(up->user, p->user) != 0 && strcmp(up->user, eve) != 0)
498 d = smalloc(sizeof(Dir)+n);
499 n = convM2D(db, n, &d[0], (char*)&d[1]);
502 if(!emptystr(d->uid) && strcmp(d->uid, p->user) != 0){
503 if(strcmp(up->user, eve) != 0)
506 kstrdup(&p->user, d->uid);
508 /* p->procmode determines default mode for files in /proc */
510 p->procmode = d->mode&0777;
520 procoffset(long offset, char *va, int *np)
525 memmove(va, va+*np+offset, -offset);
535 procqidwidth(Chan *c)
539 return sprint(buf, "%lud", c->qid.vers);
543 procfdprint(Chan *c, int fd, int w, char *s, int ns)
549 n = snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n",
551 &"r w rw"[(c->mode&3)<<1],
552 devtab[c->type]->dc, c->dev,
553 c->qid.path, w, c->qid.vers, c->qid.type,
554 c->iounit, c->offset, c->path->s);
559 procfds(Proc *p, char *va, int count, long offset)
567 /* print to buf to avoid holding fgrp lock while writing to user space */
568 if(count > sizeof buf)
574 if(f == nil || p->dot == nil){
585 n = readstr(0, a, count, p->dot->path->s);
586 n += snprint(a+n, count-n, "\n");
587 offset = procoffset(offset, a, &n);
588 /* compute width of qid.path */
590 for(i = 0; i <= f->maxfd; i++) {
594 ww = procqidwidth(c);
598 for(i = 0; i <= f->maxfd; i++) {
602 n += procfdprint(c, i, w, a+n, count-n);
603 offset = procoffset(offset, a, &n);
609 /* copy result to user space, now that locks are released */
618 if(QID(c->qid) == Qtrace && (c->flag & COPEN) != 0){
626 if(QID(c->qid) == Qns && c->aux != 0){
633 int2flag(int flag, char *s)
652 procargs(Proc *p, char *buf, int nbuf)
660 snprint(buf, nbuf, "%s [%s]", p->text, p->args);
664 for(j = 0; j < nbuf - 1; j += m){
669 m = snprint(buf+j, nbuf-j, "%q", a);
678 eventsavailable(void *)
680 return tproduced > tconsumed;
684 prochaswaitq(void *x)
690 p = proctab(SLOT(c->qid));
691 return p->pid != PID(c->qid) || p->waitq != 0;
695 procread(Chan *c, void *va, long n, vlong off)
697 /* NSEG*32 was too small for worst cases */
698 char *a, flag[10], *sps, *srv, statbuf[NSEG*64];
699 int i, j, m, navail, ne, rsize;
713 if(c->qid.type & QTDIR)
714 return devdirread(c, a, n, 0, 0, procgen);
716 if(QID(c->qid) == Qtrace){
717 if(!eventsavailable(nil))
721 navail = tproduced - tconsumed;
722 if(navail > n / sizeof(Traceevent))
723 navail = n / sizeof(Traceevent);
725 ne = ((tconsumed & Emask) + navail > Nevents)?
726 Nevents - (tconsumed & Emask): navail;
727 memmove(rptr, &tevents[tconsumed & Emask],
728 ne * sizeof(Traceevent));
731 rptr += ne * sizeof(Traceevent);
734 return rptr - (uchar*)va;
737 p = proctab(SLOT(c->qid));
738 if(p->pid != PID(c->qid))
744 j = procargs(p, up->genbuf, sizeof up->genbuf);
750 memmove(a, &up->genbuf[offset], n);
755 n = readstr(offset, a, n, p->syscalltrace);
763 return procctlmemio(p, offset, n, va, 1);
768 /* validate kernel addresses */
769 if(offset < (ulong)end) {
770 if(offset+n > (ulong)end)
771 n = (ulong)end - offset;
772 memmove(a, (char*)offset, n);
775 for(i=0; i<nelem(conf.mem); i++){
777 /* klimit-1 because klimit might be zero! */
778 if(cm->kbase <= offset && offset <= cm->klimit-1){
779 if(offset+n >= cm->klimit-1)
780 n = cm->klimit - offset;
781 memmove(a, (char*)offset, n);
789 if(s == 0 || s->profile == 0)
790 error("profile is off");
791 i = (s->top-s->base)>>LRESPROF;
792 i *= sizeof(*s->profile);
797 memmove(a, ((char*)s->profile)+offset, n);
806 if(p->pid != PID(c->qid))
808 if(n < 1) /* must accept at least the '\0' */
813 m = strlen(p->note[0].msg) + 1;
816 memmove(va, p->note[0].msg, m-1);
817 ((char*)va)[m-1] = '\0';
819 memmove(p->note, p->note+1, p->nnote*sizeof(Note));
829 if(offset >= sizeof(Proc))
831 if(offset+n > sizeof(Proc))
832 n = sizeof(Proc) - offset;
833 memmove(a, ((char*)p)+offset, n);
837 rptr = (uchar*)p->dbgreg;
838 rsize = sizeof(Ureg);
842 memset(&kur, 0, sizeof(Ureg));
845 rsize = sizeof(Ureg);
849 rptr = (uchar*)&p->fpsave;
850 rsize = sizeof(FPsave);
858 memmove(a, rptr+offset, n);
862 if(offset >= STATSIZE)
864 if(offset+n > STATSIZE)
865 n = STATSIZE - offset;
869 sps = statename[p->state];
871 memset(statbuf, ' ', sizeof statbuf);
872 readstr(0, statbuf+0*KNAMELEN, KNAMELEN-1, p->text);
873 readstr(0, statbuf+1*KNAMELEN, KNAMELEN-1, p->user);
874 readstr(0, statbuf+2*KNAMELEN, 11, sps);
877 for(i = 0; i < 6; i++) {
880 l = MACHP(0)->ticks - l;
882 readnum(0, statbuf+j+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
888 qunlock(&p->seglock);
891 for(i=0; i<NSEG; i++){
899 qunlock(&p->seglock);
901 readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, l*BY2PG/1024, NUMSIZE);
902 readnum(0, statbuf+j+NUMSIZE*7, NUMSIZE, p->basepri, NUMSIZE);
903 readnum(0, statbuf+j+NUMSIZE*8, NUMSIZE, p->priority, NUMSIZE);
904 memmove(a, statbuf+offset, n);
909 for(i = 0; i < NSEG; i++) {
913 j += sprint(statbuf+j, "%-6s %c%c %.8lux %.8lux %4ld\n",
914 sname[sg->type&SG_TYPE],
915 sg->type&SG_RONLY ? 'R' : ' ',
916 sg->profile ? 'P' : ' ',
917 sg->base, sg->top, sg->ref);
923 if(n == 0 && offset == 0)
924 exhausted("segments");
925 memmove(a, &statbuf[offset], n);
929 if(!canqlock(&p->qwaitr))
938 while(p->waitq == 0 && p->pid == PID(c->qid)) {
939 if(up == p && p->nchild == 0) {
944 sleep(&p->waitr, prochaswaitq, c);
947 if(p->pid != PID(c->qid)){
958 n = snprint(a, n, "%d %lud %lud %lud %q",
960 wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal],
971 if(p->pgrp == nil || p->dot == nil || p->pid != PID(c->qid))
982 i = snprint(a, n, "cd %s\n", p->dot->path->s);
987 int2flag(mw->cm->mflag, flag);
988 if(strcmp(mw->cm->to->path->s, "#M") == 0){
989 srv = srvname(mw->cm->to->mchan);
990 i = snprint(a, n, "mount %s %s %s %s\n", flag,
991 srv==nil? mw->cm->to->mchan->path->s : srv,
992 mw->mh->from->path->s, mw->cm->spec? mw->cm->spec : "");
995 i = snprint(a, n, "bind %s %s %s\n", flag,
996 mw->cm->to->path->s, mw->mh->from->path->s);
1002 return readnum(offset, va, n, p->noteid, NUMSIZE);
1004 return readnum(offset, va, n, p->parentpid, NUMSIZE);
1006 return procfds(p, va, n, offset);
1009 return 0; /* not reached */
1013 mntscan(Mntwalk *mw, Proc *p)
1019 ulong last, bestmid;
1029 last = mw->cm->mountid;
1031 for(i = 0; i < MNTHASH; i++) {
1032 for(f = pg->mnthash[i]; f; f = f->hash) {
1033 for(t = f->mount; t; t = t->next) {
1035 (t->mountid > last && t->mountid < bestmid)) {
1038 bestmid = mw->cm->mountid;
1051 procwrite(Chan *c, void *va, long n, vlong off)
1055 char *a, *arg, buf[ERRMAX];
1059 if(c->qid.type & QTDIR)
1062 p = proctab(SLOT(c->qid));
1064 /* Use the remembered noteid in the channel rather
1065 * than the process pgrpid
1067 if(QID(c->qid) == Qnotepg) {
1068 pgrpnote(NOTEID(c->pgrpid), va, n, NUser);
1077 if(p->pid != PID(c->qid))
1080 switch(QID(c->qid)){
1089 memmove(arg, va, n);
1100 if(p->state != Stopped)
1103 n = procctlmemio(p, offset, n, va, 0);
1107 if(offset >= sizeof(Ureg))
1109 else if(offset+n > sizeof(Ureg))
1110 n = sizeof(Ureg) - offset;
1113 setregisters(p->dbgreg, (char*)(p->dbgreg)+offset, va, n);
1117 if(offset >= sizeof(FPsave))
1119 else if(offset+n > sizeof(FPsave))
1120 n = sizeof(FPsave) - offset;
1121 memmove((uchar*)&p->fpsave+offset, va, n);
1125 procctlreq(p, va, n);
1133 memmove(buf, va, n);
1135 if(!postnote(p, 0, buf, NUser))
1136 error("note not posted");
1145 for(et = t+conf.nproc; t < et; t++) {
1146 if(t->state == Dead)
1148 if(id == t->noteid) {
1149 if(strcmp(p->user, t->user) != 0)
1159 pprint("unknown qid in procwrite\n");
1189 proctext(Chan *c, Proc *p)
1219 if(incref(tc) == 1 || (tc->flag&COPEN) == 0 || tc->mode!=OREAD) {
1224 if(p->pid != PID(c->qid)) {
1236 procstopwait(Proc *p, int ctl)
1242 if(procstopped(p) || p->state == Broken)
1251 up->psstate = "Stopwait";
1257 sleep(&up->sleep, procstopped, p);
1265 procctlcloseone(Proc *p, Fgrp *f, int fd)
1281 procctlclosefiles(Proc *p, int all, int fd)
1293 for(i = 0; i < f->maxfd; i++)
1294 procctlcloseone(p, f, i);
1296 procctlcloseone(p, f, fd);
1302 parsetime(vlong *rt, char *s)
1307 static int p10[] = {100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};
1310 return("missing value");
1311 ticks=strtoul(s, &e, 10);
1314 l = strtoul(p, &e, 10);
1315 if(e-p > nelem(p10))
1316 return "too many digits after decimal point";
1318 return "ill-formed number";
1322 if (*e == '\0' || strcmp(e, "s") == 0){
1323 ticks = 1000000000 * ticks + l;
1324 }else if (strcmp(e, "ms") == 0){
1325 ticks = 1000000 * ticks + l/1000;
1326 }else if (strcmp(e, "µs") == 0 || strcmp(e, "us") == 0){
1327 ticks = 1000 * ticks + l/1000000;
1328 }else if (strcmp(e, "ns") != 0)
1329 return "unrecognized unit";
1335 procctlreq(Proc *p, char *va, int n)
1343 void (*pt)(Proc*, int, vlong);
1345 if(p->kp) /* no ctl requests to kprocs */
1348 cb = parsecmd(va, n);
1354 ct = lookupcmd(cb, proccmd, nelem(proccmd));
1358 procctlclosefiles(p, 0, atoi(cb->f[1]));
1361 procctlclosefiles(p, 1, 0);
1372 p->procctl = Proc_exitme;
1373 postnote(p, 0, "sys: killed", NExit);
1377 p->procctl = Proc_exitme;
1378 postnote(p, 0, "sys: killed", NExit);
1388 pri = atoi(cb->f[1]);
1389 if(pri > PriNormal && !iseve())
1391 procpriority(p, pri, 0);
1394 pri = atoi(cb->f[1]);
1395 if(pri > PriNormal && !iseve())
1397 procpriority(p, pri, 1);
1404 if(s == 0 || (s->type&SG_TYPE) != SG_TEXT)
1408 npc = (s->top-s->base)>>LRESPROF;
1409 s->profile = malloc(npc*sizeof(*s->profile));
1414 if(p->state != Stopped)
1419 if(p->state != Stopped)
1421 p->procctl = Proc_traceme;
1423 procstopwait(p, Proc_traceme);
1425 case CMstartsyscall:
1426 if(p->state != Stopped)
1428 p->procctl = Proc_tracesyscall;
1430 procstopwait(p, Proc_tracesyscall);
1433 procstopwait(p, Proc_stopme);
1439 procwired(p, atoi(cb->f[1]));
1447 p->trace = (atoi(cb->f[1]) != 0);
1454 postnote(p, 0, nil, NUser);
1460 error("notes pending");
1466 if(e=parsetime(&time, cb->f[1])) /* time in ns */
1469 p->edf->T = time/1000; /* Edf times are in µs */
1474 if(e=parsetime(&time, cb->f[1]))
1477 p->edf->D = time/1000;
1482 if(e=parsetime(&time, cb->f[1]))
1485 p->edf->C = time/1000;
1490 p->edf->flags |= Sporadic;
1492 case CMdeadlinenotes:
1495 p->edf->flags |= Sendnotes;
1499 error("edf params");
1506 p->edf->flags |= Extratime;
1524 procstopped(void *a)
1527 return p->state == Stopped;
1531 procctlmemio(Proc *p, ulong offset, int n, void *va, int read)
1541 s = seg(p, offset, 1);
1545 if(offset+n >= s->top)
1548 if(!read && (s->type&SG_TYPE) == SG_TEXT)
1552 soff = offset-s->base;
1557 if(fixfault(s, offset, read, 0) == 0)
1563 pte = s->map[soff/PTEMAPMEM];
1565 panic("procctlmemio");
1566 pg = pte->pages[(soff&(PTEMAPMEM-1))/BY2PG];
1568 panic("procctlmemio1");
1570 l = BY2PG - (offset&(BY2PG-1));
1581 b += offset&(BY2PG-1);
1583 memmove(a, b, n); /* This can fault */
1589 /* Ensure the process sees text page changes */
1591 memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
1602 txt2data(Proc *p, Segment *s)
1607 ps = newseg(SG_DATA, s->base, s->size);
1608 ps->image = s->image;
1610 ps->fstart = s->fstart;
1615 for(i = 0; i < NSEG; i++)
1619 panic("segment gone");
1625 qunlock(&p->seglock);
1631 data2txt(Segment *s)
1635 ps = newseg(SG_TEXT, s->base, s->size);
1636 ps->image = s->image;
1638 ps->fstart = s->fstart;