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 ulong procpagecount(Proc *);
160 static Traceevent *tevents;
163 static int tproduced, tconsumed;
164 void (*proctrace)(Proc*, int, vlong);
169 profclock(Ureg *ur, Timer *)
173 if(up == nil || up->state != Running)
176 /* user profiling clock */
178 tos = (Tos*)(USTKTOP-sizeof(Tos));
179 tos->clock += TK2MS(1);
185 procgen(Chan *c, char *name, Dirtab *tab, int, int s, Dir *dp)
191 ulong pid, path, perm, len;
194 mkqid(&qid, Qdir, 0, QTDIR);
195 devdir(c, qid, "#p", 0, eve, 0555, dp);
199 if(c->qid.path == Qdir){
201 strcpy(up->genbuf, "trace");
202 mkqid(&qid, Qtrace, -1, QTFILE);
203 devdir(c, qid, up->genbuf, 0, eve, 0444, dp);
208 /* ignore s and use name to find pid */
209 pid = strtol(name, &ename, 10);
210 if(pid==0 || ename[0]!='\0')
216 else if(--s >= conf.nproc)
223 sprint(up->genbuf, "%lud", pid);
225 * String comparison is done in devwalk so name must match its formatted pid
227 if(name != nil && strcmp(name, up->genbuf) != 0)
229 mkqid(&qid, (s+1)<<QSHIFT, pid, QTDIR);
230 devdir(c, qid, up->genbuf, 0, p->user, DMDIR|0555, dp);
233 if(c->qid.path == Qtrace){
234 strcpy(up->genbuf, "trace");
235 mkqid(&qid, Qtrace, -1, QTFILE);
236 devdir(c, qid, up->genbuf, 0, eve, 0444, dp);
239 if(s >= nelem(procdir))
245 path = c->qid.path&~(((1<<QSHIFT)-1)); /* slot component */
247 /* p->procmode determines default mode for files in /proc */
248 p = proctab(SLOT(c->qid));
252 else /* just copy read bits */
253 perm |= p->procmode & 0444;
256 switch(QID(c->qid)) {
258 len = p->nwait; /* incorrect size, but >0 means there's something to read */
262 if(q != nil && q->profile != nil) {
263 len = (q->top-q->base)>>LRESPROF;
264 len *= sizeof(*q->profile);
269 mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
270 devdir(c, qid, tab->name, len, p->user, perm, dp);
275 _proctrace(Proc* p, Tevent etype, vlong ts)
279 if (p->trace == 0 || topens == 0 ||
280 tproduced - tconsumed >= Nevents)
283 te = &tevents[tproduced&Emask];
287 te->time = todget(nil);
296 if(conf.nproc >= (1<<(16-QSHIFT))-1)
297 print("warning: too many procs for devproc\n");
298 addclock0link((void (*)(void))profclock, 113); /* Relative prime to HZ */
302 procattach(char *spec)
304 return devattach('p', spec);
308 procwalk(Chan *c, Chan *nc, char **name, int nname)
310 return devwalk(c, nc, name, nname, 0, 0, procgen);
314 procstat(Chan *c, uchar *db, int n)
316 return devstat(c, db, n, 0, 0, procgen);
320 * none can't read or write state on other
321 * processes. This is to contain access of
322 * servers running as none should they be
323 * subverted by, for example, a stack attack.
330 if(strcmp(up->user, "none") != 0)
338 procopen(Chan *c, int omode)
345 if(c->qid.type & QTDIR)
346 return devopen(c, omode, 0, 0, procgen);
348 if(QID(c->qid) == Qtrace){
358 error("already open");
360 tevents = (Traceevent*)malloc(sizeof(Traceevent) * Nevents);
363 tproduced = tconsumed = 0;
365 proctrace = _proctrace;
369 c->mode = openmode(omode);
375 p = proctab(SLOT(c->qid));
385 omode = openmode(omode);
438 c->pgrpid.path = pg->pgrpid+1;
439 c->pgrpid.vers = p->noteid;
443 print("procopen %#lux\n", QID(c->qid));
447 /* Affix pid to qid */
449 c->qid.vers = p->pid;
451 /* make sure the process slot didn't get reallocated while we were playing */
456 tc = devopen(c, omode, 0, 0, procgen);
464 procwstat(Chan *c, uchar *db, int n)
469 if(c->qid.type&QTDIR)
472 if(QID(c->qid) == Qtrace)
473 return devwstat(c, db, n);
475 p = proctab(SLOT(c->qid));
486 if(p->pid != PID(c->qid))
489 if(strcmp(up->user, p->user) != 0 && !iseve())
492 d = smalloc(sizeof(Dir)+n);
493 n = convM2D(db, n, &d[0], (char*)&d[1]);
496 if(!emptystr(d->uid) && strcmp(d->uid, p->user) != 0){
499 kstrdup(&p->user, d->uid);
501 /* p->procmode determines default mode for files in /proc */
503 p->procmode = d->mode&0777;
514 if(QID(c->qid) == Qtrace && (c->flag & COPEN) != 0){
525 procargs(Proc *p, char *buf, int nbuf)
533 snprint(buf, nbuf, "%s [%s]", p->text, p->args);
537 for(j = 0; j < nbuf - 1; j += m){
542 m = snprint(buf+j, nbuf-j, "%q", a);
551 eventsavailable(void *)
553 return tproduced > tconsumed;
557 prochaswaitq(void *x)
563 p = proctab(SLOT(c->qid));
564 return p->pid != PID(c->qid) || p->waitq != nil;
568 int2flag(int flag, char *s)
587 readns1(Chan *c, Proc *p, char *buf, int nbuf)
592 ulong minid, bestmid;
597 if(pg == nil || p->dot == nil || p->pid != PID(c->qid))
609 for(i = 0; i < MNTHASH; i++) {
610 for(f = pg->mnthash[i]; f != nil; f = f->hash) {
611 for(t = f->mount; t != nil; t = t->next) {
612 if(t->mountid >= minid && t->mountid < bestmid) {
613 bestmid = t->mountid;
623 i = snprint(buf, nbuf, "cd %s\n", p->dot->path->s);
625 c->nrock = bestmid+1;
627 int2flag(cm->mflag, flag);
628 if(strcmp(cm->to->path->s, "#M") == 0){
629 srv = srvname(cm->to->mchan);
630 i = snprint(buf, nbuf, "mount %s %s %s %s\n", flag,
631 srv==nil? cm->to->mchan->path->s : srv,
632 mh->from->path->s, cm->spec? cm->spec : "");
635 i = snprint(buf, nbuf, "bind %s %s %s\n", flag,
636 cm->to->path->s, mh->from->path->s);
646 procfdprint(Chan *c, int fd, char *s, int ns)
648 return snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %lud %.2ux) %5ld %8lld %s\n",
650 &"r w rw"[(c->mode&3)<<1],
651 devtab[c->type]->dc, c->dev,
652 c->qid.path, c->qid.vers, c->qid.type,
653 c->iounit, c->offset, c->path->s);
657 readfd1(Chan *c, Proc *p, char *buf, int nbuf)
663 if(fg == nil || p->dot == nil || p->pid != PID(c->qid))
668 return snprint(buf, nbuf, "%s\n", p->dot->path->s);
675 if(i < 0 || i > fg->maxfd)
678 if(fg->fd[i] != nil){
679 n = procfdprint(fg->fd[i], i, buf, nbuf);
689 * userspace can't pass negative file offset for a
690 * 64 bit kernel address, so we use 63 bit and sign
702 procread(Chan *c, void *va, long n, vlong off)
704 char *a, *sps, statbuf[1024];
705 int i, j, navail, ne, rsize;
718 if(c->qid.type & QTDIR)
719 return devdirread(c, a, n, 0, 0, procgen);
721 if(QID(c->qid) == Qtrace){
722 if(!eventsavailable(nil))
726 navail = tproduced - tconsumed;
727 if(navail > n / sizeof(Traceevent))
728 navail = n / sizeof(Traceevent);
730 ne = ((tconsumed & Emask) + navail > Nevents)?
731 Nevents - (tconsumed & Emask): navail;
732 memmove(rptr, &tevents[tconsumed & Emask],
733 ne * sizeof(Traceevent));
736 rptr += ne * sizeof(Traceevent);
739 return rptr - (uchar*)va;
742 p = proctab(SLOT(c->qid));
743 if(p->pid != PID(c->qid))
749 j = procargs(p, statbuf, sizeof(statbuf));
756 memmove(a, statbuf+offset, n);
765 if(p->pid != PID(c->qid))
768 if(p->syscalltrace != nil)
769 j = 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[0]);
807 if(i < 0 || offset >= i)
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 i = strlen(p->note[0].msg) + 1;
830 memmove(a, p->note[0].msg, n-1);
834 memmove(p->note, p->note+1, p->nnote*sizeof(Note));
841 if(offset >= sizeof(Proc))
843 if(offset+n > sizeof(Proc))
844 n = sizeof(Proc) - offset;
845 memmove(a, ((char*)p)+offset, n);
849 rptr = (uchar*)p->dbgreg;
850 rsize = sizeof(Ureg);
854 memset(&kur, 0, sizeof(Ureg));
857 rsize = sizeof(Ureg);
861 rptr = (uchar*)&p->fpsave;
862 rsize = sizeof(FPsave);
870 memmove(a, rptr+offset, n);
874 if(offset >= STATSIZE)
876 if(offset+n > STATSIZE)
877 n = STATSIZE - offset;
881 sps = statename[p->state];
883 memset(statbuf, ' ', sizeof statbuf);
884 readstr(0, statbuf+0*KNAMELEN, KNAMELEN-1, p->text);
885 readstr(0, statbuf+1*KNAMELEN, KNAMELEN-1, p->user);
886 readstr(0, statbuf+2*KNAMELEN, 11, sps);
889 for(i = 0; i < 6; i++) {
892 l = MACHP(0)->ticks - l;
894 readnum(0, statbuf+j+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
897 readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, procpagecount(p)*BY2PG/1024, NUMSIZE);
898 readnum(0, statbuf+j+NUMSIZE*7, NUMSIZE, p->basepri, NUMSIZE);
899 readnum(0, statbuf+j+NUMSIZE*8, NUMSIZE, p->priority, NUMSIZE);
904 for(i = 0; i < NSEG; i++) {
908 j += sprint(statbuf+j, "%-6s %c%c %8p %8p %4ld\n",
909 sname[sg->type&SG_TYPE],
910 sg->type&SG_RONLY ? 'R' : ' ',
911 sg->profile ? 'P' : ' ',
912 sg->base, sg->top, sg->ref);
921 if(!canqlock(&p->qwaitr))
930 while(p->waitq == nil && p->pid == PID(c->qid)) {
931 if(up == p && p->nchild == 0) {
936 sleep(&p->waitr, prochaswaitq, c);
939 if(p->pid != PID(c->qid)){
951 j = snprint(statbuf, sizeof(statbuf), "%d %lud %lud %lud %q",
953 wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal],
968 if(offset == 0 || offset != c->mrock)
969 c->nrock = c->mrock = 0;
971 if(QID(c->qid) == Qns)
972 j = readns1(c, p, statbuf, sizeof(statbuf));
974 j = readfd1(c, p, statbuf, sizeof(statbuf));
978 } while(c->mrock <= offset);
979 i = c->mrock - offset;
991 return readnum(offset, va, n, p->noteid, NUMSIZE);
994 return readnum(offset, va, n, p->parentpid, NUMSIZE);
998 return 0; /* not reached */
1002 procwrite(Chan *c, void *va, long n, vlong off)
1006 char *a, *arg, buf[ERRMAX];
1011 if(c->qid.type & QTDIR)
1014 p = proctab(SLOT(c->qid));
1016 /* Use the remembered noteid in the channel rather
1017 * than the process pgrpid
1019 if(QID(c->qid) == Qnotepg) {
1020 pgrpnote(NOTEID(c->pgrpid), va, n, NUser);
1029 if(p->pid != PID(c->qid))
1032 switch(QID(c->qid)){
1041 memmove(arg, va, n);
1052 if(p->state != Stopped)
1054 n = procctlmemio(p, off2addr(off), n, va, 0);
1058 if(offset >= sizeof(Ureg))
1060 else if(offset+n > sizeof(Ureg))
1061 n = sizeof(Ureg) - offset;
1062 if(p->dbgreg == nil)
1064 setregisters(p->dbgreg, (char*)(p->dbgreg)+offset, va, n);
1068 if(offset >= sizeof(FPsave))
1070 else if(offset+n > sizeof(FPsave))
1071 n = sizeof(FPsave) - offset;
1072 memmove((uchar*)&p->fpsave+offset, va, n);
1076 procctlreq(p, va, n);
1084 memmove(buf, va, n);
1086 if(!postnote(p, 0, buf, NUser))
1087 error("note not posted");
1100 for(et = t+conf.nproc; t < et; t++) {
1101 if(t->state == Dead || t->kp)
1103 if(id == t->noteid) {
1105 if(strcmp(p->user, t->user) != 0)
1115 print("unknown qid in procwrite\n");
1145 proctext(Chan *c, Proc *p)
1174 if(incref(tc) == 1 || (tc->flag&COPEN) == 0 || tc->mode != OREAD) {
1179 if(p->pid != PID(c->qid)) {
1191 procstopwait(Proc *p, int ctl)
1197 if(procstopped(p) || p->state == Broken)
1206 up->psstate = "Stopwait";
1212 sleep(&up->sleep, procstopped, p);
1220 procctlclosefiles(Proc *p, int all, int fd)
1233 while(fd <= f->maxfd){
1252 parsetime(vlong *rt, char *s)
1257 static int p10[] = {100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};
1260 return("missing value");
1261 ticks=strtoul(s, &e, 10);
1264 l = strtoul(p, &e, 10);
1265 if(e-p > nelem(p10))
1266 return "too many digits after decimal point";
1268 return "ill-formed number";
1272 if (*e == '\0' || strcmp(e, "s") == 0){
1273 ticks = 1000000000 * ticks + l;
1274 }else if (strcmp(e, "ms") == 0){
1275 ticks = 1000000 * ticks + l/1000;
1276 }else if (strcmp(e, "µs") == 0 || strcmp(e, "us") == 0){
1277 ticks = 1000 * ticks + l/1000000;
1278 }else if (strcmp(e, "ns") != 0)
1279 return "unrecognized unit";
1285 procctlreq(Proc *p, char *va, int n)
1293 void (*pt)(Proc*, int, vlong);
1295 if(p->kp) /* no ctl requests to kprocs */
1298 cb = parsecmd(va, n);
1304 ct = lookupcmd(cb, proccmd, nelem(proccmd));
1308 procctlclosefiles(p, 0, atoi(cb->f[1]));
1311 procctlclosefiles(p, 1, 0);
1322 p->procctl = Proc_exitme;
1323 postnote(p, 0, "sys: killed", NExit);
1327 p->procctl = Proc_exitme;
1328 postnote(p, 0, "sys: killed", NExit);
1338 pri = atoi(cb->f[1]);
1339 if(pri > PriNormal && !iseve())
1341 procpriority(p, pri, 0);
1344 pri = atoi(cb->f[1]);
1345 if(pri > PriNormal && !iseve())
1347 procpriority(p, pri, 1);
1354 if(s == nil || (s->type&SG_TYPE) != SG_TEXT)
1356 if(s->profile != nil)
1358 npc = (s->top-s->base)>>LRESPROF;
1359 s->profile = malloc(npc*sizeof(*s->profile));
1360 if(s->profile == nil)
1364 if(p->state != Stopped)
1369 if(p->state != Stopped)
1371 p->procctl = Proc_traceme;
1373 procstopwait(p, Proc_traceme);
1375 case CMstartsyscall:
1376 if(p->state != Stopped)
1378 p->procctl = Proc_tracesyscall;
1380 procstopwait(p, Proc_tracesyscall);
1383 procstopwait(p, Proc_stopme);
1389 procwired(p, atoi(cb->f[1]));
1397 p->trace = (atoi(cb->f[1]) != 0);
1404 postnote(p, 0, nil, NUser);
1410 error("notes pending");
1416 if(e=parsetime(&time, cb->f[1])) /* time in ns */
1419 p->edf->T = time/1000; /* Edf times are in µs */
1424 if(e=parsetime(&time, cb->f[1]))
1427 p->edf->D = time/1000;
1432 if(e=parsetime(&time, cb->f[1]))
1435 p->edf->C = time/1000;
1440 p->edf->flags |= Sporadic;
1442 case CMdeadlinenotes:
1445 p->edf->flags |= Sendnotes;
1449 error("edf params");
1456 p->edf->flags |= Extratime;
1464 if(up->trace && pt != nil)
1474 procstopped(void *a)
1476 return ((Proc*)a)->state == Stopped;
1480 procctlmemio(Proc *p, uintptr offset, int n, void *va, int read)
1490 /* Only one page at a time */
1491 l = BY2PG - (offset&(BY2PG-1));
1496 * Make temporary copy to avoid fault while we have
1497 * segment locked as we would deadlock when trying
1498 * to read the calling procs memory.
1509 memmove(a, va, n); /* can fault */
1512 s = seg(p, offset, 0);
1516 eqlock(&p->seglock);
1518 qunlock(&p->seglock);
1522 for(i = 0; i < NSEG; i++) {
1527 error(Egreg); /* segment gone */
1534 if(!read && (s->type&SG_TYPE) == SG_TEXT) {
1539 qunlock(&p->seglock);
1542 /* segment s still locked, fixfault() unlocks */
1547 if(fixfault(s, offset, read, 0) == 0)
1554 * Only access the page while segment is locked
1555 * as the proc could segfree or relocate the pte
1563 if(offset+n >= s->top)
1565 soff = offset-s->base;
1566 pte = s->map[soff/PTEMAPMEM];
1568 error(Egreg); /* page gone, should retry? */
1569 pg = pte->pages[(soff&(PTEMAPMEM-1))/BY2PG];
1571 error(Egreg); /* page gone, should retry? */
1573 /* Map and copy the page */
1576 b += offset&(BY2PG-1);
1583 /* Ensure the process sees text page changes */
1585 memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
1596 memmove(va, a, n); /* can fault */