cae36a52 |
1 | #include <u.h> |
2 | #include <libc.h> |
3 | #include <ureg.h> |
4 | #include <mp.h> |
5 | #include <libsec.h> |
6 | |
7 | #include "dat.h" |
8 | #include "fns.h" |
9 | #include "linux.h" |
10 | |
11 | enum { |
12 | Qproc, |
13 | Qstat, |
14 | Qcpuinfo, |
15 | Qmeminfo, |
16 | Quptime, |
17 | Qloadavg, |
18 | Qself, |
19 | Qpid, |
20 | Qcwd, |
21 | Qcmdline, |
22 | Qenviron, |
23 | Qexe, |
24 | Qroot, |
25 | Qpidstat, |
26 | Qpidstatm, |
27 | Qstatus, |
28 | Qmaps, |
29 | Qfd, |
30 | Qfd1, |
31 | Qtask, |
32 | Qtask1, |
33 | Qmax, |
34 | }; |
35 | |
36 | static struct { |
37 | int mode; |
38 | char *name; |
39 | } procdevtab[] = { |
40 | 0555|S_IFDIR, "proc", |
41 | 0444|S_IFREG, "stat", |
42 | 0444|S_IFREG, "cpuinfo", |
43 | 0444|S_IFREG, "meminfo", |
44 | 0444|S_IFREG, "uptime", |
45 | 0444|S_IFREG, "loadavg", |
46 | 0777|S_IFLNK, "self", |
47 | 0555|S_IFDIR, "###", |
48 | 0777|S_IFLNK, "cwd", |
49 | 0444|S_IFREG, "cmdline", |
50 | 0444|S_IFREG, "environ", |
51 | 0777|S_IFLNK, "exe", |
52 | 0777|S_IFLNK, "root", |
53 | 0444|S_IFREG, "stat", |
54 | 0444|S_IFREG, "statm", |
55 | 0444|S_IFREG, "status", |
56 | 0444|S_IFREG, "maps", |
57 | 0555|S_IFDIR, "fd", |
58 | 0777|S_IFLNK, "###", |
59 | 0555|S_IFDIR, "task", |
60 | 0555|S_IFDIR, "###", |
61 | }; |
62 | |
63 | typedef struct Procfile Procfile; |
64 | struct Procfile |
65 | { |
66 | Ufile; |
67 | int q; |
68 | int pid; |
69 | vlong lastoff; |
70 | char *data; |
71 | int ndata; |
72 | }; |
73 | |
74 | static int |
75 | path2q(char *path, int *ppid, int *pfd) |
76 | { |
77 | int i, q, pid, fd; |
78 | char *x; |
79 | |
80 | q = -1; |
81 | pid = -1; |
82 | fd = -1; |
83 | path++; |
84 | for(i=Qproc; i<Qmax; i++){ |
85 | if(x = strchr(path, '/')) |
86 | *x = 0; |
87 | if(path[0]>='0' && path[0]<='9'){ |
88 | switch(i){ |
89 | case Qpid: |
90 | case Qtask1: |
91 | pid = atoi(path); |
92 | goto match; |
93 | case Qfd1: |
94 | fd = atoi(path); |
95 | goto match; |
96 | } |
97 | } |
98 | if(strcmp(path, procdevtab[i].name) == 0){ |
99 | match: if(x == nil){ |
100 | q = i; |
101 | break; |
102 | } |
103 | if(i == Qself){ /* hack */ |
104 | pid = current->pid; |
105 | i = Qpid; |
106 | } |
107 | if((procdevtab[i].mode & ~0777) == S_IFDIR){ |
108 | path = x+1; |
109 | if(i == Qtask1) |
110 | i = Qpid; |
111 | } |
112 | } |
113 | if(x != nil) |
114 | *x = '/'; |
115 | } |
116 | if(ppid) |
117 | *ppid = pid; |
118 | if(pfd) |
119 | *pfd = fd; |
120 | return q; |
121 | } |
122 | |
123 | /* |
124 | * the proc device also implements the functionality |
125 | * for /dev/std^(in out err) and /dev/fd. we just |
126 | * rewrite the path to the names used in /proc. |
127 | */ |
128 | static char* |
129 | rewritepath(char *path) |
130 | { |
131 | if(strcmp(path, "/dev/stdin")==0){ |
132 | path = kstrdup("/proc/self/fd/0"); |
133 | } else if(strcmp(path, "/dev/stdout")==0){ |
134 | path = kstrdup("/proc/self/fd/1"); |
135 | } else if(strcmp(path, "/dev/stderr")==0){ |
136 | path = kstrdup("/proc/self/fd/2"); |
137 | } else if(strncmp(path, "/dev/fd", 7) == 0){ |
138 | path = allocpath("/proc/self", "fd", path+7); |
139 | } else { |
140 | path = kstrdup(path); |
141 | } |
142 | return path; |
143 | } |
144 | |
145 | static int |
146 | readlinkproc(char *path, char *buf, int len); |
147 | |
148 | static int |
149 | openproc(char *path, int mode, int perm, Ufile **pf) |
150 | { |
151 | char buf[256], *t; |
152 | int n, q, pid, err; |
153 | Procfile *f; |
154 | |
155 | err = -ENOENT; |
156 | path = rewritepath(path); |
157 | if((q = path2q(path, &pid, nil)) < 0) |
158 | goto out; |
159 | if((procdevtab[q].mode & ~0777) == S_IFLNK){ |
160 | n = readlinkproc(path, buf, sizeof(buf)-1); |
161 | if(n > 0){ |
162 | buf[n] = 0; |
163 | err = fsopen(buf, mode, perm, pf); |
164 | } |
165 | goto out; |
166 | } |
167 | if((mode & O_ACCMODE) != O_RDONLY){ |
168 | err = -EPERM; |
169 | goto out; |
170 | } |
171 | if(q >= Qpid){ |
172 | qlock(&proctab); |
173 | if(getproc(pid) == nil){ |
174 | qunlock(&proctab); |
175 | goto out; |
176 | } |
177 | qunlock(&proctab); |
178 | } |
179 | |
180 | /* hack */ |
181 | if(strncmp(path, "/proc/self", 10) == 0){ |
182 | t = ksmprint("/proc/%d%s", pid, path+10); |
183 | free(path); path = t; |
184 | } |
185 | |
186 | f = kmallocz(sizeof(*f), 1); |
187 | f->ref = 1; |
188 | f->mode = mode; |
189 | f->path = path; path = nil; |
190 | f->fd = -1; |
191 | f->dev = PROCDEV; |
192 | f->q = q; |
193 | f->pid = pid; |
194 | *pf = f; |
195 | err = 0; |
196 | |
197 | out: |
198 | free(path); |
199 | return err; |
200 | } |
201 | |
202 | static int |
203 | closeproc(Ufile *file) |
204 | { |
205 | Procfile *f = (Procfile*)file; |
206 | |
207 | if(f->data) |
208 | free(f->data); |
209 | return 0; |
210 | } |
211 | |
212 | enum { |
213 | SScpu, |
214 | SSswitches, |
215 | SSinterrupts, |
216 | SSsyscalls, |
217 | SSpagefaults, |
218 | SStlbmisses, |
219 | SStlbpurges, |
220 | SSloadavg, |
221 | SSidletime, |
222 | SSintrtime, |
223 | SSmax, |
224 | }; |
225 | |
226 | static char* |
227 | sysstat(ulong *prun, ulong *pidle, ulong *pload) |
228 | { |
229 | char buf[1024], *p, *e, *t, *data; |
230 | ulong dt, swtch, user, sys, load; |
231 | static ulong run, idle, intr; |
232 | int n, fd; |
233 | |
234 | data = nil; |
235 | swtch = user = sys = load = 0; |
236 | |
237 | dt = (ulong)(((nsec() - boottime) * HZ) / 1000000000LL) - run; |
238 | run += dt; |
239 | |
240 | n = 0; |
241 | if((fd = open("/dev/sysstat", OREAD)) >= 0){ |
242 | n = read(fd, buf, sizeof(buf)-1); |
243 | close(fd); |
244 | } |
245 | if(n > 0){ |
246 | buf[n] = 0; |
247 | p = buf; |
248 | while(e = strchr(p, '\n')){ |
249 | char *f[SSmax]; |
250 | *e = 0; |
251 | if(getfields(p, f, SSmax, 1, "\t ") != SSmax) |
252 | break; |
253 | |
254 | if(p == buf){ |
255 | swtch += atoi(f[SSswitches]); |
256 | |
257 | idle += (atoi(f[SSidletime]) * dt)/100; |
258 | intr += (atoi(f[SSintrtime]) * dt)/100; |
259 | |
260 | load = 100-atoi(f[SSidletime]); |
261 | |
262 | user = run - idle - intr; |
263 | sys = run - user; |
264 | |
265 | data = ksmprint("cpu %lud %lud %lud %lud %lud %lud %lud\n", |
266 | user, 0UL, sys, idle, 0UL, intr, 0UL); |
267 | } |
268 | t = ksmprint("%scpu%d %lud %lud %lud %lud %lud %lud %lud\n", |
269 | data, atoi(f[SScpu]), user, 0UL, sys, idle, 0UL, intr, 0UL); |
270 | free(data); |
271 | data = t; |
272 | |
273 | p = e+1; |
274 | } |
275 | t = ksmprint("%sbtime %lud\nctxt %lud\n", data, |
276 | (ulong)(boottime/1000000000LL), swtch); |
277 | free(data); |
278 | data = t; |
279 | } |
280 | if(prun) |
281 | *prun = run; |
282 | if(pidle) |
283 | *pidle = idle; |
284 | if(pload) |
285 | *pload = load; |
286 | |
287 | return data; |
288 | } |
289 | |
290 | static char* |
291 | procstat(Uproc *p) |
292 | { |
293 | return |
294 | (p->wstate & WEXITED) ? "Z (zombie)" : |
295 | (p->wstate & WSTOPPED) ? "T (stopped)" : |
296 | (p->state == nil) ? "R (running)" : "S (sleeping)"; |
297 | } |
298 | |
299 | static char* |
300 | procname(Uproc *p) |
301 | { |
302 | char *s; |
303 | |
304 | p = getproc(p->pid); |
305 | if(p == nil || p->comm == nil) |
306 | return ""; |
307 | if(s = strrchr(p->comm, '/')) |
308 | return s+1; |
309 | return p->comm; |
310 | } |
311 | |
312 | |
313 | static void |
314 | gendata(Procfile *f) |
315 | { |
316 | char *s, *t; |
317 | int i, nproc, nready; |
318 | ulong tms[4]; |
319 | Uproc *p; |
320 | |
321 | f->ndata = 0; |
322 | if(s = f->data){ |
323 | f->data = nil; |
324 | free(s); |
325 | } |
326 | s = nil; |
327 | |
328 | if(f->q >= Qpid){ |
329 | ulong vmsize, vmdat, vmlib, vmshr, vmstk, vmexe; |
330 | |
331 | qlock(&proctab); |
332 | if((p = getproc(f->pid)) == nil){ |
333 | qunlock(&proctab); |
334 | return; |
335 | } |
336 | switch(f->q){ |
337 | case Qcmdline: |
338 | p = getproc(p->pid); |
339 | if(p == nil || p->comm == nil) |
340 | break; |
341 | i = strlen(p->comm)+1; |
342 | if(i >= p->ncomm-2) |
343 | break; |
344 | f->ndata = p->ncomm-i-2; |
345 | f->data = kmalloc(f->ndata); |
346 | memmove(f->data, p->comm + i, f->ndata); |
347 | qunlock(&proctab); |
348 | return; |
349 | |
350 | case Qenviron: |
351 | break; |
352 | case Qpidstat: |
353 | if(proctimes(p, tms) != 0) |
354 | memset(tms, 0, sizeof(tms)); |
355 | vmsize = procmemstat(p, nil, nil, nil, nil, nil); |
356 | s = ksmprint( |
357 | "%d (%s) %c %d %d %d %d %d %lud %lud " |
358 | "%lud %lud %lud %lud %lud %ld %ld %ld %ld %ld " |
359 | "%ld %lud %lud %ld %lud %lud %lud %lud %lud %lud " |
360 | "%lud %lud %lud %lud %lud %lud %lud %d %d\n", |
361 | p->tid, |
362 | procname(p), |
363 | procstat(p)[0], |
364 | p->ppid, |
365 | p->pgid, |
366 | p->psid, |
367 | 0, /* tty */ |
368 | 0, /* tty pgrp */ |
369 | 0UL, /* flags */ |
370 | 0UL, 0UL, 0UL, 0UL, /* pagefault stats */ |
371 | tms[0], /* utime */ |
372 | tms[1], /* stime */ |
373 | tms[2], /* cutime */ |
374 | tms[3], /* cstime */ |
375 | 0UL, /* priority */ |
376 | 0UL, /* nice */ |
377 | 0UL, /* always 0UL */ |
378 | 0UL, /* time to next alarm */ |
379 | (ulong)(((p->starttime - boottime) * HZ) / 1000000000LL), |
380 | vmsize, /* vm size in bytes */ |
381 | vmsize, /* vm working set */ |
382 | 0UL, /* rlim */ |
383 | p->codestart, |
384 | p->codeend, |
385 | p->stackstart, |
386 | 0UL, /* SP */ |
387 | 0UL, /* PC */ |
388 | 0UL, /* pending signal mask */ |
389 | 0UL, /* blocked signal mask */ |
390 | 0UL, /* ignored signal mask */ |
391 | 0UL, /* catched signal mask */ |
392 | 0UL, /* wchan */ |
393 | 0UL, /* nswap */ |
394 | 0UL, /* nswap children */ |
395 | p->exitsignal, |
396 | 0); /* cpu */ |
397 | break; |
398 | case Qpidstatm: |
399 | vmsize = procmemstat(p, &vmdat, &vmlib, &vmshr, &vmstk, &vmexe); |
400 | s = ksmprint("%lud %lud %lud %lud %lud %lud %lud\n", |
401 | vmsize/PAGESIZE, vmsize/PAGESIZE, vmshr/PAGESIZE, |
402 | vmexe/PAGESIZE, vmstk/PAGESIZE, vmlib/PAGESIZE, 0UL); |
403 | break; |
404 | case Qstatus: |
405 | s = ksmprint( |
406 | "Name:\t%s\n" |
407 | "State:\t%s\n" |
408 | "Tgid:\t%d\n" |
409 | "Pid:\t%d\n" |
410 | "PPid:\t%d\n" |
411 | "Uid:\t%d\t%d\t%d\t%d\n" |
412 | "Gid:\t%d\t%d\t%d\t%d\n" |
413 | "FDSize:\t%d\n" |
414 | "Threads:\t%d\n", |
415 | procname(p), |
416 | procstat(p), |
417 | p->pid, |
418 | p->tid, |
419 | p->ppid, |
420 | p->uid, p->uid, p->uid, p->uid, |
421 | p->gid, p->gid, p->gid, p->gid, |
422 | MAXFD, |
423 | threadcount(p->pid)); |
424 | break; |
425 | case Qmaps: |
426 | break; |
427 | } |
428 | qunlock(&proctab); |
429 | } else { |
430 | ulong run, idle, load; |
431 | |
432 | nproc = nready = 0; |
433 | qlock(&proctab); |
434 | for(i=0; i<MAXPROC; i++){ |
435 | p = getprocn(i); |
436 | if(p == nil) |
437 | continue; |
438 | nproc++; |
439 | if(p->state == nil) |
440 | nready++; |
441 | } |
442 | i = proctab.nextpid; |
443 | qunlock(&proctab); |
444 | |
445 | switch(f->q){ |
446 | case Qstat: |
447 | s = sysstat(nil, nil, nil); |
448 | t = ksmprint( |
449 | "%s" |
450 | "processes %d\n" |
451 | "procs_running %d\n" |
452 | "procs_blocked %d\n", |
453 | s, |
454 | i, |
455 | nready, |
456 | nproc-nready); |
457 | free(s); |
458 | s = t; |
459 | break; |
460 | case Qcpuinfo: |
461 | break; |
462 | case Qmeminfo: |
463 | break; |
464 | case Quptime: |
465 | free(sysstat(&run, &idle, nil)); |
466 | s = ksmprint("%lud.%lud %lud.%lud\n", run/HZ, run%HZ, idle/HZ, idle%HZ); |
467 | break; |
468 | case Qloadavg: |
469 | free(sysstat(nil, nil, &load)); |
470 | s = ksmprint("%lud.%lud 0 0 %d/%d %d\n", load/100, load%100, nready, nproc, i); |
471 | break; |
472 | } |
473 | } |
474 | |
475 | f->data = s; |
476 | f->ndata = s ? strlen(s) : 0; |
477 | } |
478 | |
479 | static vlong |
480 | sizeproc(Ufile *file) |
481 | { |
482 | Procfile *f = (Procfile*)file; |
483 | |
484 | if(f->data == nil) |
485 | gendata(f); |
486 | return f->ndata; |
487 | } |
488 | |
489 | static int |
490 | readproc(Ufile *file, void *buf, int len, vlong off) |
491 | { |
492 | Procfile *f = (Procfile*)file; |
493 | int ret; |
494 | |
495 | if((f->data == nil) || (off != f->lastoff)) |
496 | gendata(f); |
497 | ret = 0; |
498 | if(f->data && (off < f->ndata)){ |
499 | ret = f->ndata - off; |
500 | if(ret > len) |
501 | ret = len; |
502 | memmove(buf, f->data + off, ret); |
503 | f->lastoff = off + ret; |
504 | } |
505 | return ret; |
506 | } |
507 | |
508 | static int |
509 | readlinkproc(char *path, char *buf, int len) |
510 | { |
511 | int err, q, pid, fd; |
512 | char *data; |
513 | Uproc *p; |
514 | Ufile *a; |
515 | |
516 | err = -ENOENT; |
517 | path = rewritepath(path); |
518 | if((q = path2q(path, &pid, &fd)) < 0) |
519 | goto out; |
520 | data = nil; |
521 | if(q >= Qpid){ |
522 | qlock(&proctab); |
523 | if((p = getproc(pid)) == nil){ |
524 | qunlock(&proctab); |
525 | goto out; |
526 | } |
527 | switch(q){ |
528 | case Qcwd: |
529 | data = kstrdup(p->cwd); |
530 | break; |
531 | case Qexe: |
532 | p = getproc(p->pid); |
533 | if(p == nil || p->comm == nil) |
534 | break; |
535 | data = kstrdup(p->comm); |
536 | break; |
537 | case Qroot: |
538 | data = kstrdup(p->root ? p->root : "/"); |
539 | break; |
540 | case Qfd1: |
541 | a = procfdgetfile(p, fd); |
542 | if(a == nil || a->path == nil){ |
543 | putfile(a); |
544 | qunlock(&proctab); |
545 | goto out; |
546 | } |
547 | data = kstrdup(a->path); |
548 | putfile(a); |
549 | break; |
550 | } |
551 | qunlock(&proctab); |
552 | } else { |
553 | switch(q){ |
554 | case Qself: |
555 | data = ksmprint("/proc/%d", current->pid); |
556 | break; |
557 | } |
558 | } |
559 | err = 0; |
560 | if(data){ |
561 | err = strlen(data); |
562 | if(err > len) |
563 | err = len; |
564 | memmove(buf, data, err); |
565 | free(data); |
566 | } |
567 | out: |
568 | free(path); |
569 | return err; |
570 | } |
571 | |
572 | static int |
573 | readdirproc(Ufile *file, Udirent **pd) |
574 | { |
575 | Procfile *f = (Procfile*)file; |
576 | char buf[12]; |
577 | Uproc *p; |
578 | Ufile *a; |
579 | int n, i; |
580 | |
581 | n = 0; |
582 | switch(f->q){ |
583 | case Qproc: |
584 | for(i=f->q+1; (procdevtab[i].mode & ~0777) != S_IFDIR; i++){ |
585 | if((*pd = newdirent(f->path, procdevtab[i].name, procdevtab[i].mode)) == nil) |
586 | break; |
587 | pd = &((*pd)->next); |
588 | n++; |
589 | } |
590 | /* no break */ |
591 | case Qtask: |
592 | qlock(&proctab); |
593 | for(i=0; i<MAXPROC; i++){ |
594 | p = getprocn(i); |
595 | if(p == nil) |
596 | continue; |
597 | if((f->q == Qproc) && (p->pid != p->tid)) |
598 | continue; |
599 | if((f->q == Qtask) && (p->pid != f->pid)) |
600 | continue; |
601 | snprint(buf, sizeof(buf), "%d", p->tid); |
602 | if((*pd = newdirent(f->path, buf, procdevtab[i].mode)) == nil) |
603 | break; |
604 | pd = &((*pd)->next); |
605 | n++; |
606 | } |
607 | qunlock(&proctab); |
608 | break; |
609 | |
610 | case Qpid: |
611 | if((*pd = newdirent(f->path, procdevtab[Qtask].name, procdevtab[Qtask].mode)) == nil) |
612 | break; |
613 | pd = &((*pd)->next); |
614 | n++; |
615 | /* no break */ |
616 | case Qtask1: |
617 | if((*pd = newdirent(f->path, procdevtab[Qfd].name, procdevtab[Qfd].mode)) == nil) |
618 | break; |
619 | pd = &((*pd)->next); |
620 | n++; |
621 | for(i=Qpid+1; (procdevtab[i].mode & ~0777) != S_IFDIR; i++){ |
622 | if((*pd = newdirent(f->path, procdevtab[i].name, procdevtab[i].mode)) == nil) |
623 | break; |
624 | pd = &((*pd)->next); |
625 | n++; |
626 | } |
627 | break; |
628 | |
629 | case Qfd: |
630 | qlock(&proctab); |
631 | if((p = getproc(f->pid)) == nil){ |
632 | qunlock(&proctab); |
633 | break; |
634 | } |
635 | for(i=0; i<MAXFD; i++){ |
636 | a = procfdgetfile(p, i); |
637 | if(a == nil || a->path == nil){ |
638 | putfile(a); |
639 | continue; |
640 | } |
641 | putfile(a); |
642 | snprint(buf, sizeof(buf), "%d", i); |
643 | if((*pd = newdirent(f->path, buf, procdevtab[Qfd1].mode)) == nil) |
644 | break; |
645 | pd = &((*pd)->next); |
646 | n++; |
647 | } |
648 | qunlock(&proctab); |
649 | break; |
650 | } |
651 | |
652 | return n; |
653 | } |
654 | |
655 | static int |
656 | statproc(char *path, int, Ustat *s) |
657 | { |
658 | int q, pid, fd, uid, gid, err; |
659 | ulong ctime; |
660 | Uproc *p; |
661 | Ufile *a; |
662 | |
663 | err = -ENOENT; |
664 | path = rewritepath(path); |
665 | if((q = path2q(path, &pid, &fd)) < 0) |
666 | goto out; |
667 | if(q >= Qpid){ |
668 | qlock(&proctab); |
669 | if((p = getproc(pid)) == nil){ |
670 | qunlock(&proctab); |
671 | goto out; |
672 | } |
673 | if(q == Qfd1){ |
674 | a = procfdgetfile(p, fd); |
675 | if(a == nil || a->path == nil){ |
676 | putfile(a); |
677 | qunlock(&proctab); |
678 | goto out; |
679 | } |
680 | putfile(a); |
681 | } |
682 | uid = p->uid; |
683 | gid = p->gid; |
684 | ctime = p->starttime/1000000000LL; |
685 | qunlock(&proctab); |
686 | } else { |
687 | uid = current->uid; |
688 | gid = current->gid; |
689 | ctime = boottime/1000000000LL; |
690 | } |
691 | err = 0; |
692 | s->mode = procdevtab[q].mode; |
693 | s->uid = uid; |
694 | s->gid = gid; |
695 | s->size = 0; |
696 | s->ino = hashpath(path); |
697 | s->dev = 0; |
698 | s->rdev = 0; |
699 | s->atime = s->mtime = s->ctime = ctime; |
700 | out: |
701 | free(path); |
702 | return err; |
703 | } |
704 | |
705 | static int |
706 | fstatproc(Ufile *f, Ustat *s) |
707 | { |
708 | return fsstat(f->path, 0, s); |
709 | }; |
710 | |
711 | static Udev procdev = |
712 | { |
713 | .open = openproc, |
714 | .read = readproc, |
715 | .size = sizeproc, |
716 | .readlink = readlinkproc, |
717 | .readdir = readdirproc, |
718 | .close = closeproc, |
719 | .stat = statproc, |
720 | .fstat = fstatproc, |
721 | }; |
722 | |
723 | void procdevinit(void) |
724 | { |
725 | devtab[PROCDEV] = &procdev; |
726 | |
727 | fsmount(&procdev, "/proc"); |
728 | fsmount(&procdev, "/dev/fd"); |
729 | fsmount(&procdev, "/dev/stdin"); |
730 | fsmount(&procdev, "/dev/stdout"); |
731 | fsmount(&procdev, "/dev/stderr"); |
732 | } |