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*, uintptr, int, void*, int);
156 Chan* proctext(Chan*, Proc*);
157 int procstopped(void*);
158 void mntscan(Mntwalk*, Proc*);
159 ulong procpagecount(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 != nil && q->profile != nil) {
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 print("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 * userspace can't pass negative file offset for a
696 * 64 bit kernel address, so we use 63 bit and sign
708 procread(Chan *c, void *va, long n, vlong off)
710 /* NSEG*32 was too small for worst cases */
711 char *a, flag[10], *sps, *srv, statbuf[NSEG*64];
712 int i, j, m, navail, ne, rsize;
726 if(c->qid.type & QTDIR)
727 return devdirread(c, a, n, 0, 0, procgen);
729 if(QID(c->qid) == Qtrace){
730 if(!eventsavailable(nil))
734 navail = tproduced - tconsumed;
735 if(navail > n / sizeof(Traceevent))
736 navail = n / sizeof(Traceevent);
738 ne = ((tconsumed & Emask) + navail > Nevents)?
739 Nevents - (tconsumed & Emask): navail;
740 memmove(rptr, &tevents[tconsumed & Emask],
741 ne * sizeof(Traceevent));
744 rptr += ne * sizeof(Traceevent);
747 return rptr - (uchar*)va;
750 p = proctab(SLOT(c->qid));
751 if(p->pid != PID(c->qid))
757 j = procargs(p, up->genbuf, sizeof up->genbuf);
763 memmove(a, &up->genbuf[offset], n);
768 n = readstr(offset, a, n, p->syscalltrace);
775 addr = off2addr(off);
777 return procctlmemio(p, addr, n, va, 1);
782 /* validate kernel addresses */
783 if(addr < (uintptr)end) {
784 if(addr+n > (uintptr)end)
785 n = (uintptr)end - addr;
786 memmove(a, (char*)addr, n);
789 for(i=0; i<nelem(conf.mem); i++){
791 /* klimit-1 because klimit might be zero! */
792 if(cm->kbase <= addr && addr <= cm->klimit-1){
793 if(addr+n >= cm->klimit-1)
794 n = cm->klimit - addr;
795 memmove(a, (char*)addr, n);
803 if(s == nil || s->profile == nil)
804 error("profile is off");
805 i = (s->top-s->base)>>LRESPROF;
806 i *= sizeof(*s->profile);
811 memmove(a, ((char*)s->profile)+offset, n);
820 if(p->pid != PID(c->qid))
822 if(n < 1) /* must accept at least the '\0' */
827 m = strlen(p->note[0].msg) + 1;
830 memmove(va, p->note[0].msg, m-1);
831 ((char*)va)[m-1] = '\0';
833 memmove(p->note, p->note+1, p->nnote*sizeof(Note));
843 if(offset >= sizeof(Proc))
845 if(offset+n > sizeof(Proc))
846 n = sizeof(Proc) - offset;
847 memmove(a, ((char*)p)+offset, n);
851 rptr = (uchar*)p->dbgreg;
852 rsize = sizeof(Ureg);
856 memset(&kur, 0, sizeof(Ureg));
859 rsize = sizeof(Ureg);
863 rptr = (uchar*)&p->fpsave;
864 rsize = sizeof(FPsave);
872 memmove(a, rptr+offset, n);
876 if(offset >= STATSIZE)
878 if(offset+n > STATSIZE)
879 n = STATSIZE - offset;
883 sps = statename[p->state];
885 memset(statbuf, ' ', sizeof statbuf);
886 readstr(0, statbuf+0*KNAMELEN, KNAMELEN-1, p->text);
887 readstr(0, statbuf+1*KNAMELEN, KNAMELEN-1, p->user);
888 readstr(0, statbuf+2*KNAMELEN, 11, sps);
891 for(i = 0; i < 6; i++) {
894 l = MACHP(0)->ticks - l;
896 readnum(0, statbuf+j+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
899 readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, procpagecount(p)*BY2PG/1024, NUMSIZE);
900 readnum(0, statbuf+j+NUMSIZE*7, NUMSIZE, p->basepri, NUMSIZE);
901 readnum(0, statbuf+j+NUMSIZE*8, NUMSIZE, p->priority, NUMSIZE);
902 memmove(a, statbuf+offset, n);
907 for(i = 0; i < NSEG; i++) {
911 j += sprint(statbuf+j, "%-6s %c%c %8p %8p %4ld\n",
912 sname[sg->type&SG_TYPE],
913 sg->type&SG_RONLY ? 'R' : ' ',
914 sg->profile ? 'P' : ' ',
915 sg->base, sg->top, sg->ref);
921 if(n == 0 && offset == 0)
922 exhausted("segments");
923 memmove(a, &statbuf[offset], n);
927 if(!canqlock(&p->qwaitr))
936 while(p->waitq == 0 && p->pid == PID(c->qid)) {
937 if(up == p && p->nchild == 0) {
942 sleep(&p->waitr, prochaswaitq, c);
945 if(p->pid != PID(c->qid)){
956 n = snprint(a, n, "%d %lud %lud %lud %q",
958 wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal],
969 if(p->pgrp == nil || p->dot == nil || p->pid != PID(c->qid))
980 i = snprint(a, n, "cd %s\n", p->dot->path->s);
985 int2flag(mw->cm->mflag, flag);
986 if(strcmp(mw->cm->to->path->s, "#M") == 0){
987 srv = srvname(mw->cm->to->mchan);
988 i = snprint(a, n, "mount %s %s %s %s\n", flag,
989 srv==nil? mw->cm->to->mchan->path->s : srv,
990 mw->mh->from->path->s, mw->cm->spec? mw->cm->spec : "");
993 i = snprint(a, n, "bind %s %s %s\n", flag,
994 mw->cm->to->path->s, mw->mh->from->path->s);
1000 return readnum(offset, va, n, p->noteid, NUMSIZE);
1002 return readnum(offset, va, n, p->parentpid, NUMSIZE);
1004 return procfds(p, va, n, offset);
1007 return 0; /* not reached */
1011 mntscan(Mntwalk *mw, Proc *p)
1017 ulong last, bestmid;
1027 last = mw->cm->mountid;
1029 for(i = 0; i < MNTHASH; i++) {
1030 for(f = pg->mnthash[i]; f; f = f->hash) {
1031 for(t = f->mount; t; t = t->next) {
1033 (t->mountid > last && t->mountid < bestmid)) {
1036 bestmid = mw->cm->mountid;
1049 procwrite(Chan *c, void *va, long n, vlong off)
1053 char *a, *arg, buf[ERRMAX];
1058 if(c->qid.type & QTDIR)
1061 p = proctab(SLOT(c->qid));
1063 /* Use the remembered noteid in the channel rather
1064 * than the process pgrpid
1066 if(QID(c->qid) == Qnotepg) {
1067 pgrpnote(NOTEID(c->pgrpid), va, n, NUser);
1076 if(p->pid != PID(c->qid))
1079 switch(QID(c->qid)){
1088 memmove(arg, va, n);
1099 if(p->state != Stopped)
1101 n = procctlmemio(p, off2addr(off), n, va, 0);
1105 if(offset >= sizeof(Ureg))
1107 else if(offset+n > sizeof(Ureg))
1108 n = sizeof(Ureg) - offset;
1111 setregisters(p->dbgreg, (char*)(p->dbgreg)+offset, va, n);
1115 if(offset >= sizeof(FPsave))
1117 else if(offset+n > sizeof(FPsave))
1118 n = sizeof(FPsave) - offset;
1119 memmove((uchar*)&p->fpsave+offset, va, n);
1123 procctlreq(p, va, n);
1131 memmove(buf, va, n);
1133 if(!postnote(p, 0, buf, NUser))
1134 error("note not posted");
1147 for(et = t+conf.nproc; t < et; t++) {
1148 if(t->state == Dead || t->kp)
1150 if(id == t->noteid) {
1152 if(strcmp(p->user, t->user) != 0)
1162 print("unknown qid in procwrite\n");
1192 proctext(Chan *c, Proc *p)
1221 if(incref(tc) == 1 || (tc->flag&COPEN) == 0 || tc->mode!=OREAD) {
1226 if(p->pid != PID(c->qid)) {
1238 procstopwait(Proc *p, int ctl)
1244 if(procstopped(p) || p->state == Broken)
1253 up->psstate = "Stopwait";
1259 sleep(&up->sleep, procstopped, p);
1267 procctlclosefiles(Proc *p, int all, int fd)
1280 while(fd <= f->maxfd){
1299 parsetime(vlong *rt, char *s)
1304 static int p10[] = {100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};
1307 return("missing value");
1308 ticks=strtoul(s, &e, 10);
1311 l = strtoul(p, &e, 10);
1312 if(e-p > nelem(p10))
1313 return "too many digits after decimal point";
1315 return "ill-formed number";
1319 if (*e == '\0' || strcmp(e, "s") == 0){
1320 ticks = 1000000000 * ticks + l;
1321 }else if (strcmp(e, "ms") == 0){
1322 ticks = 1000000 * ticks + l/1000;
1323 }else if (strcmp(e, "µs") == 0 || strcmp(e, "us") == 0){
1324 ticks = 1000 * ticks + l/1000000;
1325 }else if (strcmp(e, "ns") != 0)
1326 return "unrecognized unit";
1332 procctlreq(Proc *p, char *va, int n)
1340 void (*pt)(Proc*, int, vlong);
1342 if(p->kp) /* no ctl requests to kprocs */
1345 cb = parsecmd(va, n);
1351 ct = lookupcmd(cb, proccmd, nelem(proccmd));
1355 procctlclosefiles(p, 0, atoi(cb->f[1]));
1358 procctlclosefiles(p, 1, 0);
1369 p->procctl = Proc_exitme;
1370 postnote(p, 0, "sys: killed", NExit);
1374 p->procctl = Proc_exitme;
1375 postnote(p, 0, "sys: killed", NExit);
1385 pri = atoi(cb->f[1]);
1386 if(pri > PriNormal && !iseve())
1388 procpriority(p, pri, 0);
1391 pri = atoi(cb->f[1]);
1392 if(pri > PriNormal && !iseve())
1394 procpriority(p, pri, 1);
1401 if(s == 0 || (s->type&SG_TYPE) != SG_TEXT)
1403 if(s->profile != nil)
1405 npc = (s->top-s->base)>>LRESPROF;
1406 s->profile = malloc(npc*sizeof(*s->profile));
1407 if(s->profile == nil)
1411 if(p->state != Stopped)
1416 if(p->state != Stopped)
1418 p->procctl = Proc_traceme;
1420 procstopwait(p, Proc_traceme);
1422 case CMstartsyscall:
1423 if(p->state != Stopped)
1425 p->procctl = Proc_tracesyscall;
1427 procstopwait(p, Proc_tracesyscall);
1430 procstopwait(p, Proc_stopme);
1436 procwired(p, atoi(cb->f[1]));
1444 p->trace = (atoi(cb->f[1]) != 0);
1451 postnote(p, 0, nil, NUser);
1457 error("notes pending");
1463 if(e=parsetime(&time, cb->f[1])) /* time in ns */
1466 p->edf->T = time/1000; /* Edf times are in µs */
1471 if(e=parsetime(&time, cb->f[1]))
1474 p->edf->D = time/1000;
1479 if(e=parsetime(&time, cb->f[1]))
1482 p->edf->C = time/1000;
1487 p->edf->flags |= Sporadic;
1489 case CMdeadlinenotes:
1492 p->edf->flags |= Sendnotes;
1496 error("edf params");
1503 p->edf->flags |= Extratime;
1521 procstopped(void *a)
1524 return p->state == Stopped;
1528 procpagecount(Proc *p)
1534 eqlock(&p->seglock);
1536 qunlock(&p->seglock);
1540 for(i=0; i<NSEG; i++){
1541 if((s = p->seg[i]) != nil){
1543 pages += mcountseg(s);
1547 qunlock(&p->seglock);
1554 procctlmemio(Proc *p, uintptr offset, int n, void *va, int read)
1564 /* Only one page at a time */
1565 l = BY2PG - (offset&(BY2PG-1));
1570 * Make temporary copy to avoid fault while we have
1571 * segment locked as we would deadlock when trying
1572 * to read the calling procs memory.
1583 memmove(a, va, n); /* can fault */
1586 s = seg(p, offset, 0);
1590 eqlock(&p->seglock);
1592 qunlock(&p->seglock);
1596 for(i = 0; i < NSEG; i++) {
1601 error(Egreg); /* segment gone */
1608 if(!read && (s->type&SG_TYPE) == SG_TEXT) {
1613 qunlock(&p->seglock);
1616 /* segment s still locked, fixfault() unlocks */
1621 if(fixfault(s, offset, read, 0) == 0)
1628 * Only access the page while segment is locked
1629 * as the proc could segfree or relocate the pte
1637 if(offset+n >= s->top)
1639 soff = offset-s->base;
1640 pte = s->map[soff/PTEMAPMEM];
1642 error(Egreg); /* page gone, should retry? */
1643 pg = pte->pages[(soff&(PTEMAPMEM-1))/BY2PG];
1645 error(Egreg); /* page gone, should retry? */
1647 /* Map and copy the page */
1650 b += offset&(BY2PG-1);
1657 /* Ensure the process sees text page changes */
1659 memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
1670 memmove(va, a, n); /* can fault */