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