xref: /freebsd/sys/compat/linprocfs/linprocfs.c (revision eacee0ff7ec955b32e09515246bd97b6edcd2b0f)
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$
42  */
43 
44 #include <sys/param.h>
45 #include <sys/queue.h>
46 #include <sys/blist.h>
47 #include <sys/conf.h>
48 #include <sys/dkstat.h>
49 #include <sys/exec.h>
50 #include <sys/jail.h>
51 #include <sys/kernel.h>
52 #include <sys/linker.h>
53 #include <sys/lock.h>
54 #include <sys/malloc.h>
55 #include <sys/mount.h>
56 #include <sys/mutex.h>
57 #include <sys/namei.h>
58 #include <sys/proc.h>
59 #include <sys/resourcevar.h>
60 #include <sys/sbuf.h>
61 #include <sys/socket.h>
62 #include <sys/sysctl.h>
63 #include <sys/systm.h>
64 #include <sys/tty.h>
65 #include <sys/user.h>
66 #include <sys/vmmeter.h>
67 #include <sys/vnode.h>
68 
69 #include <net/if.h>
70 
71 #include <vm/vm.h>
72 #include <vm/pmap.h>
73 #include <vm/vm_map.h>
74 #include <vm/vm_param.h>
75 #include <vm/vm_object.h>
76 #include <vm/vm_zone.h>
77 #include <vm/swap_pager.h>
78 
79 #include <machine/clock.h>
80 
81 #ifdef __alpha__
82 #include <machine/alpha_cpu.h>
83 #include <machine/cpuconf.h>
84 #include <machine/rpb.h>
85 extern int ncpus;
86 #endif /* __alpha__ */
87 
88 #ifdef __i386__
89 #include <machine/cputypes.h>
90 #include <machine/md_var.h>
91 #endif /* __i386__ */
92 
93 #include <machine/../linux/linux.h>
94 #include <compat/linux/linux_ioctl.h>
95 #include <compat/linux/linux_mib.h>
96 #include <compat/linux/linux_util.h>
97 #include <fs/pseudofs/pseudofs.h>
98 #include <fs/procfs/procfs.h>
99 
100 /*
101  * Various conversion macros
102  */
103 #define T2J(x) (((x) * 100UL) / (stathz ? stathz : hz))	/* ticks to jiffies */
104 #define T2S(x) ((x) / (stathz ? stathz : hz))		/* ticks to seconds */
105 #define B2K(x) ((x) >> 10)				/* bytes to kbytes */
106 #define B2P(x) ((x) >> PAGE_SHIFT)			/* bytes to pages */
107 #define P2B(x) ((x) << PAGE_SHIFT)			/* pages to bytes */
108 #define P2K(x) ((x) << (PAGE_SHIFT - 10))		/* pages to kbytes */
109 
110 /*
111  * Filler function for proc/meminfo
112  */
113 static int
114 linprocfs_domeminfo(PFS_FILL_ARGS)
115 {
116 	unsigned long memtotal;		/* total memory in bytes */
117 	unsigned long memused;		/* used memory in bytes */
118 	unsigned long memfree;		/* free memory in bytes */
119 	unsigned long memshared;	/* shared memory ??? */
120 	unsigned long buffers, cached;	/* buffer / cache memory ??? */
121 	u_quad_t swaptotal;		/* total swap space in bytes */
122 	u_quad_t swapused;		/* used swap space in bytes */
123 	u_quad_t swapfree;		/* free swap space in bytes */
124 	vm_object_t object;
125 
126 	memtotal = physmem * PAGE_SIZE;
127 	/*
128 	 * The correct thing here would be:
129 	 *
130 	memfree = cnt.v_free_count * PAGE_SIZE;
131 	memused = memtotal - memfree;
132 	 *
133 	 * but it might mislead linux binaries into thinking there
134 	 * is very little memory left, so we cheat and tell them that
135 	 * all memory that isn't wired down is free.
136 	 */
137 	memused = cnt.v_wire_count * PAGE_SIZE;
138 	memfree = memtotal - memused;
139 	if (swapblist == NULL) {
140 		swaptotal = 0;
141 		swapfree = 0;
142 	} else {
143 		swaptotal = (u_quad_t)swapblist->bl_blocks * 1024; /* XXX why 1024? */
144 		swapfree = (u_quad_t)swapblist->bl_root->u.bmu_avail * PAGE_SIZE;
145 	}
146 	swapused = swaptotal - swapfree;
147 	memshared = 0;
148 	TAILQ_FOREACH(object, &vm_object_list, object_list)
149 		if (object->shadow_count > 1)
150 			memshared += object->resident_page_count;
151 	memshared *= PAGE_SIZE;
152 	/*
153 	 * We'd love to be able to write:
154 	 *
155 	buffers = bufspace;
156 	 *
157 	 * but bufspace is internal to vfs_bio.c and we don't feel
158 	 * like unstaticizing it just for linprocfs's sake.
159 	 */
160 	buffers = 0;
161 	cached = cnt.v_cache_count * PAGE_SIZE;
162 
163 	sbuf_printf(sb,
164 	    "	     total:    used:	free:  shared: buffers:	 cached:\n"
165 	    "Mem:  %lu %lu %lu %lu %lu %lu\n"
166 	    "Swap: %llu %llu %llu\n"
167 	    "MemTotal: %9lu kB\n"
168 	    "MemFree:  %9lu kB\n"
169 	    "MemShared:%9lu kB\n"
170 	    "Buffers:  %9lu kB\n"
171 	    "Cached:   %9lu kB\n"
172 	    "SwapTotal:%9llu kB\n"
173 	    "SwapFree: %9llu kB\n",
174 	    memtotal, memused, memfree, memshared, buffers, cached,
175 	    swaptotal, swapused, swapfree,
176 	    B2K(memtotal), B2K(memfree),
177 	    B2K(memshared), B2K(buffers), B2K(cached),
178 	    B2K(swaptotal), B2K(swapfree));
179 
180 	return (0);
181 }
182 
183 #ifdef __alpha__
184 /*
185  * Filler function for proc/cpuinfo (Alpha version)
186  */
187 static int
188 linprocfs_docpuinfo(PFS_FILL_ARGS)
189 {
190 	u_int64_t type, major;
191 	struct pcs *pcsp;
192 	const char *model, *sysname;
193 
194 	static const char *cpuname[] = {
195 		"EV3", "EV4", "Simulate", "LCA4", "EV5", "EV45", "EV56",
196 		"EV6", "PCA56", "PCA57", "EV67", "EV68CB", "EV68AL"
197 	};
198 
199 	pcsp = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id);
200 	type = pcsp->pcs_proc_type;
201 	major = (type & PCS_PROC_MAJOR) >> PCS_PROC_MAJORSHIFT;
202 	if (major < sizeof(cpuname)/sizeof(char *)) {
203 		model = cpuname[major - 1];
204 	} else {
205 		model = "unknown";
206 	}
207 
208 	sysname = alpha_dsr_sysname();
209 
210 	sbuf_printf(sb,
211 	    "cpu\t\t\t: Alpha\n"
212 	    "cpu model\t\t: %s\n"
213 	    "cpu variation\t\t: %ld\n"
214 	    "cpu revision\t\t: %ld\n"
215 	    "cpu serial number\t: %s\n"
216 	    "system type\t\t: %s\n"
217 	    "system variation\t: %s\n"
218 	    "system revision\t\t: %ld\n"
219 	    "system serial number\t: %s\n"
220 	    "cycle frequency [Hz]\t: %lu\n"
221 	    "timer frequency [Hz]\t: %lu\n"
222 	    "page size [bytes]\t: %ld\n"
223 	    "phys. address bits\t: %ld\n"
224 	    "max. addr. space #\t: %ld\n"
225 	    "BogoMIPS\t\t: %lu.%02lu\n"
226 	    "kernel unaligned acc\t: %ld (pc=%lx,va=%lx)\n"
227 	    "user unaligned acc\t: %ld (pc=%lx,va=%lx)\n"
228 	    "platform string\t\t: %s\n"
229 	    "cpus detected\t\t: %d\n"
230 	    ,
231 	    model,
232 	    pcsp->pcs_proc_var,
233 	    *(int *)hwrpb->rpb_revision,
234 	    " ",
235 	    " ",
236 	    "0",
237 	    0,
238 	    " ",
239 	    hwrpb->rpb_cc_freq,
240 	    hz,
241 	    hwrpb->rpb_page_size,
242 	    hwrpb->rpb_phys_addr_size,
243 	    hwrpb->rpb_max_asn,
244 	    0, 0,
245 	    0, 0, 0,
246 	    0, 0, 0,
247 	    sysname,
248 	    ncpus);
249 	return (0);
250 }
251 #endif /* __alpha__ */
252 
253 #ifdef __i386__
254 /*
255  * Filler function for proc/cpuinfo (i386 version)
256  */
257 static int
258 linprocfs_docpuinfo(PFS_FILL_ARGS)
259 {
260 	int class, i, fqmhz, fqkhz;
261 
262 	/*
263 	 * We default the flags to include all non-conflicting flags,
264 	 * and the Intel versions of conflicting flags.
265 	 */
266 	static char *flags[] = {
267 		"fpu",	    "vme",     "de",	   "pse",      "tsc",
268 		"msr",	    "pae",     "mce",	   "cx8",      "apic",
269 		"sep",	    "sep",     "mtrr",	   "pge",      "mca",
270 		"cmov",	    "pat",     "pse36",	   "pn",       "b19",
271 		"b20",	    "b21",     "mmxext",   "mmx",      "fxsr",
272 		"xmm",	    "b26",     "b27",	   "b28",      "b29",
273 		"3dnowext", "3dnow"
274 	};
275 
276 	switch (cpu_class) {
277 	case CPUCLASS_286:
278 		class = 2;
279 		break;
280 	case CPUCLASS_386:
281 		class = 3;
282 		break;
283 	case CPUCLASS_486:
284 		class = 4;
285 		break;
286 	case CPUCLASS_586:
287 		class = 5;
288 		break;
289 	case CPUCLASS_686:
290 		class = 6;
291 		break;
292 	default:
293 		class = 0;
294 		break;
295 	}
296 
297 	sbuf_printf(sb,
298 	    "processor\t: %d\n"
299 	    "vendor_id\t: %.20s\n"
300 	    "cpu family\t: %d\n"
301 	    "model\t\t: %d\n"
302 	    "stepping\t: %d\n",
303 	    0, cpu_vendor, class, cpu, cpu_id & 0xf);
304 
305 	sbuf_cat(sb,
306 	    "flags\t\t:");
307 
308 	if (!strcmp(cpu_vendor, "AuthenticAMD") && (class < 6)) {
309 		flags[16] = "fcmov";
310 	} else if (!strcmp(cpu_vendor, "CyrixInstead")) {
311 		flags[24] = "cxmmx";
312 	}
313 
314 	for (i = 0; i < 32; i++)
315 		if (cpu_feature & (1 << i))
316 			sbuf_printf(sb, " %s", flags[i]);
317 	sbuf_cat(sb, "\n");
318 	if (class >= 5) {
319 		fqmhz = (tsc_freq + 4999) / 1000000;
320 		fqkhz = ((tsc_freq + 4999) / 10000) % 100;
321 		sbuf_printf(sb,
322 		    "cpu MHz\t\t: %d.%02d\n"
323 		    "bogomips\t: %d.%02d\n",
324 		    fqmhz, fqkhz, fqmhz, fqkhz);
325 	}
326 
327 	return (0);
328 }
329 #endif /* __i386__ */
330 
331 /*
332  * Filler function for proc/mtab
333  *
334  * This file doesn't exist in Linux' procfs, but is included here so
335  * users can symlink /compat/linux/etc/mtab to /proc/mtab
336  */
337 static int
338 linprocfs_domtab(PFS_FILL_ARGS)
339 {
340 	struct nameidata nd;
341 	struct mount *mp;
342 	char *lep, *flep, *mntto, *mntfrom, *fstype;
343 	size_t lep_len;
344 	int error;
345 
346 	/* resolve symlinks etc. in the emulation tree prefix */
347 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, linux_emul_path, td);
348 	flep = NULL;
349 	if (namei(&nd) != 0 || vn_fullpath(td, nd.ni_vp, &lep, &flep) == -1)
350 		lep = linux_emul_path;
351 	lep_len = strlen(lep);
352 
353 	mtx_lock(&mountlist_mtx);
354 	error = 0;
355 	TAILQ_FOREACH(mp, &mountlist, mnt_list) {
356 		error = VFS_STATFS(mp, &mp->mnt_stat, td);
357 		if (error)
358 			break;
359 
360 		/* determine device name */
361 		mntfrom = mp->mnt_stat.f_mntfromname;
362 
363 		/* determine mount point */
364 		mntto = mp->mnt_stat.f_mntonname;
365 		if (strncmp(mntto, lep, lep_len) == 0 &&
366 		    mntto[lep_len] == '/')
367 			mntto += lep_len;
368 
369 		/* determine fs type */
370 		fstype = mp->mnt_stat.f_fstypename;
371 		if (strcmp(fstype, pn->pn_info->pi_name) == 0)
372 			mntfrom = fstype = "proc";
373 		else if (strcmp(fstype, "procfs") == 0)
374 			continue;
375 
376 		sbuf_printf(sb, "%s %s %s %s", mntfrom, mntto, fstype,
377 		    mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw");
378 #define ADD_OPTION(opt, name) \
379 	if (mp->mnt_stat.f_flags & (opt)) sbuf_printf(sb, "," name);
380 		ADD_OPTION(MNT_SYNCHRONOUS,	"sync");
381 		ADD_OPTION(MNT_NOEXEC,		"noexec");
382 		ADD_OPTION(MNT_NOSUID,		"nosuid");
383 		ADD_OPTION(MNT_NODEV,		"nodev");
384 		ADD_OPTION(MNT_UNION,		"union");
385 		ADD_OPTION(MNT_ASYNC,		"async");
386 		ADD_OPTION(MNT_SUIDDIR,		"suiddir");
387 		ADD_OPTION(MNT_NOSYMFOLLOW,	"nosymfollow");
388 		ADD_OPTION(MNT_NOATIME,		"noatime");
389 #undef ADD_OPTION
390 		/* a real Linux mtab will also show NFS options */
391 		sbuf_printf(sb, " 0 0\n");
392 	}
393 	mtx_unlock(&mountlist_mtx);
394 	if (flep != NULL)
395 		free(flep, M_TEMP);
396 	return (error);
397 }
398 
399 /*
400  * Filler function for proc/stat
401  */
402 static int
403 linprocfs_dostat(PFS_FILL_ARGS)
404 {
405 	sbuf_printf(sb,
406 	    "cpu %ld %ld %ld %ld\n"
407 	    "disk 0 0 0 0\n"
408 	    "page %u %u\n"
409 	    "swap %u %u\n"
410 	    "intr %u\n"
411 	    "ctxt %u\n"
412 	    "btime %lld\n",
413 	    T2J(cp_time[CP_USER]),
414 	    T2J(cp_time[CP_NICE]),
415 	    T2J(cp_time[CP_SYS] /*+ cp_time[CP_INTR]*/),
416 	    T2J(cp_time[CP_IDLE]),
417 	    cnt.v_vnodepgsin,
418 	    cnt.v_vnodepgsout,
419 	    cnt.v_swappgsin,
420 	    cnt.v_swappgsout,
421 	    cnt.v_intr,
422 	    cnt.v_swtch,
423 	    (quad_t)boottime.tv_sec);
424 	return (0);
425 }
426 
427 /*
428  * Filler function for proc/uptime
429  */
430 static int
431 linprocfs_douptime(PFS_FILL_ARGS)
432 {
433 	struct timeval tv;
434 
435 	getmicrouptime(&tv);
436 	sbuf_printf(sb, "%lld.%02ld %ld.%02ld\n",
437 	    (quad_t)tv.tv_sec, tv.tv_usec / 10000,
438 	    T2S(cp_time[CP_IDLE]), T2J(cp_time[CP_IDLE]) % 100);
439 	return (0);
440 }
441 
442 /*
443  * Filler function for proc/version
444  */
445 static int
446 linprocfs_doversion(PFS_FILL_ARGS)
447 {
448 	char osname[LINUX_MAX_UTSNAME];
449 	char osrelease[LINUX_MAX_UTSNAME];
450 
451 	linux_get_osname(td->td_proc, osname);
452 	linux_get_osrelease(td->td_proc, osrelease);
453 
454 	sbuf_printf(sb,
455 	    "%s version %s (des@freebsd.org) (gcc version " __VERSION__ ")"
456 	    " #4 Sun Dec 18 04:30:00 CET 1977\n", osname, osrelease);
457 	return (0);
458 }
459 
460 /*
461  * Filler function for proc/loadavg
462  */
463 static int
464 linprocfs_doloadavg(PFS_FILL_ARGS)
465 {
466 	sbuf_printf(sb,
467 	    "%d.%02d %d.%02d %d.%02d %d/%d %d\n",
468 	    (int)(averunnable.ldavg[0] / averunnable.fscale),
469 	    (int)(averunnable.ldavg[0] * 100 / averunnable.fscale % 100),
470 	    (int)(averunnable.ldavg[1] / averunnable.fscale),
471 	    (int)(averunnable.ldavg[1] * 100 / averunnable.fscale % 100),
472 	    (int)(averunnable.ldavg[2] / averunnable.fscale),
473 	    (int)(averunnable.ldavg[2] * 100 / averunnable.fscale % 100),
474 	    1,				/* number of running tasks */
475 	    nprocs,			/* number of tasks */
476 	    lastpid			/* the last pid */
477 	);
478 
479 	return (0);
480 }
481 
482 /*
483  * Filler function for proc/pid/stat
484  */
485 static int
486 linprocfs_doprocstat(PFS_FILL_ARGS)
487 {
488 	struct kinfo_proc kp;
489 
490 	fill_kinfo_proc(p, &kp);
491 	sbuf_printf(sb, "%d", p->p_pid);
492 #define PS_ADD(name, fmt, arg) sbuf_printf(sb, " " fmt, arg)
493 	PS_ADD("comm",		"(%s)",	p->p_comm);
494 	PS_ADD("statr",		"%c",	'0'); /* XXX */
495 	PROC_LOCK(p);
496 	PS_ADD("ppid",		"%d",	p->p_pptr ? p->p_pptr->p_pid : 0);
497 	PROC_UNLOCK(p);
498 	PS_ADD("pgrp",		"%d",	p->p_pgid);
499 	PS_ADD("session",	"%d",	p->p_session->s_sid);
500 	PS_ADD("tty",		"%d",	0); /* XXX */
501 	PS_ADD("tpgid",		"%d",	0); /* XXX */
502 	PS_ADD("flags",		"%u",	0); /* XXX */
503 	PS_ADD("minflt",	"%u",	0); /* XXX */
504 	PS_ADD("cminflt",	"%u",	0); /* XXX */
505 	PS_ADD("majflt",	"%u",	0); /* XXX */
506 	PS_ADD("cminflt",	"%u",	0); /* XXX */
507 	PS_ADD("utime",		"%d",	0); /* XXX */
508 	PS_ADD("stime",		"%d",	0); /* XXX */
509 	PS_ADD("cutime",	"%d",	0); /* XXX */
510 	PS_ADD("cstime",	"%d",	0); /* XXX */
511 	PS_ADD("counter",	"%d",	0); /* XXX */
512 	PS_ADD("priority",	"%d",	0); /* XXX */
513 	PS_ADD("timeout",	"%u",	0); /* XXX */
514 	PS_ADD("itrealvalue",	"%u",	0); /* XXX */
515 	PS_ADD("starttime",	"%d",	0); /* XXX */
516 	PS_ADD("vsize",		"%u",	kp.ki_size);
517 	PS_ADD("rss",		"%u",	P2K(kp.ki_rssize));
518 	PS_ADD("rlim",		"%u",	0); /* XXX */
519 	PS_ADD("startcode",	"%u",	(unsigned)0);
520 	PS_ADD("endcode",	"%u",	0); /* XXX */
521 	PS_ADD("startstack",	"%u",	0); /* XXX */
522 	PS_ADD("esp",		"%u",	0); /* XXX */
523 	PS_ADD("eip",		"%u",	0); /* XXX */
524 	PS_ADD("signal",	"%d",	0); /* XXX */
525 	PS_ADD("blocked",	"%d",	0); /* XXX */
526 	PS_ADD("sigignore",	"%d",	0); /* XXX */
527 	PS_ADD("sigcatch",	"%d",	0); /* XXX */
528 	PS_ADD("wchan",		"%u",	0); /* XXX */
529 	PS_ADD("nswap",		"%lu",	(long unsigned)0); /* XXX */
530 	PS_ADD("cnswap",	"%lu",	(long unsigned)0); /* XXX */
531 	PS_ADD("exitsignal",	"%d",	0); /* XXX */
532 	PS_ADD("processor",	"%d",	0); /* XXX */
533 #undef PS_ADD
534 	sbuf_putc(sb, '\n');
535 
536 	return (0);
537 }
538 
539 /*
540  * Map process state to descriptive letter. Note that this does not
541  * quite correspond to what Linux outputs, but it's close enough.
542  */
543 static char *state_str[] = {
544 	"? (unknown)",
545 	"I (idle)",
546 	"R (running)",
547 	"S (sleeping)",
548 	"T (stopped)",
549 	"Z (zombie)",
550 	"W (waiting)",
551 	"M (mutex)"
552 };
553 
554 /*
555  * Filler function for proc/pid/status
556  */
557 static int
558 linprocfs_doprocstatus(PFS_FILL_ARGS)
559 {
560 	struct kinfo_proc kp;
561 	char *state;
562 	segsz_t lsize;
563 	int i;
564 
565 	mtx_lock_spin(&sched_lock);
566 	if (p->p_stat > sizeof state_str / sizeof *state_str)
567 		state = state_str[0];
568 	else
569 		state = state_str[(int)p->p_stat];
570 	mtx_unlock_spin(&sched_lock);
571 
572 	fill_kinfo_proc(p, &kp);
573 	sbuf_printf(sb, "Name:\t%s\n",		p->p_comm); /* XXX escape */
574 	sbuf_printf(sb, "State:\t%s\n",		state);
575 
576 	/*
577 	 * Credentials
578 	 */
579 	sbuf_printf(sb, "Pid:\t%d\n",		p->p_pid);
580 	PROC_LOCK(p);
581 	sbuf_printf(sb, "PPid:\t%d\n",		p->p_pptr ?
582 						p->p_pptr->p_pid : 0);
583 	sbuf_printf(sb, "Uid:\t%d %d %d %d\n",	p->p_ucred->cr_ruid,
584 						p->p_ucred->cr_uid,
585 						p->p_ucred->cr_svuid,
586 						/* FreeBSD doesn't have fsuid */
587 						p->p_ucred->cr_uid);
588 	sbuf_printf(sb, "Gid:\t%d %d %d %d\n",	p->p_ucred->cr_rgid,
589 						p->p_ucred->cr_gid,
590 						p->p_ucred->cr_svgid,
591 						/* FreeBSD doesn't have fsgid */
592 						p->p_ucred->cr_gid);
593 	sbuf_cat(sb, "Groups:\t");
594 	for (i = 0; i < p->p_ucred->cr_ngroups; i++)
595 		sbuf_printf(sb, "%d ",		p->p_ucred->cr_groups[i]);
596 	PROC_UNLOCK(p);
597 	sbuf_putc(sb, '\n');
598 
599 	/*
600 	 * Memory
601 	 *
602 	 * While our approximation of VmLib may not be accurate (I
603 	 * don't know of a simple way to verify it, and I'm not sure
604 	 * it has much meaning anyway), I believe it's good enough.
605 	 *
606 	 * The same code that could (I think) accurately compute VmLib
607 	 * could also compute VmLck, but I don't really care enough to
608 	 * implement it. Submissions are welcome.
609 	 */
610 	sbuf_printf(sb, "VmSize:\t%8u kB\n",	B2K(kp.ki_size));
611 	sbuf_printf(sb, "VmLck:\t%8u kB\n",	P2K(0)); /* XXX */
612 	sbuf_printf(sb, "VmRss:\t%8u kB\n",	P2K(kp.ki_rssize));
613 	sbuf_printf(sb, "VmData:\t%8u kB\n",	P2K(kp.ki_dsize));
614 	sbuf_printf(sb, "VmStk:\t%8u kB\n",	P2K(kp.ki_ssize));
615 	sbuf_printf(sb, "VmExe:\t%8u kB\n",	P2K(kp.ki_tsize));
616 	lsize = B2P(kp.ki_size) - kp.ki_dsize -
617 	    kp.ki_ssize - kp.ki_tsize - 1;
618 	sbuf_printf(sb, "VmLib:\t%8u kB\n",	P2K(lsize));
619 
620 	/*
621 	 * Signal masks
622 	 *
623 	 * We support up to 128 signals, while Linux supports 32,
624 	 * but we only define 32 (the same 32 as Linux, to boot), so
625 	 * just show the lower 32 bits of each mask. XXX hack.
626 	 *
627 	 * NB: on certain platforms (Sparc at least) Linux actually
628 	 * supports 64 signals, but this code is a long way from
629 	 * running on anything but i386, so ignore that for now.
630 	 */
631 	PROC_LOCK(p);
632 	sbuf_printf(sb, "SigPnd:\t%08x\n",	p->p_siglist.__bits[0]);
633 	/*
634 	 * I can't seem to find out where the signal mask is in
635 	 * relation to struct proc, so SigBlk is left unimplemented.
636 	 */
637 	sbuf_printf(sb, "SigBlk:\t%08x\n",	0); /* XXX */
638 	sbuf_printf(sb, "SigIgn:\t%08x\n",	p->p_sigignore.__bits[0]);
639 	sbuf_printf(sb, "SigCgt:\t%08x\n",	p->p_sigcatch.__bits[0]);
640 	PROC_UNLOCK(p);
641 
642 	/*
643 	 * Linux also prints the capability masks, but we don't have
644 	 * capabilities yet, and when we do get them they're likely to
645 	 * be meaningless to Linux programs, so we lie. XXX
646 	 */
647 	sbuf_printf(sb, "CapInh:\t%016x\n",	0);
648 	sbuf_printf(sb, "CapPrm:\t%016x\n",	0);
649 	sbuf_printf(sb, "CapEff:\t%016x\n",	0);
650 
651 	return (0);
652 }
653 
654 /*
655  * Filler function for proc/pid/cmdline
656  */
657 static int
658 linprocfs_doproccmdline(PFS_FILL_ARGS)
659 {
660 	struct ps_strings pstr;
661 	int error, i;
662 
663 	/*
664 	 * If we are using the ps/cmdline caching, use that.  Otherwise
665 	 * revert back to the old way which only implements full cmdline
666 	 * for the currept process and just p->p_comm for all other
667 	 * processes.
668 	 * Note that if the argv is no longer available, we deliberately
669 	 * don't fall back on p->p_comm or return an error: the authentic
670 	 * Linux behaviour is to return zero-length in this case.
671 	 */
672 
673 	if (p->p_args && (ps_argsopen || !p_cansee(td->td_proc, p))) {
674 		sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length);
675 	} else if (p != td->td_proc) {
676 		sbuf_printf(sb, "%.*s", MAXCOMLEN, p->p_comm);
677 	} else {
678 		error = copyin((void*)PS_STRINGS, &pstr, sizeof(pstr));
679 		if (error)
680 			return (error);
681 		for (i = 0; i < pstr.ps_nargvstr; i++) {
682 			sbuf_copyin(sb, pstr.ps_argvstr[i], 0);
683 			sbuf_printf(sb, "%c", '\0');
684 		}
685 	}
686 
687 	return (0);
688 }
689 
690 /*
691  * Filler function for proc/net/dev
692  */
693 static int
694 linprocfs_donetdev(PFS_FILL_ARGS)
695 {
696 	char ifname[16]; /* XXX LINUX_IFNAMSIZ */
697 	struct ifnet *ifp;
698 
699 	sbuf_printf(sb, "%6s|%58s|%s\n%6s|%58s|%58s\n",
700 	    "Inter-", "   Receive", "  Transmit", " face",
701 	    "bytes    packets errs drop fifo frame compressed",
702 	    "bytes    packets errs drop fifo frame compressed");
703 
704 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
705 		linux_ifname(ifp, ifname, sizeof ifname);
706 			sbuf_printf(sb, "%6.6s:", ifname);
707 		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu ",
708 		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
709 		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
710 		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
711 	}
712 
713 	return (0);
714 }
715 
716 #if 0
717 extern struct cdevsw *cdevsw[];
718 
719 /*
720  * Filler function for proc/devices
721  */
722 static int
723 linprocfs_dodevices(PFS_FILL_ARGS)
724 {
725 	int i;
726 
727 	sbuf_printf(sb, "Character devices:\n");
728 
729 	for (i = 0; i < NUMCDEVSW; i++)
730 		if (cdevsw[i] != NULL)
731 			sbuf_printf(sb, "%3d %s\n", i, cdevsw[i]->d_name);
732 
733 	sbuf_printf(sb, "\nBlock devices:\n");
734 
735 	return (0);
736 }
737 #endif
738 
739 /*
740  * Filler function for proc/cmdline
741  */
742 static int
743 linprocfs_docmdline(PFS_FILL_ARGS)
744 {
745 	sbuf_printf(sb, "BOOT_IMAGE=%s", kernelname);
746 	sbuf_printf(sb, " ro root=302\n");
747 	return (0);
748 }
749 
750 #if 0
751 /*
752  * Filler function for proc/modules
753  */
754 static int
755 linprocfs_domodules(PFS_FILL_ARGS)
756 {
757 	struct linker_file *lf;
758 
759 	TAILQ_FOREACH(lf, &linker_files, link) {
760 		sbuf_printf(sb, "%-20s%8lu%4d\n", lf->filename,
761 		    (unsigned long)lf->size, lf->refs);
762 	}
763 	return (0);
764 }
765 #endif
766 
767 /*
768  * Constructor
769  */
770 static int
771 linprocfs_init(PFS_INIT_ARGS)
772 {
773 	struct pfs_node *root;
774 	struct pfs_node *dir;
775 
776 	root = pi->pi_root;
777 
778 #define PFS_CREATE_FILE(name) \
779 	pfs_create_file(root, #name, &linprocfs_do##name, NULL, NULL, PFS_RD)
780 	PFS_CREATE_FILE(cmdline);
781 	PFS_CREATE_FILE(cpuinfo);
782 #if 0
783 	PFS_CREATE_FILE(devices);
784 #endif
785 	PFS_CREATE_FILE(loadavg);
786 	PFS_CREATE_FILE(meminfo);
787 #if 0
788 	PFS_CREATE_FILE(modules);
789 #endif
790 	PFS_CREATE_FILE(mtab);
791 	PFS_CREATE_FILE(stat);
792 	PFS_CREATE_FILE(uptime);
793 	PFS_CREATE_FILE(version);
794 #undef PFS_CREATE_FILE
795 	pfs_create_link(root, "self", &procfs_docurproc,
796 	    NULL, NULL, 0);
797 
798 	dir = pfs_create_dir(root, "net", NULL, NULL, 0);
799 	pfs_create_file(dir, "dev", &linprocfs_donetdev,
800 	    NULL, NULL, PFS_RD);
801 
802 	dir = pfs_create_dir(root, "pid", NULL, NULL, PFS_PROCDEP);
803 	pfs_create_file(dir, "cmdline", &linprocfs_doproccmdline,
804 	    NULL, NULL, PFS_RD);
805 	pfs_create_link(dir, "exe", &procfs_doprocfile,
806 	    NULL, &procfs_notsystem, 0);
807 	pfs_create_file(dir, "mem", &procfs_doprocmem,
808 	    &procfs_attr, &procfs_candebug, PFS_RDWR|PFS_RAW);
809 	pfs_create_file(dir, "stat", &linprocfs_doprocstat,
810 	    NULL, NULL, PFS_RD);
811 	pfs_create_file(dir, "status", &linprocfs_doprocstatus,
812 	    NULL, NULL, PFS_RD);
813 
814 	return (0);
815 }
816 
817 /*
818  * Destructor
819  */
820 static int
821 linprocfs_uninit(PFS_INIT_ARGS)
822 {
823 
824 	/* nothing to do, pseudofs will GC */
825 	return (0);
826 }
827 
828 PSEUDOFS(linprocfs, 1);
829 MODULE_DEPEND(linprocfs, linux, 1, 1, 1);
830 MODULE_DEPEND(linprocfs, procfs, 1, 1, 1);
831