xref: /freebsd/sys/compat/linprocfs/linprocfs.c (revision ee2ea5ceafed78a5bd9810beb9e3ca927180c226)
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  * Map process state to descriptive letter. Note that this does not
543  * quite correspond to what Linux outputs, but it's close enough.
544  */
545 static char *state_str[] = {
546 	"? (unknown)",
547 	"I (idle)",
548 	"R (running)",
549 	"S (sleeping)",
550 	"T (stopped)",
551 	"Z (zombie)",
552 	"W (waiting)",
553 	"M (mutex)"
554 };
555 
556 /*
557  * Filler function for proc/pid/status
558  */
559 static int
560 linprocfs_doprocstatus(PFS_FILL_ARGS)
561 {
562 	struct kinfo_proc kp;
563 	char *state;
564 	segsz_t lsize;
565 	int i;
566 
567 	mtx_lock_spin(&sched_lock);
568 	if (p->p_stat > sizeof state_str / sizeof *state_str)
569 		state = state_str[0];
570 	else
571 		state = state_str[(int)p->p_stat];
572 	mtx_unlock_spin(&sched_lock);
573 
574 	PROC_LOCK(p);
575 	fill_kinfo_proc(p, &kp);
576 	sbuf_printf(sb, "Name:\t%s\n",		p->p_comm); /* XXX escape */
577 	sbuf_printf(sb, "State:\t%s\n",		state);
578 
579 	/*
580 	 * Credentials
581 	 */
582 	sbuf_printf(sb, "Pid:\t%d\n",		p->p_pid);
583 	sbuf_printf(sb, "PPid:\t%d\n",		p->p_pptr ?
584 						p->p_pptr->p_pid : 0);
585 	sbuf_printf(sb, "Uid:\t%d %d %d %d\n",	p->p_ucred->cr_ruid,
586 						p->p_ucred->cr_uid,
587 						p->p_ucred->cr_svuid,
588 						/* FreeBSD doesn't have fsuid */
589 						p->p_ucred->cr_uid);
590 	sbuf_printf(sb, "Gid:\t%d %d %d %d\n",	p->p_ucred->cr_rgid,
591 						p->p_ucred->cr_gid,
592 						p->p_ucred->cr_svgid,
593 						/* FreeBSD doesn't have fsgid */
594 						p->p_ucred->cr_gid);
595 	sbuf_cat(sb, "Groups:\t");
596 	for (i = 0; i < p->p_ucred->cr_ngroups; i++)
597 		sbuf_printf(sb, "%d ",		p->p_ucred->cr_groups[i]);
598 	PROC_UNLOCK(p);
599 	sbuf_putc(sb, '\n');
600 
601 	/*
602 	 * Memory
603 	 *
604 	 * While our approximation of VmLib may not be accurate (I
605 	 * don't know of a simple way to verify it, and I'm not sure
606 	 * it has much meaning anyway), I believe it's good enough.
607 	 *
608 	 * The same code that could (I think) accurately compute VmLib
609 	 * could also compute VmLck, but I don't really care enough to
610 	 * implement it. Submissions are welcome.
611 	 */
612 	sbuf_printf(sb, "VmSize:\t%8u kB\n",	B2K(kp.ki_size));
613 	sbuf_printf(sb, "VmLck:\t%8u kB\n",	P2K(0)); /* XXX */
614 	sbuf_printf(sb, "VmRss:\t%8u kB\n",	P2K(kp.ki_rssize));
615 	sbuf_printf(sb, "VmData:\t%8u kB\n",	P2K(kp.ki_dsize));
616 	sbuf_printf(sb, "VmStk:\t%8u kB\n",	P2K(kp.ki_ssize));
617 	sbuf_printf(sb, "VmExe:\t%8u kB\n",	P2K(kp.ki_tsize));
618 	lsize = B2P(kp.ki_size) - kp.ki_dsize -
619 	    kp.ki_ssize - kp.ki_tsize - 1;
620 	sbuf_printf(sb, "VmLib:\t%8u kB\n",	P2K(lsize));
621 
622 	/*
623 	 * Signal masks
624 	 *
625 	 * We support up to 128 signals, while Linux supports 32,
626 	 * but we only define 32 (the same 32 as Linux, to boot), so
627 	 * just show the lower 32 bits of each mask. XXX hack.
628 	 *
629 	 * NB: on certain platforms (Sparc at least) Linux actually
630 	 * supports 64 signals, but this code is a long way from
631 	 * running on anything but i386, so ignore that for now.
632 	 */
633 	PROC_LOCK(p);
634 	sbuf_printf(sb, "SigPnd:\t%08x\n",	p->p_siglist.__bits[0]);
635 	/*
636 	 * I can't seem to find out where the signal mask is in
637 	 * relation to struct proc, so SigBlk is left unimplemented.
638 	 */
639 	sbuf_printf(sb, "SigBlk:\t%08x\n",	0); /* XXX */
640 	sbuf_printf(sb, "SigIgn:\t%08x\n",	p->p_sigignore.__bits[0]);
641 	sbuf_printf(sb, "SigCgt:\t%08x\n",	p->p_sigcatch.__bits[0]);
642 	PROC_UNLOCK(p);
643 
644 	/*
645 	 * Linux also prints the capability masks, but we don't have
646 	 * capabilities yet, and when we do get them they're likely to
647 	 * be meaningless to Linux programs, so we lie. XXX
648 	 */
649 	sbuf_printf(sb, "CapInh:\t%016x\n",	0);
650 	sbuf_printf(sb, "CapPrm:\t%016x\n",	0);
651 	sbuf_printf(sb, "CapEff:\t%016x\n",	0);
652 
653 	return (0);
654 }
655 
656 /*
657  * Filler function for proc/pid/cmdline
658  */
659 static int
660 linprocfs_doproccmdline(PFS_FILL_ARGS)
661 {
662 	struct ps_strings pstr;
663 	int error, i;
664 
665 	/*
666 	 * If we are using the ps/cmdline caching, use that.  Otherwise
667 	 * revert back to the old way which only implements full cmdline
668 	 * for the currept process and just p->p_comm for all other
669 	 * processes.
670 	 * Note that if the argv is no longer available, we deliberately
671 	 * don't fall back on p->p_comm or return an error: the authentic
672 	 * Linux behaviour is to return zero-length in this case.
673 	 */
674 
675 	PROC_LOCK(p);
676 	if (p->p_args && (ps_argsopen || !p_cansee(td->td_proc, p))) {
677 		sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length);
678 		PROC_UNLOCK(p);
679 	} else if (p != td->td_proc) {
680 		PROC_UNLOCK(p);
681 		sbuf_printf(sb, "%.*s", MAXCOMLEN, p->p_comm);
682 	} else {
683 		PROC_UNLOCK(p);
684 		error = copyin((void*)PS_STRINGS, &pstr, sizeof(pstr));
685 		if (error)
686 			return (error);
687 		for (i = 0; i < pstr.ps_nargvstr; i++) {
688 			sbuf_copyin(sb, pstr.ps_argvstr[i], 0);
689 			sbuf_printf(sb, "%c", '\0');
690 		}
691 	}
692 
693 	return (0);
694 }
695 
696 /*
697  * Filler function for proc/net/dev
698  */
699 static int
700 linprocfs_donetdev(PFS_FILL_ARGS)
701 {
702 	char ifname[16]; /* XXX LINUX_IFNAMSIZ */
703 	struct ifnet *ifp;
704 
705 	sbuf_printf(sb, "%6s|%58s|%s\n%6s|%58s|%58s\n",
706 	    "Inter-", "   Receive", "  Transmit", " face",
707 	    "bytes    packets errs drop fifo frame compressed",
708 	    "bytes    packets errs drop fifo frame compressed");
709 
710 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
711 		linux_ifname(ifp, ifname, sizeof ifname);
712 			sbuf_printf(sb, "%6.6s:", ifname);
713 		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu ",
714 		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
715 		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
716 		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
717 	}
718 
719 	return (0);
720 }
721 
722 #if 0
723 extern struct cdevsw *cdevsw[];
724 
725 /*
726  * Filler function for proc/devices
727  */
728 static int
729 linprocfs_dodevices(PFS_FILL_ARGS)
730 {
731 	int i;
732 
733 	sbuf_printf(sb, "Character devices:\n");
734 
735 	for (i = 0; i < NUMCDEVSW; i++)
736 		if (cdevsw[i] != NULL)
737 			sbuf_printf(sb, "%3d %s\n", i, cdevsw[i]->d_name);
738 
739 	sbuf_printf(sb, "\nBlock devices:\n");
740 
741 	return (0);
742 }
743 #endif
744 
745 /*
746  * Filler function for proc/cmdline
747  */
748 static int
749 linprocfs_docmdline(PFS_FILL_ARGS)
750 {
751 	sbuf_printf(sb, "BOOT_IMAGE=%s", kernelname);
752 	sbuf_printf(sb, " ro root=302\n");
753 	return (0);
754 }
755 
756 #if 0
757 /*
758  * Filler function for proc/modules
759  */
760 static int
761 linprocfs_domodules(PFS_FILL_ARGS)
762 {
763 	struct linker_file *lf;
764 
765 	TAILQ_FOREACH(lf, &linker_files, link) {
766 		sbuf_printf(sb, "%-20s%8lu%4d\n", lf->filename,
767 		    (unsigned long)lf->size, lf->refs);
768 	}
769 	return (0);
770 }
771 #endif
772 
773 /*
774  * Constructor
775  */
776 static int
777 linprocfs_init(PFS_INIT_ARGS)
778 {
779 	struct pfs_node *root;
780 	struct pfs_node *dir;
781 
782 	root = pi->pi_root;
783 
784 #define PFS_CREATE_FILE(name) \
785 	pfs_create_file(root, #name, &linprocfs_do##name, NULL, NULL, PFS_RD)
786 	PFS_CREATE_FILE(cmdline);
787 	PFS_CREATE_FILE(cpuinfo);
788 #if 0
789 	PFS_CREATE_FILE(devices);
790 #endif
791 	PFS_CREATE_FILE(loadavg);
792 	PFS_CREATE_FILE(meminfo);
793 #if 0
794 	PFS_CREATE_FILE(modules);
795 #endif
796 	PFS_CREATE_FILE(mtab);
797 	PFS_CREATE_FILE(stat);
798 	PFS_CREATE_FILE(uptime);
799 	PFS_CREATE_FILE(version);
800 #undef PFS_CREATE_FILE
801 	pfs_create_link(root, "self", &procfs_docurproc,
802 	    NULL, NULL, 0);
803 
804 	dir = pfs_create_dir(root, "net", NULL, NULL, 0);
805 	pfs_create_file(dir, "dev", &linprocfs_donetdev,
806 	    NULL, NULL, PFS_RD);
807 
808 	dir = pfs_create_dir(root, "pid", NULL, NULL, PFS_PROCDEP);
809 	pfs_create_file(dir, "cmdline", &linprocfs_doproccmdline,
810 	    NULL, NULL, PFS_RD);
811 	pfs_create_link(dir, "exe", &procfs_doprocfile,
812 	    NULL, &procfs_notsystem, 0);
813 	pfs_create_file(dir, "mem", &procfs_doprocmem,
814 	    &procfs_attr, &procfs_candebug, PFS_RDWR|PFS_RAW);
815 	pfs_create_file(dir, "stat", &linprocfs_doprocstat,
816 	    NULL, NULL, PFS_RD);
817 	pfs_create_file(dir, "status", &linprocfs_doprocstatus,
818 	    NULL, NULL, PFS_RD);
819 
820 	return (0);
821 }
822 
823 /*
824  * Destructor
825  */
826 static int
827 linprocfs_uninit(PFS_INIT_ARGS)
828 {
829 
830 	/* nothing to do, pseudofs will GC */
831 	return (0);
832 }
833 
834 PSEUDOFS(linprocfs, 1);
835 MODULE_DEPEND(linprocfs, linux, 1, 1, 1);
836 MODULE_DEPEND(linprocfs, procfs, 1, 1, 1);
837