add linux_emul base, reorganize docs
[openbsd_emul.git] / linux_emul_base / procdev.c
CommitLineData
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
11enum {
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
36static 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
63typedef struct Procfile Procfile;
64struct Procfile
65{
66 Ufile;
67 int q;
68 int pid;
69 vlong lastoff;
70 char *data;
71 int ndata;
72};
73
74static int
75path2q(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){
99match: 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 */
128static char*
129rewritepath(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
145static int
146readlinkproc(char *path, char *buf, int len);
147
148static int
149openproc(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
197out:
198 free(path);
199 return err;
200}
201
202static int
203closeproc(Ufile *file)
204{
205 Procfile *f = (Procfile*)file;
206
207 if(f->data)
208 free(f->data);
209 return 0;
210}
211
212enum {
213 SScpu,
214 SSswitches,
215 SSinterrupts,
216 SSsyscalls,
217 SSpagefaults,
218 SStlbmisses,
219 SStlbpurges,
220 SSloadavg,
221 SSidletime,
222 SSintrtime,
223 SSmax,
224};
225
226static char*
227sysstat(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
290static char*
291procstat(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
299static char*
300procname(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
313static void
314gendata(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
479static vlong
480sizeproc(Ufile *file)
481{
482 Procfile *f = (Procfile*)file;
483
484 if(f->data == nil)
485 gendata(f);
486 return f->ndata;
487}
488
489static int
490readproc(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
508static int
509readlinkproc(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 }
567out:
568 free(path);
569 return err;
570}
571
572static int
573readdirproc(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
655static int
656statproc(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;
700out:
701 free(path);
702 return err;
703}
704
705static int
706fstatproc(Ufile *f, Ustat *s)
707{
708 return fsstat(f->path, 0, s);
709};
710
711static 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
723void 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}