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);
433 c->aux = smalloc(sizeof(Mntwalk));
441 if(omode!=OWRITE || pg->pgrpid == 1)
443 c->pgrpid.path = pg->pgrpid+1;
444 c->pgrpid.vers = p->noteid;
448 pprint("procopen %#lux\n", QID(c->qid));
452 /* Affix pid to qid */
454 c->qid.vers = p->pid;
456 /* make sure the process slot didn't get reallocated while we were playing */
461 tc = devopen(c, omode, 0, 0, procgen);
469 procwstat(Chan *c, uchar *db, int n)
474 if(c->qid.type&QTDIR)
477 if(QID(c->qid) == Qtrace)
478 return devwstat(c, db, n);
480 p = proctab(SLOT(c->qid));
491 if(p->pid != PID(c->qid))
494 if(strcmp(up->user, p->user) != 0 && strcmp(up->user, eve) != 0)
497 d = smalloc(sizeof(Dir)+n);
498 n = convM2D(db, n, &d[0], (char*)&d[1]);
501 if(!emptystr(d->uid) && strcmp(d->uid, p->user) != 0){
502 if(strcmp(up->user, eve) != 0)
505 kstrdup(&p->user, d->uid);
507 /* p->procmode determines default mode for files in /proc */
509 p->procmode = d->mode&0777;
519 procoffset(long offset, char *va, int *np)
524 memmove(va, va+*np+offset, -offset);
534 procqidwidth(Chan *c)
538 return sprint(buf, "%lud", c->qid.vers);
542 procfdprint(Chan *c, int fd, int w, char *s, int ns)
548 n = snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n",
550 &"r w rw"[(c->mode&3)<<1],
551 devtab[c->type]->dc, c->dev,
552 c->qid.path, w, c->qid.vers, c->qid.type,
553 c->iounit, c->offset, c->path->s);
558 procfds(Proc *p, char *va, int count, long offset)
566 /* print to buf to avoid holding fgrp lock while writing to user space */
567 if(count > sizeof buf)
584 n = readstr(0, a, count, p->dot->path->s);
585 n += snprint(a+n, count-n, "\n");
586 offset = procoffset(offset, a, &n);
587 /* compute width of qid.path */
589 for(i = 0; i <= f->maxfd; i++) {
593 ww = procqidwidth(c);
597 for(i = 0; i <= f->maxfd; i++) {
601 n += procfdprint(c, i, w, a+n, count-n);
602 offset = procoffset(offset, a, &n);
608 /* copy result to user space, now that locks are released */
617 if(QID(c->qid) == Qtrace){
625 if(QID(c->qid) == Qns && c->aux != 0)
630 int2flag(int flag, char *s)
649 procargs(Proc *p, char *buf, int nbuf)
657 snprint(buf, nbuf, "%s [%s]", p->text, p->args);
661 for(j = 0; j < nbuf - 1; j += m){
666 m = snprint(buf+j, nbuf-j, "%q", a);
675 eventsavailable(void *)
677 return tproduced > tconsumed;
681 procread(Chan *c, void *va, long n, vlong off)
683 /* NSEG*32 was too small for worst cases */
684 char *a, flag[10], *sps, *srv, statbuf[NSEG*64];
685 int i, j, m, navail, ne, pid, rsize;
699 if(c->qid.type & QTDIR)
700 return devdirread(c, a, n, 0, 0, procgen);
702 if(QID(c->qid) == Qtrace){
703 if(!eventsavailable(nil))
707 navail = tproduced - tconsumed;
708 if(navail > n / sizeof(Traceevent))
709 navail = n / sizeof(Traceevent);
711 ne = ((tconsumed & Emask) + navail > Nevents)?
712 Nevents - (tconsumed & Emask): navail;
713 memmove(rptr, &tevents[tconsumed & Emask],
714 ne * sizeof(Traceevent));
717 rptr += ne * sizeof(Traceevent);
720 return rptr - (uchar*)va;
723 p = proctab(SLOT(c->qid));
724 if(p->pid != PID(c->qid))
730 j = procargs(p, up->genbuf, sizeof up->genbuf);
736 memmove(a, &up->genbuf[offset], n);
741 n = readstr(offset, a, n, p->syscalltrace);
746 return procctlmemio(p, offset, n, va, 1);
751 /* validate kernel addresses */
752 if(offset < (ulong)end) {
753 if(offset+n > (ulong)end)
754 n = (ulong)end - offset;
755 memmove(a, (char*)offset, n);
758 for(i=0; i<nelem(conf.mem); i++){
760 /* klimit-1 because klimit might be zero! */
761 if(cm->kbase <= offset && offset <= cm->klimit-1){
762 if(offset+n >= cm->klimit-1)
763 n = cm->klimit - offset;
764 memmove(a, (char*)offset, n);
772 if(s == 0 || s->profile == 0)
773 error("profile is off");
774 i = (s->top-s->base)>>LRESPROF;
775 i *= sizeof(*s->profile);
780 memmove(a, ((char*)s->profile)+offset, n);
789 if(p->pid != PID(c->qid))
791 if(n < 1) /* must accept at least the '\0' */
796 m = strlen(p->note[0].msg) + 1;
799 memmove(va, p->note[0].msg, m-1);
800 ((char*)va)[m-1] = '\0';
802 memmove(p->note, p->note+1, p->nnote*sizeof(Note));
812 if(offset >= sizeof(Proc))
814 if(offset+n > sizeof(Proc))
815 n = sizeof(Proc) - offset;
816 memmove(a, ((char*)p)+offset, n);
820 rptr = (uchar*)p->dbgreg;
821 rsize = sizeof(Ureg);
825 memset(&kur, 0, sizeof(Ureg));
828 rsize = sizeof(Ureg);
832 rptr = (uchar*)&p->fpsave;
833 rsize = sizeof(FPsave);
841 memmove(a, rptr+offset, n);
845 if(offset >= STATSIZE)
847 if(offset+n > STATSIZE)
848 n = STATSIZE - offset;
852 sps = statename[p->state];
854 memset(statbuf, ' ', sizeof statbuf);
855 readstr(0, statbuf+0*KNAMELEN, KNAMELEN-1, p->text);
856 readstr(0, statbuf+1*KNAMELEN, KNAMELEN-1, p->user);
857 readstr(0, statbuf+2*KNAMELEN, 11, sps);
860 for(i = 0; i < 6; i++) {
863 l = MACHP(0)->ticks - l;
865 readnum(0, statbuf+j+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
871 qunlock(&p->seglock);
874 for(i=0; i<NSEG; i++){
882 qunlock(&p->seglock);
884 readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, l*BY2PG/1024, NUMSIZE);
885 readnum(0, statbuf+j+NUMSIZE*7, NUMSIZE, p->basepri, NUMSIZE);
886 readnum(0, statbuf+j+NUMSIZE*8, NUMSIZE, p->priority, NUMSIZE);
887 memmove(a, statbuf+offset, n);
892 for(i = 0; i < NSEG; i++) {
896 j += sprint(statbuf+j, "%-6s %c%c %.8lux %.8lux %4ld\n",
897 sname[sg->type&SG_TYPE],
898 sg->type&SG_RONLY ? 'R' : ' ',
899 sg->profile ? 'P' : ' ',
900 sg->base, sg->top, sg->ref);
906 if(n == 0 && offset == 0)
907 exhausted("segments");
908 memmove(a, &statbuf[offset], n);
912 if(!canqlock(&p->qwaitr))
922 while(p->waitq == 0) {
923 if(up == p && p->nchild == 0) {
928 sleep(&p->waitr, haswaitq, p);
940 n = snprint(a, n, "%d %lud %lud %lud %q",
942 wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal],
953 if(p->pgrp == nil || p->pid != PID(c->qid))
964 i = snprint(a, n, "cd %s\n", p->dot->path->s);
969 int2flag(mw->cm->mflag, flag);
970 if(strcmp(mw->cm->to->path->s, "#M") == 0){
971 srv = srvname(mw->cm->to->mchan);
972 i = snprint(a, n, "mount %s %s %s %s\n", flag,
973 srv==nil? mw->cm->to->mchan->path->s : srv,
974 mw->mh->from->path->s, mw->cm->spec? mw->cm->spec : "");
977 i = snprint(a, n, "bind %s %s %s\n", flag,
978 mw->cm->to->path->s, mw->mh->from->path->s);
984 return readnum(offset, va, n, p->noteid, NUMSIZE);
986 return readnum(offset, va, n, p->parentpid, NUMSIZE);
988 return procfds(p, va, n, offset);
991 return 0; /* not reached */
995 mntscan(Mntwalk *mw, Proc *p)
1001 ulong last, bestmid;
1011 last = mw->cm->mountid;
1013 for(i = 0; i < MNTHASH; i++) {
1014 for(f = pg->mnthash[i]; f; f = f->hash) {
1015 for(t = f->mount; t; t = t->next) {
1017 (t->mountid > last && t->mountid < bestmid)) {
1020 bestmid = mw->cm->mountid;
1033 procwrite(Chan *c, void *va, long n, vlong off)
1037 char *a, *arg, buf[ERRMAX];
1041 if(c->qid.type & QTDIR)
1044 p = proctab(SLOT(c->qid));
1046 /* Use the remembered noteid in the channel rather
1047 * than the process pgrpid
1049 if(QID(c->qid) == Qnotepg) {
1050 pgrpnote(NOTEID(c->pgrpid), va, n, NUser);
1059 if(p->pid != PID(c->qid))
1062 switch(QID(c->qid)){
1071 memmove(arg, va, n);
1082 if(p->state != Stopped)
1085 n = procctlmemio(p, offset, n, va, 0);
1089 if(offset >= sizeof(Ureg))
1091 else if(offset+n > sizeof(Ureg))
1092 n = sizeof(Ureg) - offset;
1095 setregisters(p->dbgreg, (char*)(p->dbgreg)+offset, va, n);
1099 if(offset >= sizeof(FPsave))
1101 else if(offset+n > sizeof(FPsave))
1102 n = sizeof(FPsave) - offset;
1103 memmove((uchar*)&p->fpsave+offset, va, n);
1107 procctlreq(p, va, n);
1115 memmove(buf, va, n);
1117 if(!postnote(p, 0, buf, NUser))
1118 error("note not posted");
1127 for(et = t+conf.nproc; t < et; t++) {
1128 if(t->state == Dead)
1130 if(id == t->noteid) {
1131 if(strcmp(p->user, t->user) != 0)
1141 pprint("unknown qid in procwrite\n");
1171 proctext(Chan *c, Proc *p)
1201 if(incref(tc) == 1 || (tc->flag&COPEN) == 0 || tc->mode!=OREAD) {
1206 if(p->pid != PID(c->qid))
1216 procstopwait(Proc *p, int ctl)
1222 if(procstopped(p) || p->state == Broken)
1230 up->psstate = "Stopwait";
1236 sleep(&up->sleep, procstopped, p);
1244 procctlcloseone(Proc *p, Fgrp *f, int fd)
1260 procctlclosefiles(Proc *p, int all, int fd)
1272 for(i = 0; i < f->maxfd; i++)
1273 procctlcloseone(p, f, i);
1275 procctlcloseone(p, f, fd);
1281 parsetime(vlong *rt, char *s)
1286 static int p10[] = {100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};
1289 return("missing value");
1290 ticks=strtoul(s, &e, 10);
1293 l = strtoul(p, &e, 10);
1294 if(e-p > nelem(p10))
1295 return "too many digits after decimal point";
1297 return "ill-formed number";
1301 if (*e == '\0' || strcmp(e, "s") == 0){
1302 ticks = 1000000000 * ticks + l;
1303 }else if (strcmp(e, "ms") == 0){
1304 ticks = 1000000 * ticks + l/1000;
1305 }else if (strcmp(e, "µs") == 0 || strcmp(e, "us") == 0){
1306 ticks = 1000 * ticks + l/1000000;
1307 }else if (strcmp(e, "ns") != 0)
1308 return "unrecognized unit";
1314 procctlreq(Proc *p, char *va, int n)
1322 void (*pt)(Proc*, int, vlong);
1324 if(p->kp) /* no ctl requests to kprocs */
1327 cb = parsecmd(va, n);
1333 ct = lookupcmd(cb, proccmd, nelem(proccmd));
1337 procctlclosefiles(p, 0, atoi(cb->f[1]));
1340 procctlclosefiles(p, 1, 0);
1351 p->procctl = Proc_exitme;
1352 postnote(p, 0, "sys: killed", NExit);
1356 p->procctl = Proc_exitme;
1357 postnote(p, 0, "sys: killed", NExit);
1367 pri = atoi(cb->f[1]);
1368 if(pri > PriNormal && !iseve())
1370 procpriority(p, pri, 0);
1373 pri = atoi(cb->f[1]);
1374 if(pri > PriNormal && !iseve())
1376 procpriority(p, pri, 1);
1383 if(s == 0 || (s->type&SG_TYPE) != SG_TEXT)
1387 npc = (s->top-s->base)>>LRESPROF;
1388 s->profile = malloc(npc*sizeof(*s->profile));
1393 if(p->state != Stopped)
1398 if(p->state != Stopped)
1400 p->procctl = Proc_traceme;
1402 procstopwait(p, Proc_traceme);
1404 case CMstartsyscall:
1405 if(p->state != Stopped)
1407 p->procctl = Proc_tracesyscall;
1409 procstopwait(p, Proc_tracesyscall);
1412 procstopwait(p, Proc_stopme);
1418 procwired(p, atoi(cb->f[1]));
1426 p->trace = (atoi(cb->f[1]) != 0);
1433 postnote(p, 0, nil, NUser);
1439 error("notes pending");
1445 if(e=parsetime(&time, cb->f[1])) /* time in ns */
1448 p->edf->T = time/1000; /* Edf times are in µs */
1453 if(e=parsetime(&time, cb->f[1]))
1456 p->edf->D = time/1000;
1461 if(e=parsetime(&time, cb->f[1]))
1464 p->edf->C = time/1000;
1469 p->edf->flags |= Sporadic;
1471 case CMdeadlinenotes:
1474 p->edf->flags |= Sendnotes;
1478 error("edf params");
1485 p->edf->flags |= Extratime;
1503 procstopped(void *a)
1506 return p->state == Stopped;
1510 procctlmemio(Proc *p, ulong offset, int n, void *va, int read)
1520 s = seg(p, offset, 1);
1524 if(offset+n >= s->top)
1527 if(!read && (s->type&SG_TYPE) == SG_TEXT)
1531 soff = offset-s->base;
1536 if(fixfault(s, offset, read, 0) == 0)
1542 pte = s->map[soff/PTEMAPMEM];
1544 panic("procctlmemio");
1545 pg = pte->pages[(soff&(PTEMAPMEM-1))/BY2PG];
1547 panic("procctlmemio1");
1549 l = BY2PG - (offset&(BY2PG-1));
1560 b += offset&(BY2PG-1);
1562 memmove(a, b, n); /* This can fault */
1568 /* Ensure the process sees text page changes */
1570 memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
1581 txt2data(Proc *p, Segment *s)
1586 ps = newseg(SG_DATA, s->base, s->size);
1587 ps->image = s->image;
1589 ps->fstart = s->fstart;
1594 for(i = 0; i < NSEG; i++)
1598 panic("segment gone");
1604 qunlock(&p->seglock);
1610 data2txt(Segment *s)
1614 ps = newseg(SG_TEXT, s->base, s->size);
1615 ps->image = s->image;
1617 ps->fstart = s->fstart;