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