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){
358 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)
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){
626 if(QID(c->qid) == Qns && c->aux != 0)
631 int2flag(int flag, char *s)
650 procargs(Proc *p, char *buf, int nbuf)
658 snprint(buf, nbuf, "%s [%s]", p->text, p->args);
662 for(j = 0; j < nbuf - 1; j += m){
667 m = snprint(buf+j, nbuf-j, "%q", a);
676 eventsavailable(void *)
678 return tproduced > tconsumed;
682 procread(Chan *c, void *va, long n, vlong off)
684 /* NSEG*32 was too small for worst cases */
685 char *a, flag[10], *sps, *srv, statbuf[NSEG*64];
686 int i, j, m, navail, ne, pid, rsize;
700 if(c->qid.type & QTDIR)
701 return devdirread(c, a, n, 0, 0, procgen);
703 if(QID(c->qid) == Qtrace){
704 if(!eventsavailable(nil))
708 navail = tproduced - tconsumed;
709 if(navail > n / sizeof(Traceevent))
710 navail = n / sizeof(Traceevent);
712 ne = ((tconsumed & Emask) + navail > Nevents)?
713 Nevents - (tconsumed & Emask): navail;
714 memmove(rptr, &tevents[tconsumed & Emask],
715 ne * sizeof(Traceevent));
718 rptr += ne * sizeof(Traceevent);
721 return rptr - (uchar*)va;
724 p = proctab(SLOT(c->qid));
725 if(p->pid != PID(c->qid))
731 j = procargs(p, up->genbuf, sizeof up->genbuf);
737 memmove(a, &up->genbuf[offset], n);
742 n = readstr(offset, a, n, p->syscalltrace);
750 return procctlmemio(p, offset, n, va, 1);
755 /* validate kernel addresses */
756 if(offset < (ulong)end) {
757 if(offset+n > (ulong)end)
758 n = (ulong)end - offset;
759 memmove(a, (char*)offset, n);
762 for(i=0; i<nelem(conf.mem); i++){
764 /* klimit-1 because klimit might be zero! */
765 if(cm->kbase <= offset && offset <= cm->klimit-1){
766 if(offset+n >= cm->klimit-1)
767 n = cm->klimit - offset;
768 memmove(a, (char*)offset, n);
776 if(s == 0 || s->profile == 0)
777 error("profile is off");
778 i = (s->top-s->base)>>LRESPROF;
779 i *= sizeof(*s->profile);
784 memmove(a, ((char*)s->profile)+offset, n);
793 if(p->pid != PID(c->qid))
795 if(n < 1) /* must accept at least the '\0' */
800 m = strlen(p->note[0].msg) + 1;
803 memmove(va, p->note[0].msg, m-1);
804 ((char*)va)[m-1] = '\0';
806 memmove(p->note, p->note+1, p->nnote*sizeof(Note));
816 if(offset >= sizeof(Proc))
818 if(offset+n > sizeof(Proc))
819 n = sizeof(Proc) - offset;
820 memmove(a, ((char*)p)+offset, n);
824 rptr = (uchar*)p->dbgreg;
825 rsize = sizeof(Ureg);
829 memset(&kur, 0, sizeof(Ureg));
832 rsize = sizeof(Ureg);
836 rptr = (uchar*)&p->fpsave;
837 rsize = sizeof(FPsave);
845 memmove(a, rptr+offset, n);
849 if(offset >= STATSIZE)
851 if(offset+n > STATSIZE)
852 n = STATSIZE - offset;
856 sps = statename[p->state];
858 memset(statbuf, ' ', sizeof statbuf);
859 readstr(0, statbuf+0*KNAMELEN, KNAMELEN-1, p->text);
860 readstr(0, statbuf+1*KNAMELEN, KNAMELEN-1, p->user);
861 readstr(0, statbuf+2*KNAMELEN, 11, sps);
864 for(i = 0; i < 6; i++) {
867 l = MACHP(0)->ticks - l;
869 readnum(0, statbuf+j+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
875 qunlock(&p->seglock);
878 for(i=0; i<NSEG; i++){
886 qunlock(&p->seglock);
888 readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, l*BY2PG/1024, NUMSIZE);
889 readnum(0, statbuf+j+NUMSIZE*7, NUMSIZE, p->basepri, NUMSIZE);
890 readnum(0, statbuf+j+NUMSIZE*8, NUMSIZE, p->priority, NUMSIZE);
891 memmove(a, statbuf+offset, n);
896 for(i = 0; i < NSEG; i++) {
900 j += sprint(statbuf+j, "%-6s %c%c %.8lux %.8lux %4ld\n",
901 sname[sg->type&SG_TYPE],
902 sg->type&SG_RONLY ? 'R' : ' ',
903 sg->profile ? 'P' : ' ',
904 sg->base, sg->top, sg->ref);
910 if(n == 0 && offset == 0)
911 exhausted("segments");
912 memmove(a, &statbuf[offset], n);
916 if(!canqlock(&p->qwaitr))
926 while(p->waitq == 0) {
927 if(up == p && p->nchild == 0) {
932 sleep(&p->waitr, haswaitq, p);
944 n = snprint(a, n, "%d %lud %lud %lud %q",
946 wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal],
957 if(p->pgrp == nil || p->pid != PID(c->qid))
968 i = snprint(a, n, "cd %s\n", p->dot->path->s);
973 int2flag(mw->cm->mflag, flag);
974 if(strcmp(mw->cm->to->path->s, "#M") == 0){
975 srv = srvname(mw->cm->to->mchan);
976 i = snprint(a, n, "mount %s %s %s %s\n", flag,
977 srv==nil? mw->cm->to->mchan->path->s : srv,
978 mw->mh->from->path->s, mw->cm->spec? mw->cm->spec : "");
981 i = snprint(a, n, "bind %s %s %s\n", flag,
982 mw->cm->to->path->s, mw->mh->from->path->s);
988 return readnum(offset, va, n, p->noteid, NUMSIZE);
990 return readnum(offset, va, n, p->parentpid, NUMSIZE);
992 return procfds(p, va, n, offset);
995 return 0; /* not reached */
999 mntscan(Mntwalk *mw, Proc *p)
1005 ulong last, bestmid;
1015 last = mw->cm->mountid;
1017 for(i = 0; i < MNTHASH; i++) {
1018 for(f = pg->mnthash[i]; f; f = f->hash) {
1019 for(t = f->mount; t; t = t->next) {
1021 (t->mountid > last && t->mountid < bestmid)) {
1024 bestmid = mw->cm->mountid;
1037 procwrite(Chan *c, void *va, long n, vlong off)
1041 char *a, *arg, buf[ERRMAX];
1045 if(c->qid.type & QTDIR)
1048 p = proctab(SLOT(c->qid));
1050 /* Use the remembered noteid in the channel rather
1051 * than the process pgrpid
1053 if(QID(c->qid) == Qnotepg) {
1054 pgrpnote(NOTEID(c->pgrpid), va, n, NUser);
1063 if(p->pid != PID(c->qid))
1066 switch(QID(c->qid)){
1075 memmove(arg, va, n);
1086 if(p->state != Stopped)
1089 n = procctlmemio(p, offset, n, va, 0);
1093 if(offset >= sizeof(Ureg))
1095 else if(offset+n > sizeof(Ureg))
1096 n = sizeof(Ureg) - offset;
1099 setregisters(p->dbgreg, (char*)(p->dbgreg)+offset, va, n);
1103 if(offset >= sizeof(FPsave))
1105 else if(offset+n > sizeof(FPsave))
1106 n = sizeof(FPsave) - offset;
1107 memmove((uchar*)&p->fpsave+offset, va, n);
1111 procctlreq(p, va, n);
1119 memmove(buf, va, n);
1121 if(!postnote(p, 0, buf, NUser))
1122 error("note not posted");
1131 for(et = t+conf.nproc; t < et; t++) {
1132 if(t->state == Dead)
1134 if(id == t->noteid) {
1135 if(strcmp(p->user, t->user) != 0)
1145 pprint("unknown qid in procwrite\n");
1175 proctext(Chan *c, Proc *p)
1205 if(incref(tc) == 1 || (tc->flag&COPEN) == 0 || tc->mode!=OREAD) {
1210 if(p->pid != PID(c->qid)) {
1222 procstopwait(Proc *p, int ctl)
1228 if(procstopped(p) || p->state == Broken)
1236 up->psstate = "Stopwait";
1242 sleep(&up->sleep, procstopped, p);
1250 procctlcloseone(Proc *p, Fgrp *f, int fd)
1266 procctlclosefiles(Proc *p, int all, int fd)
1278 for(i = 0; i < f->maxfd; i++)
1279 procctlcloseone(p, f, i);
1281 procctlcloseone(p, f, fd);
1287 parsetime(vlong *rt, char *s)
1292 static int p10[] = {100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};
1295 return("missing value");
1296 ticks=strtoul(s, &e, 10);
1299 l = strtoul(p, &e, 10);
1300 if(e-p > nelem(p10))
1301 return "too many digits after decimal point";
1303 return "ill-formed number";
1307 if (*e == '\0' || strcmp(e, "s") == 0){
1308 ticks = 1000000000 * ticks + l;
1309 }else if (strcmp(e, "ms") == 0){
1310 ticks = 1000000 * ticks + l/1000;
1311 }else if (strcmp(e, "µs") == 0 || strcmp(e, "us") == 0){
1312 ticks = 1000 * ticks + l/1000000;
1313 }else if (strcmp(e, "ns") != 0)
1314 return "unrecognized unit";
1320 procctlreq(Proc *p, char *va, int n)
1328 void (*pt)(Proc*, int, vlong);
1330 if(p->kp) /* no ctl requests to kprocs */
1333 cb = parsecmd(va, n);
1339 ct = lookupcmd(cb, proccmd, nelem(proccmd));
1343 procctlclosefiles(p, 0, atoi(cb->f[1]));
1346 procctlclosefiles(p, 1, 0);
1357 p->procctl = Proc_exitme;
1358 postnote(p, 0, "sys: killed", NExit);
1362 p->procctl = Proc_exitme;
1363 postnote(p, 0, "sys: killed", NExit);
1373 pri = atoi(cb->f[1]);
1374 if(pri > PriNormal && !iseve())
1376 procpriority(p, pri, 0);
1379 pri = atoi(cb->f[1]);
1380 if(pri > PriNormal && !iseve())
1382 procpriority(p, pri, 1);
1389 if(s == 0 || (s->type&SG_TYPE) != SG_TEXT)
1393 npc = (s->top-s->base)>>LRESPROF;
1394 s->profile = malloc(npc*sizeof(*s->profile));
1399 if(p->state != Stopped)
1404 if(p->state != Stopped)
1406 p->procctl = Proc_traceme;
1408 procstopwait(p, Proc_traceme);
1410 case CMstartsyscall:
1411 if(p->state != Stopped)
1413 p->procctl = Proc_tracesyscall;
1415 procstopwait(p, Proc_tracesyscall);
1418 procstopwait(p, Proc_stopme);
1424 procwired(p, atoi(cb->f[1]));
1432 p->trace = (atoi(cb->f[1]) != 0);
1439 postnote(p, 0, nil, NUser);
1445 error("notes pending");
1451 if(e=parsetime(&time, cb->f[1])) /* time in ns */
1454 p->edf->T = time/1000; /* Edf times are in µs */
1459 if(e=parsetime(&time, cb->f[1]))
1462 p->edf->D = time/1000;
1467 if(e=parsetime(&time, cb->f[1]))
1470 p->edf->C = time/1000;
1475 p->edf->flags |= Sporadic;
1477 case CMdeadlinenotes:
1480 p->edf->flags |= Sendnotes;
1484 error("edf params");
1491 p->edf->flags |= Extratime;
1509 procstopped(void *a)
1512 return p->state == Stopped;
1516 procctlmemio(Proc *p, ulong offset, int n, void *va, int read)
1526 s = seg(p, offset, 1);
1530 if(offset+n >= s->top)
1533 if(!read && (s->type&SG_TYPE) == SG_TEXT)
1537 soff = offset-s->base;
1542 if(fixfault(s, offset, read, 0) == 0)
1548 pte = s->map[soff/PTEMAPMEM];
1550 panic("procctlmemio");
1551 pg = pte->pages[(soff&(PTEMAPMEM-1))/BY2PG];
1553 panic("procctlmemio1");
1555 l = BY2PG - (offset&(BY2PG-1));
1566 b += offset&(BY2PG-1);
1568 memmove(a, b, n); /* This can fault */
1574 /* Ensure the process sees text page changes */
1576 memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
1587 txt2data(Proc *p, Segment *s)
1592 ps = newseg(SG_DATA, s->base, s->size);
1593 ps->image = s->image;
1595 ps->fstart = s->fstart;
1600 for(i = 0; i < NSEG; i++)
1604 panic("segment gone");
1610 qunlock(&p->seglock);
1616 data2txt(Segment *s)
1620 ps = newseg(SG_TEXT, s->base, s->size);
1621 ps->image = s->image;
1623 ps->fstart = s->fstart;