xref: /freebsd/sys/compat/linprocfs/linprocfs.c (revision a3e8fd0b7f663db7eafff527d5c3ca3bcfa8a537)
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_INHIBITED:
570 				state = "S (sleeping)";
571 				break;
572 			case TDS_RUNQ:
573 			case TDS_RUNNING:
574 				state = "R (running)";
575 				break;
576 			default:
577 				state = "? (unknown)";
578 				break;
579 			}
580 			break;
581 		case PRS_ZOMBIE:
582 			state = "Z (zombie)";
583 			break;
584 		default:
585 			state = "? (unknown)";
586 			break;
587 		}
588 	}
589 	mtx_unlock_spin(&sched_lock);
590 
591 	PROC_LOCK(p);
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%8u kB\n",	B2K(kp.ki_size));
630 	sbuf_printf(sb, "VmLck:\t%8u kB\n",	P2K(0)); /* XXX */
631 	sbuf_printf(sb, "VmRss:\t%8u kB\n",	P2K(kp.ki_rssize));
632 	sbuf_printf(sb, "VmData:\t%8u kB\n",	P2K(kp.ki_dsize));
633 	sbuf_printf(sb, "VmStk:\t%8u kB\n",	P2K(kp.ki_ssize));
634 	sbuf_printf(sb, "VmExe:\t%8u kB\n",	P2K(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%8u kB\n",	P2K(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 	sbuf_printf(sb, "SigIgn:\t%08x\n",	p->p_sigignore.__bits[0]);
658 	sbuf_printf(sb, "SigCgt:\t%08x\n",	p->p_sigcatch.__bits[0]);
659 	PROC_UNLOCK(p);
660 
661 	/*
662 	 * Linux also prints the capability masks, but we don't have
663 	 * capabilities yet, and when we do get them they're likely to
664 	 * be meaningless to Linux programs, so we lie. XXX
665 	 */
666 	sbuf_printf(sb, "CapInh:\t%016x\n",	0);
667 	sbuf_printf(sb, "CapPrm:\t%016x\n",	0);
668 	sbuf_printf(sb, "CapEff:\t%016x\n",	0);
669 
670 	return (0);
671 }
672 
673 /*
674  * Filler function for proc/pid/cmdline
675  */
676 static int
677 linprocfs_doproccmdline(PFS_FILL_ARGS)
678 {
679 	struct ps_strings pstr;
680 	int error, i;
681 
682 	/*
683 	 * If we are using the ps/cmdline caching, use that.  Otherwise
684 	 * revert back to the old way which only implements full cmdline
685 	 * for the currept process and just p->p_comm for all other
686 	 * processes.
687 	 * Note that if the argv is no longer available, we deliberately
688 	 * don't fall back on p->p_comm or return an error: the authentic
689 	 * Linux behaviour is to return zero-length in this case.
690 	 */
691 
692 	PROC_LOCK(p);
693 	if (p->p_args && (ps_argsopen || !p_cansee(td, p))) {
694 		sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length);
695 		PROC_UNLOCK(p);
696 	} else if (p != td->td_proc) {
697 		PROC_UNLOCK(p);
698 		sbuf_printf(sb, "%.*s", MAXCOMLEN, p->p_comm);
699 	} else {
700 		PROC_UNLOCK(p);
701 		error = copyin((void *)p->p_sysent->sv_psstrings, &pstr,
702 		    sizeof(pstr));
703 		if (error)
704 			return (error);
705 		for (i = 0; i < pstr.ps_nargvstr; i++) {
706 			sbuf_copyin(sb, pstr.ps_argvstr[i], 0);
707 			sbuf_printf(sb, "%c", '\0');
708 		}
709 	}
710 
711 	return (0);
712 }
713 
714 /*
715  * Filler function for proc/net/dev
716  */
717 static int
718 linprocfs_donetdev(PFS_FILL_ARGS)
719 {
720 	char ifname[16]; /* XXX LINUX_IFNAMSIZ */
721 	struct ifnet *ifp;
722 
723 	sbuf_printf(sb, "%6s|%58s|%s\n%6s|%58s|%58s\n",
724 	    "Inter-", "   Receive", "  Transmit", " face",
725 	    "bytes    packets errs drop fifo frame compressed",
726 	    "bytes    packets errs drop fifo frame compressed");
727 
728 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
729 		linux_ifname(ifp, ifname, sizeof ifname);
730 			sbuf_printf(sb, "%6.6s:", ifname);
731 		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu ",
732 		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
733 		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
734 		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
735 	}
736 
737 	return (0);
738 }
739 
740 #if 0
741 extern struct cdevsw *cdevsw[];
742 
743 /*
744  * Filler function for proc/devices
745  */
746 static int
747 linprocfs_dodevices(PFS_FILL_ARGS)
748 {
749 	int i;
750 
751 	sbuf_printf(sb, "Character devices:\n");
752 
753 	for (i = 0; i < NUMCDEVSW; i++)
754 		if (cdevsw[i] != NULL)
755 			sbuf_printf(sb, "%3d %s\n", i, cdevsw[i]->d_name);
756 
757 	sbuf_printf(sb, "\nBlock devices:\n");
758 
759 	return (0);
760 }
761 #endif
762 
763 /*
764  * Filler function for proc/cmdline
765  */
766 static int
767 linprocfs_docmdline(PFS_FILL_ARGS)
768 {
769 	sbuf_printf(sb, "BOOT_IMAGE=%s", kernelname);
770 	sbuf_printf(sb, " ro root=302\n");
771 	return (0);
772 }
773 
774 #if 0
775 /*
776  * Filler function for proc/modules
777  */
778 static int
779 linprocfs_domodules(PFS_FILL_ARGS)
780 {
781 	struct linker_file *lf;
782 
783 	TAILQ_FOREACH(lf, &linker_files, link) {
784 		sbuf_printf(sb, "%-20s%8lu%4d\n", lf->filename,
785 		    (unsigned long)lf->size, lf->refs);
786 	}
787 	return (0);
788 }
789 #endif
790 
791 /*
792  * Constructor
793  */
794 static int
795 linprocfs_init(PFS_INIT_ARGS)
796 {
797 	struct pfs_node *root;
798 	struct pfs_node *dir;
799 
800 	root = pi->pi_root;
801 
802 #define PFS_CREATE_FILE(name) \
803 	pfs_create_file(root, #name, &linprocfs_do##name, NULL, NULL, PFS_RD)
804 	PFS_CREATE_FILE(cmdline);
805 	PFS_CREATE_FILE(cpuinfo);
806 #if 0
807 	PFS_CREATE_FILE(devices);
808 #endif
809 	PFS_CREATE_FILE(loadavg);
810 	PFS_CREATE_FILE(meminfo);
811 #if 0
812 	PFS_CREATE_FILE(modules);
813 #endif
814 	PFS_CREATE_FILE(mtab);
815 	PFS_CREATE_FILE(stat);
816 	PFS_CREATE_FILE(uptime);
817 	PFS_CREATE_FILE(version);
818 #undef PFS_CREATE_FILE
819 	pfs_create_link(root, "self", &procfs_docurproc,
820 	    NULL, NULL, 0);
821 
822 	dir = pfs_create_dir(root, "net", NULL, NULL, 0);
823 	pfs_create_file(dir, "dev", &linprocfs_donetdev,
824 	    NULL, NULL, PFS_RD);
825 
826 	dir = pfs_create_dir(root, "pid", NULL, NULL, PFS_PROCDEP);
827 	pfs_create_file(dir, "cmdline", &linprocfs_doproccmdline,
828 	    NULL, NULL, PFS_RD);
829 	pfs_create_link(dir, "exe", &procfs_doprocfile,
830 	    NULL, &procfs_notsystem, 0);
831 	pfs_create_file(dir, "mem", &procfs_doprocmem,
832 	    &procfs_attr, &procfs_candebug, PFS_RDWR|PFS_RAW);
833 	pfs_create_file(dir, "stat", &linprocfs_doprocstat,
834 	    NULL, NULL, PFS_RD);
835 	pfs_create_file(dir, "status", &linprocfs_doprocstatus,
836 	    NULL, NULL, PFS_RD);
837 
838 	return (0);
839 }
840 
841 /*
842  * Destructor
843  */
844 static int
845 linprocfs_uninit(PFS_INIT_ARGS)
846 {
847 
848 	/* nothing to do, pseudofs will GC */
849 	return (0);
850 }
851 
852 PSEUDOFS(linprocfs, 1);
853 MODULE_DEPEND(linprocfs, linux, 1, 1, 1);
854 MODULE_DEPEND(linprocfs, procfs, 1, 1, 1);
855