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