kernel/linprocfs: Implement /proc/devices.
[dragonfly.git] / sys / emulation / linux / i386 / linprocfs / linprocfs_misc.c
1 /*
2  * Copyright (c) 2000 Dag-Erling Coïdan Smørgrav
3  * Copyright (c) 1999 Pierre Beyssac
4  * Copyright (c) 1993 Jan-Simon Pendry
5  * Copyright (c) 1993
6  *      The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *      This product includes software developed by the University of
22  *      California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  *      @(#)procfs_status.c     8.4 (Berkeley) 6/15/94
40  *
41  * $FreeBSD: src/sys/i386/linux/linprocfs/linprocfs_misc.c,v 1.3.2.8 2001/06/25 19:46:47 pirzyk Exp $
42  */
43
44 #include <sys/param.h>
45 #include <sys/blist.h>
46 #include <sys/kernel.h>
47 #include <sys/kinfo.h>
48 #include <sys/proc.h>
49 #include <sys/jail.h>
50 #include <sys/resourcevar.h>
51 #include <sys/systm.h>
52 #include <sys/tty.h>
53 #include <sys/vnode.h>
54 #include <sys/lock.h>
55 #include <sys/sbuf.h>
56 #include <sys/mount.h>
57 #include <sys/sysctl.h>
58
59 #include <vm/vm.h>
60 #include <vm/pmap.h>
61 #include <vm/vm_map.h>
62 #include <vm/vm_param.h>
63 #include <vm/vm_object.h>
64 #include <vm/swap_pager.h>
65 #include <sys/vmmeter.h>
66 #include <sys/exec.h>
67 #include <sys/devfs.h>
68
69 #include <machine/clock.h>
70 #include <machine/cputypes.h>
71 #include <machine/inttypes.h>
72 #include <machine/md_var.h>
73 #include <machine/vmparam.h>
74
75 #include "linprocfs.h"
76 #include "../linux.h"
77 #include "../../linux_ioctl.h"
78 #include "../../linux_mib.h"
79
80 /*
81  * Various conversion macros
82  */
83 #define T2J(x) (((x) * 100) / (stathz ? stathz : hz))   /* ticks to jiffies */
84 #define T2S(x) ((x) / (stathz ? stathz : hz))           /* ticks to seconds */
85 #define B2K(x) ((unsigned long)((x) >> 10))                     /* bytes to kbytes */
86 #define P2B(x) ((x) << PAGE_SHIFT)                      /* pages to bytes */
87 #define P2K(x) ((x) << (PAGE_SHIFT - 10))               /* pages to kbytes */
88
89 int
90 linprocfs_domeminfo(struct proc *curp, struct proc *p, struct pfsnode *pfs,
91                     struct uio *uio)
92 {
93         char *ps;
94         char psbuf[512];                /* XXX - conservative */
95         unsigned long memtotal;         /* total memory in bytes */
96         unsigned long memused;          /* used memory in bytes */
97         unsigned long memfree;          /* free memory in bytes */
98         unsigned long memshared;        /* shared memory ??? */
99         unsigned long buffers, cached;  /* buffer / cache memory ??? */
100         unsigned long long swaptotal;   /* total swap space in bytes */
101         unsigned long long swapused;    /* used swap space in bytes */
102         unsigned long long swapfree;    /* free swap space in bytes */
103         vm_object_t object;
104
105         if (uio->uio_rw != UIO_READ)
106                 return (EOPNOTSUPP);
107
108         memtotal = Maxmem * PAGE_SIZE;
109         /*
110          * The correct thing here would be:
111          *
112         memfree = vmstats.v_free_count * PAGE_SIZE;
113         memused = memtotal - memfree;
114          *
115          * but it might mislead linux binaries into thinking there
116          * is very little memory left, so we cheat and tell them that
117          * all memory that isn't wired down is free.
118          */
119         memused = vmstats.v_wire_count * PAGE_SIZE;
120         memfree = memtotal - memused;
121         if (swapblist == NULL) {
122                 swaptotal = 0;
123                 swapfree = 0;
124         } else {
125                 swaptotal = swapblist->bl_blocks * 1024LL; /* XXX why 1024? */
126                 swapfree = (unsigned long long)swapblist->bl_root->u.bmu_avail * PAGE_SIZE;
127         }
128         swapused = swaptotal - swapfree;
129         memshared = 0;
130
131         lwkt_gettoken(&vmobj_token);
132         for (object = TAILQ_FIRST(&vm_object_list); object != NULL;
133             object = TAILQ_NEXT(object, object_list)) {
134                 if (object->type == OBJT_MARKER)
135                         continue;
136                 if (object->shadow_count > 1)
137                         memshared += object->resident_page_count;
138         }
139         lwkt_reltoken(&vmobj_token);
140         memshared *= PAGE_SIZE;
141         /*
142          * We'd love to be able to write:
143          *
144         buffers = bufspace;
145          *
146          * but bufspace is internal to vfs_bio.c and we don't feel
147          * like unstaticizing it just for linprocfs's sake.
148          */
149         buffers = 0;
150         cached = vmstats.v_cache_count * PAGE_SIZE;
151
152         ps = psbuf;
153         ps += ksprintf(ps,
154                 "        total:    used:    free:  shared: buffers:  cached:\n"
155                 "Mem:  %lu %lu %lu %lu %lu %lu\n"
156                 "Swap: %llu %llu %llu\n"
157                 "MemTotal: %9lu kB\n"
158                 "MemFree:  %9lu kB\n"
159                 "MemShared:%9lu kB\n"
160                 "Buffers:  %9lu kB\n"
161                 "Cached:   %9lu kB\n"
162                 "SwapTotal:%9lu kB\n"
163                 "SwapFree: %9lu kB\n",
164                 memtotal, memused, memfree, memshared, buffers, cached,
165                 swaptotal, swapused, swapfree,
166                 B2K(memtotal), B2K(memfree),
167                 B2K(memshared), B2K(buffers), B2K(cached),
168                 B2K(swaptotal), B2K(swapfree));
169
170         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
171 }
172
173 int
174 linprocfs_docpuinfo(struct proc *curp, struct proc *p, struct pfsnode *pfs,
175                     struct uio *uio)
176 {
177         char *ps;
178         char psbuf[8192];
179         char hwmodel[128];
180         size_t  modellen = sizeof(hwmodel);
181         int mib[] = { CTL_HW, HW_MODEL };
182         int class;
183         int cpu;
184         int i;
185         int error;
186 #if 0
187         extern char *cpu_model;         /* Yuck */
188 #endif
189         /* We default the flags to include all non-conflicting flags,
190            and the Intel versions of conflicting flags.  Note the space
191            before each name; that is significant, and should be 
192            preserved. */
193
194         static char *flags[] = {
195                 "fpu",      "vme",     "de",       "pse",      "tsc",
196                 "msr",      "pae",     "mce",      "cx8",      "apic",
197                 "sep",      "sep",     "mtrr",     "pge",      "mca",
198                 "cmov",     "pat",     "pse36",    "pn",       "b19",
199                 "b20",      "b21",     "mmxext",   "mmx",      "fxsr",
200                 "xmm",      "b26",     "b27",      "b28",      "b29",
201                 "3dnowext", "3dnow"
202         };
203
204         if (uio->uio_rw != UIO_READ)
205                 return (EOPNOTSUPP);
206
207         switch (cpu_class) {
208         case CPUCLASS_286:
209                 class = 2;
210                 break;
211         case CPUCLASS_386:
212                 class = 3;
213                 break;
214         case CPUCLASS_486:
215                 class = 4;
216                 break;
217         case CPUCLASS_586:
218                 class = 5;
219                 break;
220         case CPUCLASS_686:
221                 class = 6;
222                 break;
223         default:
224                 class = 0;
225                 break;
226         }
227
228         ps = psbuf;
229
230         error = kernel_sysctl(mib, 2, hwmodel, &modellen, NULL, 0, NULL);
231         if (error)
232                 strcpy(hwmodel, "unknown");
233
234         for (cpu = 0; cpu < ncpus; cpu++) {
235                 ps += ksprintf(ps,
236                     "processor\t: %d\n"
237                     "vendor_id\t: %.20s\n"
238                     "cpu family\t: %d\n"
239                     "model\t\t: %d\n"
240                     "model name\t: %s\n"
241                     "stepping\t: %d\n",
242                     cpu, cpu_vendor, class, cpu, hwmodel, cpu_id & 0xf);
243         }
244
245         ps += ksprintf(ps,
246                         "flags\t\t:");
247
248         if (cpu_vendor_id == CPU_VENDOR_AMD && (class < 6)) {
249                 flags[16] = "fcmov";
250         } else if (cpu_vendor_id == CPU_VENDOR_CYRIX) {
251                 flags[24] = "cxmmx";
252         }
253         
254         for (i = 0; i < 32; i++)
255                 if (cpu_feature & (1 << i))
256                         ps += ksprintf(ps, " %s", flags[i]);
257         ps += ksprintf(ps, "\n");
258         if (class >= 5) {
259                 ps += ksprintf(ps,
260                         "cpu MHz\t\t: %d.%02d\n"
261                         "bogomips\t: %d.%02d\n",
262                         (int)((tsc_frequency + 4999) / 1000000),
263                         (int)((tsc_frequency + 4999) / 10000) % 100,
264                         (int)((tsc_frequency + 4999) / 1000000),
265                         (int)((tsc_frequency + 4999) / 10000) % 100);
266         }
267         
268         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
269 }
270
271 static unsigned int
272 cpucnt(int offset)
273 {
274     int i;
275     int count = 0;
276
277     for (i = 0; i < ncpus; ++i) {
278         struct globaldata *gd = globaldata_find(i);
279         count += *(unsigned int *)((char *)&gd->gd_cnt + offset);
280     }
281     return(count);
282 }
283
284 static int
285 linprocfs_domounts_callback(struct mount *mp, void *data)
286 {
287         struct statfs *st;
288         struct sbuf *sb = (struct sbuf *)data;
289         char *to, *from, *fs;
290
291         st = &mp->mnt_stat;
292
293         from = st->f_mntfromname;
294         to = st->f_mntonname;
295         fs = st->f_fstypename;
296
297         if (!strcmp(st->f_fstypename, "linprocfs"))
298                 fs = "proc";
299         else if (!strcmp(st->f_fstypename, "ext2fs"))
300                 fs = "ext2";
301         else if (!strcmp(st->f_fstypename, "msdos"))
302                 fs = "vfat";
303         else if (!strcmp(st->f_fstypename, "msdosfs"))
304                 fs = "vfat";
305
306         sbuf_printf(sb, "%s %s %s %s", from, to, fs,
307             st->f_flags & MNT_RDONLY ? "ro" : "rw");
308
309 #define OPT_ADD(name, flag) if (st->f_flags & (flag)) sbuf_printf(sb, "," name)
310         OPT_ADD("sync",         MNT_SYNCHRONOUS);
311         OPT_ADD("noexec",       MNT_NOEXEC);
312         OPT_ADD("nosuid",       MNT_NOSUID);
313         OPT_ADD("nodev",        MNT_NODEV);
314         OPT_ADD("async",        MNT_ASYNC);
315         OPT_ADD("suiddir",      MNT_SUIDDIR);
316         OPT_ADD("nosymfollow",  MNT_NOSYMFOLLOW);
317         OPT_ADD("noatime",      MNT_NOATIME);
318 #undef OPT_ADD
319
320         sbuf_printf(sb, " 0 0\n");
321
322         return 0;
323 }
324
325 int
326 linprocfs_domounts(struct proc *curp, struct proc *p, struct pfsnode *pfs,
327                     struct uio *uio)
328 {
329         struct sbuf *sb;
330         int error;
331
332         sb = sbuf_new_auto();
333
334         error = mountlist_scan(linprocfs_domounts_callback, sb, MNTSCAN_FORWARD);
335
336         sbuf_finish(sb);
337         if (error == 0)
338                 error = uiomove_frombuf(sbuf_data(sb), sbuf_len(sb), uio);
339         sbuf_delete(sb);
340         return (error);
341 }
342
343 int
344 linprocfs_dostat(struct proc *curp, struct proc *p, struct pfsnode *pfs,
345                  struct uio *uio)
346 {
347         char *ps;
348         char psbuf[8192];
349         int cpu;
350
351         ps = psbuf;
352         ps += ksprintf(ps,
353                       "cpu %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64"\n",
354                       T2J(cpu_time.cp_user),
355                       T2J(cpu_time.cp_nice),
356                       T2J(cpu_time.cp_sys),
357                       T2J(cpu_time.cp_idle));
358
359         for (cpu = 0; cpu < ncpus; cpu++) {
360                 ps += ksprintf(ps,
361                       "cpu%d %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64"\n",
362                       cpu,
363                       T2J(cputime_percpu[cpu].cp_user),
364                       T2J(cputime_percpu[cpu].cp_nice),
365                       T2J(cputime_percpu[cpu].cp_sys),
366                       T2J(cputime_percpu[cpu].cp_idle));                        
367         }
368
369         ps += ksprintf(ps,
370                       "disk 0 0 0 0\n"
371                       "page %u %u\n"
372                       "swap %u %u\n"
373                       "intr %u\n"
374                       "ctxt %u\n"
375                       "btime %ld\n",
376                       cpucnt(offsetof(struct vmmeter, v_vnodepgsin)),
377                       cpucnt(offsetof(struct vmmeter, v_vnodepgsout)),
378                       cpucnt(offsetof(struct vmmeter, v_swappgsin)),
379                       cpucnt(offsetof(struct vmmeter, v_swappgsout)),
380                       cpucnt(offsetof(struct vmmeter, v_intr)),
381                       cpucnt(offsetof(struct vmmeter, v_swtch)),
382                       boottime.tv_sec);
383
384         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
385 }
386
387 int
388 linprocfs_douptime(struct proc *curp, struct proc *p, struct pfsnode *pfs,
389                    struct uio *uio)
390 {
391         char *ps;
392         char psbuf[64];
393         struct timeval tv;
394
395         getmicrouptime(&tv);
396         ps = psbuf;
397         ps += ksprintf(ps, "%ld.%02ld %"PRIu64".%02"PRIu64"\n",
398                       tv.tv_sec, tv.tv_usec / 10000,
399                       T2S(cpu_time.cp_idle), T2J(cpu_time.cp_idle) % 100);
400         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
401 }
402
403 int
404 linprocfs_doversion(struct proc *curp, struct proc *p, struct pfsnode *pfs,
405                     struct uio *uio)
406 {
407         char *ps;
408         size_t xlen;
409
410         ps = version; /* XXX not entirely correct */
411         for (xlen = 0; ps[xlen] != '\n'; ++xlen)
412                 /* nothing */ ;
413         ++xlen;
414         return (uiomove_frombuf(ps, xlen, uio));
415 }
416
417 #define B2P(x) ((x) >> PAGE_SHIFT)                      /* bytes to pages */
418 int
419 linprocfs_dostatm(struct proc *curp, struct proc *p, struct pfsnode *pfs,
420                     struct uio *uio)
421 {
422         char *ps, psbuf[1024];
423         struct kinfo_proc kp;
424
425         lwkt_gettoken(&p->p_token);
426         fill_kinfo_proc(p, &kp);
427
428         ps = psbuf;
429         ps += ksprintf(ps, "%d", p->p_pid);
430 #define PS_ADD(name, fmt, arg) ps += ksprintf(ps, " " fmt, arg)
431         PS_ADD("",      "%ju",  B2P((uintmax_t)(kp.kp_vm_tsize + kp.kp_vm_dsize + kp.kp_vm_ssize)));
432         PS_ADD("",      "%ju",  (uintmax_t)kp.kp_vm_rssize);
433         PS_ADD("",      "%ju",  (uintmax_t)0); /* XXX */
434         PS_ADD("",      "%ju",  (uintmax_t)kp.kp_vm_tsize);
435         PS_ADD("",      "%ju",  (uintmax_t)kp.kp_vm_dsize);
436         PS_ADD("",      "%ju",  (uintmax_t)kp.kp_vm_ssize);
437         PS_ADD("",      "%ju",  (uintmax_t)0); /* XXX */
438 #undef  PS_ADD
439         ps += ksprintf(ps, "\n");
440         lwkt_reltoken(&p->p_token);
441
442         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
443 }
444
445 #define P2K(x) ((x) << (PAGE_SHIFT - 10))               /* pages to kbytes */
446 int
447 linprocfs_doprocstat(struct proc *curp, struct proc *p, struct pfsnode *pfs,
448                      struct uio *uio)
449 {
450         vm_map_t map = &p->p_vmspace->vm_map;
451         vm_map_entry_t entry;
452         vm_offset_t start, end;
453         char *ps, psbuf[1024];
454         struct kinfo_proc kp;
455
456         lwkt_gettoken(&p->p_token);
457         fill_kinfo_proc(p, &kp);
458
459         start = 0;
460         end = 0;
461         vm_map_lock_read(map);
462         for (entry = map->header.next; entry != &map->header;
463                 entry = entry->next) {
464                 if (entry->maptype != VM_MAPTYPE_NORMAL &&
465                     entry->maptype != VM_MAPTYPE_VPAGETABLE) {
466                         continue;
467                 }
468                 /* Assuming that text is the first entry */
469                 start = entry->start;
470                 end = entry->end;
471         }
472         vm_map_unlock_read(map);
473
474         ps = psbuf;
475         ps += ksprintf(ps, "%d", p->p_pid);
476 #define PS_ADD(name, fmt, arg) ps += ksprintf(ps, " " fmt, arg)
477         PS_ADD("comm",          "(%s)", p->p_comm);
478         PS_ADD("statr",         "%c",   '0'); /* XXX */
479         PS_ADD("ppid",          "%d",   p->p_pptr ? p->p_pptr->p_pid : 0);
480         PS_ADD("pgrp",          "%d",   p->p_pgid);
481         PS_ADD("session",       "%d",   p->p_session->s_sid);
482         PS_ADD("tty",           "%d",   0); /* XXX */
483         PS_ADD("tpgid",         "%d",   kp.kp_tpgid); /* XXX */
484         PS_ADD("flags",         "%u",   0); /* XXX */
485         PS_ADD("minflt",        "%lu",  kp.kp_ru.ru_minflt); /* XXX */
486         PS_ADD("cminflt",       "%lu",  kp.kp_cru.ru_minflt); /* XXX */
487         PS_ADD("majflt",        "%lu",  kp.kp_ru.ru_majflt); /* XXX */
488         PS_ADD("cmajflt",       "%lu",  kp.kp_cru.ru_majflt); /* XXX */
489         PS_ADD("utime",         "%d",   T2J(tvtohz_high(&kp.kp_ru.ru_utime))); /* XXX */
490         PS_ADD("stime",         "%d",   T2J(tvtohz_high(&kp.kp_ru.ru_stime))); /* XXX */
491         PS_ADD("cutime",        "%d",   T2J(tvtohz_high(&kp.kp_cru.ru_utime))); /* XXX */
492         PS_ADD("cstime",        "%d",   T2J(tvtohz_high(&kp.kp_cru.ru_stime))); /* XXX */
493         PS_ADD("priority",      "%d",   0); /* XXX */
494         PS_ADD("nice",          "%d",   kp.kp_nice);
495         PS_ADD("timeout",       "%u",   0); /* XXX */
496         PS_ADD("itrealvalue",   "%u",   0); /* XXX */
497         PS_ADD("starttime",     "%d",   T2J(tvtohz_high(&kp.kp_start))); /* XXX */
498         PS_ADD("vsize",         "%ju",  P2K((uintmax_t)(kp.kp_vm_tsize + kp.kp_vm_dsize + kp.kp_vm_ssize))); /* XXX: not sure */
499         PS_ADD("rss",           "%ju",  (uintmax_t)kp.kp_vm_rssize); /* XXX */
500         PS_ADD("rlim",          "%lu",  kp.kp_ru.ru_maxrss); /* XXX */
501         PS_ADD("startcode",     "%lu",  start); /* XXX */
502         PS_ADD("endcode",       "%lu",  end); /* XXX */
503         PS_ADD("startstack",    "%lu",  (u_long)p->p_vmspace->vm_minsaddr); /* XXX */
504         PS_ADD("kstkesp",       "%u",   0); /* XXX */
505         PS_ADD("kstkeip",       "%u",   0); /* XXX */
506         PS_ADD("signal",        "%d",   0); /* XXX */
507         PS_ADD("blocked",       "%d",   0); /* XXX */
508         PS_ADD("sigignore",     "%d",   0); /* XXX */
509         PS_ADD("sigcatch",      "%d",   0); /* XXX */
510         PS_ADD("wchan",         "%u",   0); /* XXX */
511         PS_ADD("nswap",         "%lu",  kp.kp_ru.ru_nswap); /* XXX */
512         PS_ADD("cnswap",        "%lu",  kp.kp_cru.ru_nswap); /* XXX */
513         PS_ADD("exitsignal",    "%d",   0); /* XXX */
514         PS_ADD("processor",     "%u",   kp.kp_lwp.kl_cpuid); /* XXX */
515         PS_ADD("rt_priority",   "%u",   0); /* XXX */ /* >= 2.5.19 */
516         PS_ADD("policy",        "%u",   kp.kp_nice); /* XXX */ /* >= 2.5.19 */
517 #undef PS_ADD
518         ps += ksprintf(ps, "\n");
519         lwkt_reltoken(&p->p_token);
520         
521         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
522 }
523
524 /*
525  * Map process state to descriptive letter. Note that this does not
526  * quite correspond to what Linux outputs, but it's close enough.
527  */
528 static char *state_str[] = {
529         "? (unknown)",
530         "I (idle)",
531         "R (running)",
532         "T (stopped)",
533         "Z (zombie)",
534         "S (sleeping)",
535         "W (waiting)",
536         "M (mutex)"
537 };
538
539 int
540 linprocfs_doprocstatus(struct proc *curp, struct proc *p, struct pfsnode *pfs,
541                        struct uio *uio)
542 {
543         char *ps, psbuf[1024];
544         char *state;
545         int i;
546
547         ps = psbuf;
548
549         lwkt_gettoken(&p->p_token);
550         if (p->p_stat > NELEM(state_str))
551                 state = state_str[0];
552         else
553                 state = state_str[(int)p->p_stat];
554
555 #define PS_ADD ps += ksprintf
556         PS_ADD(ps, "Name:\t%s\n",         p->p_comm); /* XXX escape */
557         PS_ADD(ps, "State:\t%s\n",        state);
558
559         /*
560          * Credentials
561          */
562         PS_ADD(ps, "Pid:\t%d\n",          p->p_pid);
563         PS_ADD(ps, "PPid:\t%d\n",         p->p_pptr ? p->p_pptr->p_pid : 0);
564         PS_ADD(ps, "Uid:\t%d %d %d %d\n", p->p_ucred->cr_ruid,
565                                           p->p_ucred->cr_uid,
566                                           p->p_ucred->cr_svuid,
567                                           /* FreeBSD doesn't have fsuid */
568                                           p->p_ucred->cr_uid);
569         PS_ADD(ps, "Gid:\t%d %d %d %d\n", p->p_ucred->cr_rgid,
570                                           p->p_ucred->cr_gid,
571                                           p->p_ucred->cr_svgid,
572                                           /* FreeBSD doesn't have fsgid */
573                                           p->p_ucred->cr_gid);
574         PS_ADD(ps, "Groups:\t");
575         for (i = 0; i < p->p_ucred->cr_ngroups; i++)
576                 PS_ADD(ps, "%d ", p->p_ucred->cr_groups[i]);
577         PS_ADD(ps, "\n");
578         
579         /*
580          * Memory
581          */
582         PS_ADD(ps, "VmSize:\t%8lu kB\n",  B2K(p->p_vmspace->vm_map.size));
583         PS_ADD(ps, "VmLck:\t%8u kB\n",    P2K(0)); /* XXX */
584         /* XXX vm_rssize seems to always be zero, how can this be? */
585         PS_ADD(ps, "VmRss:\t%8u kB\n",    P2K(p->p_vmspace->vm_rssize));
586         PS_ADD(ps, "VmData:\t%8u kB\n",   P2K(p->p_vmspace->vm_dsize));
587         PS_ADD(ps, "VmStk:\t%8u kB\n",    P2K(p->p_vmspace->vm_ssize));
588         PS_ADD(ps, "VmExe:\t%8u kB\n",    P2K(p->p_vmspace->vm_tsize));
589         PS_ADD(ps, "VmLib:\t%8u kB\n",    P2K(0)); /* XXX */
590
591         /*
592          * Signal masks
593          *
594          * We support up to 128 signals, while Linux supports 32,
595          * but we only define 32 (the same 32 as Linux, to boot), so
596          * just show the lower 32 bits of each mask. XXX hack.
597          *
598          * NB: on certain platforms (Sparc at least) Linux actually
599          * supports 64 signals, but this code is a long way from
600          * running on anything but i386, so ignore that for now.
601          */
602         PS_ADD(ps, "SigPnd:\t%08x\n",     p->p_siglist.__bits[0]);
603         PS_ADD(ps, "SigBlk:\t%08x\n",     0); /* XXX */
604         PS_ADD(ps, "SigIgn:\t%08x\n",     p->p_sigignore.__bits[0]);
605         PS_ADD(ps, "SigCgt:\t%08x\n",     p->p_sigcatch.__bits[0]);
606         
607         /*
608          * Linux also prints the capability masks, but we don't have
609          * capabilities yet, and when we do get them they're likely to
610          * be meaningless to Linux programs, so we lie. XXX
611          */
612         PS_ADD(ps, "CapInh:\t%016x\n",    0);
613         PS_ADD(ps, "CapPrm:\t%016x\n",    0);
614         PS_ADD(ps, "CapEff:\t%016x\n",    0);
615 #undef PS_ADD
616         lwkt_reltoken(&p->p_token);
617         
618         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
619 }
620
621 int
622 linprocfs_doloadavg(struct proc *curp, struct proc *p,
623                     struct pfsnode *pfs, struct uio *uio)
624 {
625         char *ps, psbuf[512];
626
627         ps = psbuf;
628         ps += ksprintf(ps, "%d.%02d %d.%02d %d.%02d %d/%d %d\n",
629             (int)(averunnable.ldavg[0] / averunnable.fscale),
630             (int)(averunnable.ldavg[0] * 100 / averunnable.fscale % 100),
631             (int)(averunnable.ldavg[1] / averunnable.fscale),
632             (int)(averunnable.ldavg[1] * 100 / averunnable.fscale % 100),
633             (int)(averunnable.ldavg[2] / averunnable.fscale),
634             (int)(averunnable.ldavg[2] * 100 / averunnable.fscale % 100),
635             1,                      /* number of running tasks */
636             -1,                     /* number of tasks */
637             1         /* The last pid, just kidding */
638         );
639         return(uiomove_frombuf(psbuf, ps - psbuf, uio));
640 }
641
642 int
643 linprocfs_donetdev(struct proc *curp, struct proc *p, struct pfsnode *pfs,
644                    struct uio *uio)
645 {
646         struct sbuf *sb;
647         char ifname[16]; /* XXX LINUX_IFNAMSIZ */
648         struct ifnet *ifp;
649         int error;
650
651         sb = sbuf_new_auto();
652
653         sbuf_printf(sb, "%6s|%58s|%s\n%6s|%58s|%58s\n",
654             "Inter-", "   Receive", "  Transmit", " face",
655             "bytes    packets errs drop fifo frame compressed",
656             "bytes    packets errs drop fifo frame compressed");
657
658         crit_enter();
659         TAILQ_FOREACH(ifp, &ifnet, if_link) {
660                 linux_ifname(ifp, ifname, sizeof ifname);
661                 sbuf_printf(sb, "%6.6s:", ifname);
662                 sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu ",
663                     0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
664                 sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
665                     0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
666         }
667         crit_exit();
668         sbuf_finish(sb);
669         error = uiomove_frombuf(sbuf_data(sb), sbuf_len(sb), uio);
670         sbuf_delete(sb);
671         return (error);
672 }
673
674 static void
675 linprocfs_dodevices_callback(char *name, cdev_t dev, bool is_alias, void *arg)
676 {
677         struct sbuf *sb = arg;
678
679         sbuf_printf(sb, "%3d %s\n", dev->si_umajor, name);
680 }
681
682 int
683 linprocfs_dodevices(struct proc *curp, struct proc *p, struct pfsnode *pfs,
684     struct uio *uio)
685 {
686         struct sbuf *sb;
687         int error;
688
689         sb = sbuf_new_auto();
690         sbuf_printf(sb, "Character devices:\n");
691         devfs_scan_callback(linprocfs_dodevices_callback, sb);
692         sbuf_printf(sb, "\nBlock devices:\n");
693         sbuf_finish(sb);
694         error = uiomove_frombuf(sbuf_data(sb), sbuf_len(sb), uio);
695         sbuf_delete(sb);
696         return (error);
697 }
698
699 int
700 linprocfs_doosrelease(struct proc *curp, struct proc *p, struct pfsnode *pfs,
701                    struct uio *uio)
702 {
703         char *osrelease;
704
705         osrelease = linux_get_osrelease(curthread);
706         return(uiomove_frombuf(osrelease, strlen(osrelease)+1, uio));
707 }
708
709 int
710 linprocfs_doostype(struct proc *curp, struct proc *p, struct pfsnode *pfs,
711                    struct uio *uio)
712 {
713         char *osname;
714
715         osname = linux_get_osname(curthread);
716         return(uiomove_frombuf(osname, strlen(osname)+1, uio));
717 }
718
719 int
720 linprocfs_dopidmax(struct proc *curp, struct proc *p, struct pfsnode *pfs,
721                    struct uio *uio)
722 {
723         char buf[32];
724
725         ksnprintf(buf, sizeof(buf), "%d", PID_MAX);
726         return(uiomove_frombuf(buf, strlen(buf)+1, uio));
727 }
728
729 int
730 linprocfs_domaps(struct proc *curp, struct proc *p, struct pfsnode *pfs,
731              struct uio *uio)
732 {
733         int error;
734         vm_map_t map = &p->p_vmspace->vm_map;
735         vm_map_entry_t entry;
736         vm_ooffset_t off = 0;
737         struct sbuf *sb;
738         char *name = "", *freename = NULL;
739         struct vnode *vp;
740         struct vattr vat;
741         int major, minor;
742         ino_t ino;
743
744         if (uio->uio_rw != UIO_READ)
745                 return (EOPNOTSUPP);
746
747         sb = sbuf_new_auto();
748
749         error = 0;
750         vm_map_lock_read(map);
751         for (entry = map->header.next;
752                 ((uio->uio_resid > 0) && (entry != &map->header));
753                 entry = entry->next) {
754                 vm_object_t obj, tobj, lobj;
755                 vm_offset_t ostart;
756                 name = "";
757                 freename = NULL;
758                 ino = 0;
759                 if (entry->maptype != VM_MAPTYPE_NORMAL &&
760                     entry->maptype != VM_MAPTYPE_VPAGETABLE) {
761                         continue;
762                 }
763                 /*
764                  * Use map->hint as a poor man's ripout detector.
765                  */
766                 map->hint = entry;
767                 ostart = entry->start;
768
769                 /*
770                  * Find the bottom-most object, leaving the base object
771                  * and the bottom-most object held (but only one hold
772                  * if they happen to be the same).
773                  */
774                 obj = entry->object.vm_object;
775                 if (obj)
776                         vm_object_hold(obj);
777
778                 lobj = obj;
779                 while (lobj && (tobj = lobj->backing_object) != NULL) {
780                         KKASSERT(tobj != obj);
781                         vm_object_hold(tobj);
782                         if (tobj == lobj->backing_object) {
783                                 if (lobj != obj) {
784                                         vm_object_lock_swap();
785                                         vm_object_drop(lobj);
786                                 }
787                                 lobj = tobj;
788                         } else {
789                                 vm_object_drop(tobj);
790                         }
791                 }
792
793                 if (lobj) {
794                         off = IDX_TO_OFF(lobj->size);
795                         if (lobj->type == OBJT_VNODE) {
796                                 vp = lobj->handle;
797                                 if (vp)
798                                         vref(vp);
799                         } else {
800                                 vp = NULL;
801                         }
802                         
803                         if (vp) {
804                                 vn_fullpath(curproc, vp, &name, &freename, 1);
805                                 vn_lock(vp, LK_SHARED | LK_RETRY);
806                                 VOP_GETATTR(vp, &vat);
807                                 ino = vat.va_fileid;
808                                 major = vat.va_rmajor;
809                                 minor = vat.va_rminor;
810                                 vput(vp);
811                         }
812                 }
813                 if (freename == NULL) {
814                         if (entry->eflags & MAP_ENTRY_STACK)
815                                 name = "[stack]";
816                 }
817
818                 if (lobj != obj)
819                         vm_object_drop(lobj);
820                 if (obj)
821                         vm_object_drop(obj);
822
823                 /*
824                  * We cannot safely hold the map locked while accessing
825                  * userspace as a VM fault might recurse the locked map.
826                  */
827                 vm_map_unlock_read(map);
828
829                 /*
830                  * format:
831                  *  start-end access offset major:minor inode [.text file]
832                  */
833                 error = sbuf_printf(sb,
834                     "%08lx-%08lx %s%s%s%s %08llx %02x:%02x %llu%s%s\n",
835                     (u_long)entry->start, (u_long)entry->end,
836                     (entry->protection & VM_PROT_READ)?"r":"-",
837                     (entry->protection & VM_PROT_WRITE)?"w":"-",
838                     (entry->protection & VM_PROT_EXECUTE)?"x":"-",
839                     "p",
840                     off,        /* offset */
841                     0,          /* major */
842                     0,          /* minor */
843                     ino,        /* inode */
844                     (name && *name) ? "     " : "",
845                     name ? name : "");
846                 if (error == -1)
847                         error = ENOMEM;
848                 if (freename)
849                         kfree(freename, M_TEMP);
850
851                 vm_map_lock_read(map);
852                 if (error)
853                         break;
854
855                 /*
856                  * We use map->hint as a poor man's ripout detector.  If
857                  * it does not match the entry we set it to prior to
858                  * unlocking the map the entry MIGHT now be stale.  In
859                  * this case we do an expensive lookup to find our place
860                  * in the iteration again.
861                  */
862                 if (map->hint != entry) {
863                         vm_map_entry_t reentry;
864                 
865                         vm_map_lookup_entry(map, ostart, &reentry);
866                         entry = reentry;
867                 }
868         }
869         vm_map_unlock_read(map);
870
871         sbuf_finish(sb);
872         if (error == 0)
873                 error = uiomove_frombuf(sbuf_data(sb) + uio->uio_offset,
874                     sbuf_len(sb) - uio->uio_offset, uio);
875         sbuf_delete(sb);
876         return error;
877 }