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