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