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