linprocfs.c revision 170170
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 170170 2007-05-31 22:52:15Z attilio $");
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/filedesc.h>
51#include <sys/jail.h>
52#include <sys/kernel.h>
53#include <sys/linker.h>
54#include <sys/lock.h>
55#include <sys/malloc.h>
56#include <sys/mount.h>
57#include <sys/msg.h>
58#include <sys/mutex.h>
59#include <sys/namei.h>
60#include <sys/proc.h>
61#include <sys/resourcevar.h>
62#include <sys/sbuf.h>
63#include <sys/sem.h>
64#include <sys/smp.h>
65#include <sys/socket.h>
66#include <sys/sysctl.h>
67#include <sys/systm.h>
68#include <sys/time.h>
69#include <sys/tty.h>
70#include <sys/user.h>
71#include <sys/vmmeter.h>
72#include <sys/vnode.h>
73
74#include <net/if.h>
75
76#include <vm/vm.h>
77#include <vm/pmap.h>
78#include <vm/vm_map.h>
79#include <vm/vm_param.h>
80#include <vm/vm_object.h>
81#include <vm/swap_pager.h>
82
83#include <machine/clock.h>
84
85#if defined(__i386__) || defined(__amd64__)
86#include <machine/cputypes.h>
87#include <machine/md_var.h>
88#endif /* __i386__ || __amd64__ */
89
90#include "opt_compat.h"
91#ifdef COMPAT_LINUX32				/* XXX */
92#include <machine/../linux32/linux.h>
93#else
94#include <machine/../linux/linux.h>
95#endif
96#include <compat/linux/linux_ioctl.h>
97#include <compat/linux/linux_mib.h>
98#include <compat/linux/linux_util.h>
99#include <fs/pseudofs/pseudofs.h>
100#include <fs/procfs/procfs.h>
101
102/*
103 * Various conversion macros
104 */
105#define T2J(x) (((x) * 100UL) / (stathz ? stathz : hz))	/* ticks to jiffies */
106#define T2S(x) ((x) / (stathz ? stathz : hz))		/* ticks to seconds */
107#define B2K(x) ((x) >> 10)				/* bytes to kbytes */
108#define B2P(x) ((x) >> PAGE_SHIFT)			/* bytes to pages */
109#define P2B(x) ((x) << PAGE_SHIFT)			/* pages to bytes */
110#define P2K(x) ((x) << (PAGE_SHIFT - 10))		/* pages to kbytes */
111
112/**
113 * @brief Mapping of ki_stat in struct kinfo_proc to the linux state
114 *
115 * The linux procfs state field displays one of the characters RSDZTW to
116 * denote running, sleeping in an interruptible wait, waiting in an
117 * uninteruptible disk sleep, a zombie process, process is being traced
118 * or stopped, or process is paging respectively.
119 *
120 * Our struct kinfo_proc contains the variable ki_stat which contains a
121 * value out of SIDL, SRUN, SSLEEP, SSTOP, SZOMB, SWAIT and SLOCK.
122 *
123 * This character array is used with ki_stati-1 as an index and tries to
124 * map our states to suitable linux states.
125 */
126static char linux_state[] = "RRSTZDD";
127
128/*
129 * Filler function for proc/meminfo
130 */
131static int
132linprocfs_domeminfo(PFS_FILL_ARGS)
133{
134	unsigned long memtotal;		/* total memory in bytes */
135	unsigned long memused;		/* used memory in bytes */
136	unsigned long memfree;		/* free memory in bytes */
137	unsigned long memshared;	/* shared memory ??? */
138	unsigned long buffers, cached;	/* buffer / cache memory ??? */
139	unsigned long long swaptotal;	/* total swap space in bytes */
140	unsigned long long swapused;	/* used swap space in bytes */
141	unsigned long long swapfree;	/* free swap space in bytes */
142	vm_object_t object;
143	int i, j;
144
145	memtotal = physmem * PAGE_SIZE;
146	/*
147	 * The correct thing here would be:
148	 *
149	memfree = cnt.v_free_count * PAGE_SIZE;
150	memused = memtotal - memfree;
151	 *
152	 * but it might mislead linux binaries into thinking there
153	 * is very little memory left, so we cheat and tell them that
154	 * all memory that isn't wired down is free.
155	 */
156	memused = cnt.v_wire_count * PAGE_SIZE;
157	memfree = memtotal - memused;
158	swap_pager_status(&i, &j);
159	swaptotal = (unsigned long long)i * PAGE_SIZE;
160	swapused = (unsigned long long)j * PAGE_SIZE;
161	swapfree = swaptotal - swapused;
162	memshared = 0;
163	mtx_lock(&vm_object_list_mtx);
164	TAILQ_FOREACH(object, &vm_object_list, object_list)
165		if (object->shadow_count > 1)
166			memshared += object->resident_page_count;
167	mtx_unlock(&vm_object_list_mtx);
168	memshared *= PAGE_SIZE;
169	/*
170	 * We'd love to be able to write:
171	 *
172	buffers = bufspace;
173	 *
174	 * but bufspace is internal to vfs_bio.c and we don't feel
175	 * like unstaticizing it just for linprocfs's sake.
176	 */
177	buffers = 0;
178	cached = cnt.v_cache_count * PAGE_SIZE;
179
180	sbuf_printf(sb,
181	    "	     total:    used:	free:  shared: buffers:	 cached:\n"
182	    "Mem:  %lu %lu %lu %lu %lu %lu\n"
183	    "Swap: %llu %llu %llu\n"
184	    "MemTotal: %9lu kB\n"
185	    "MemFree:  %9lu kB\n"
186	    "MemShared:%9lu kB\n"
187	    "Buffers:  %9lu kB\n"
188	    "Cached:   %9lu kB\n"
189	    "SwapTotal:%9llu kB\n"
190	    "SwapFree: %9llu kB\n",
191	    memtotal, memused, memfree, memshared, buffers, cached,
192	    swaptotal, swapused, swapfree,
193	    B2K(memtotal), B2K(memfree),
194	    B2K(memshared), B2K(buffers), B2K(cached),
195	    B2K(swaptotal), B2K(swapfree));
196
197	return (0);
198}
199
200#if defined(__i386__) || defined(__amd64__)
201/*
202 * Filler function for proc/cpuinfo (i386 & amd64 version)
203 */
204static int
205linprocfs_docpuinfo(PFS_FILL_ARGS)
206{
207	int hw_model[2];
208	char model[128];
209	size_t size;
210	int class, fqmhz, fqkhz;
211	int i;
212
213	/*
214	 * We default the flags to include all non-conflicting flags,
215	 * and the Intel versions of conflicting flags.
216	 */
217	static char *flags[] = {
218		"fpu",	    "vme",     "de",	   "pse",      "tsc",
219		"msr",	    "pae",     "mce",	   "cx8",      "apic",
220		"sep",	    "sep",     "mtrr",	   "pge",      "mca",
221		"cmov",	    "pat",     "pse36",	   "pn",       "b19",
222		"b20",	    "b21",     "mmxext",   "mmx",      "fxsr",
223		"xmm",	    "b26",     "b27",	   "b28",      "b29",
224		"3dnowext", "3dnow"
225	};
226
227	switch (cpu_class) {
228#ifdef __i386__
229	case CPUCLASS_286:
230		class = 2;
231		break;
232	case CPUCLASS_386:
233		class = 3;
234		break;
235	case CPUCLASS_486:
236		class = 4;
237		break;
238	case CPUCLASS_586:
239		class = 5;
240		break;
241	case CPUCLASS_686:
242		class = 6;
243		break;
244	default:
245		class = 0;
246		break;
247#else /* __amd64__ */
248	default:
249		class = 15;
250		break;
251#endif
252	}
253
254	hw_model[0] = CTL_HW;
255	hw_model[1] = HW_MODEL;
256	model[0] = '\0';
257	size = sizeof(model);
258	if (kernel_sysctl(td, hw_model, 2, &model, &size, 0, 0, 0, 0) != 0)
259		strcpy(model, "unknown");
260	for (i = 0; i < mp_ncpus; ++i) {
261		sbuf_printf(sb,
262		    "processor\t: %d\n"
263		    "vendor_id\t: %.20s\n"
264		    "cpu family\t: %d\n"
265		    "model\t\t: %d\n"
266		    "model name\t: %s\n"
267		    "stepping\t: %d\n",
268		    i, cpu_vendor, class, cpu, model, cpu_id & 0xf);
269		/* XXX per-cpu vendor / class / model / id? */
270	}
271
272	sbuf_cat(sb,
273	    "flags\t\t:");
274
275	if (!strcmp(cpu_vendor, "AuthenticAMD") && (class < 6)) {
276		flags[16] = "fcmov";
277	} else if (!strcmp(cpu_vendor, "CyrixInstead")) {
278		flags[24] = "cxmmx";
279	}
280
281	for (i = 0; i < 32; i++)
282		if (cpu_feature & (1 << i))
283			sbuf_printf(sb, " %s", flags[i]);
284	sbuf_cat(sb, "\n");
285	if (class >= 5) {
286		fqmhz = (tsc_freq + 4999) / 1000000;
287		fqkhz = ((tsc_freq + 4999) / 10000) % 100;
288		sbuf_printf(sb,
289		    "cpu MHz\t\t: %d.%02d\n"
290		    "bogomips\t: %d.%02d\n",
291		    fqmhz, fqkhz, fqmhz, fqkhz);
292	}
293
294	return (0);
295}
296#endif /* __i386__ || __amd64__ */
297
298/*
299 * Filler function for proc/mtab
300 *
301 * This file doesn't exist in Linux' procfs, but is included here so
302 * users can symlink /compat/linux/etc/mtab to /proc/mtab
303 */
304static int
305linprocfs_domtab(PFS_FILL_ARGS)
306{
307	struct nameidata nd;
308	struct mount *mp;
309	const char *lep;
310	char *dlep, *flep, *mntto, *mntfrom, *fstype;
311	size_t lep_len;
312	int error;
313
314	/* resolve symlinks etc. in the emulation tree prefix */
315	NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE, UIO_SYSSPACE, linux_emul_path, td);
316	flep = NULL;
317	error = namei(&nd);
318	VFS_UNLOCK_GIANT(NDHASGIANT(&nd));
319	if (error != 0 || vn_fullpath(td, nd.ni_vp, &dlep, &flep) != 0)
320		lep = linux_emul_path;
321	else
322		lep = dlep;
323	lep_len = strlen(lep);
324
325	mtx_lock(&mountlist_mtx);
326	error = 0;
327	TAILQ_FOREACH(mp, &mountlist, mnt_list) {
328		/* determine device name */
329		mntfrom = mp->mnt_stat.f_mntfromname;
330
331		/* determine mount point */
332		mntto = mp->mnt_stat.f_mntonname;
333		if (strncmp(mntto, lep, lep_len) == 0 &&
334		    mntto[lep_len] == '/')
335			mntto += lep_len;
336
337		/* determine fs type */
338		fstype = mp->mnt_stat.f_fstypename;
339		if (strcmp(fstype, pn->pn_info->pi_name) == 0)
340			mntfrom = fstype = "proc";
341		else if (strcmp(fstype, "procfs") == 0)
342			continue;
343
344		if (strcmp(fstype, "linsysfs") == 0) {
345			sbuf_printf(sb, "/sys %s sysfs %s", mntto,
346			    mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw");
347		} else {
348			sbuf_printf(sb, "%s %s %s %s", mntfrom, mntto, fstype,
349			    mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw");
350		}
351#define ADD_OPTION(opt, name) \
352	if (mp->mnt_stat.f_flags & (opt)) sbuf_printf(sb, "," name);
353		ADD_OPTION(MNT_SYNCHRONOUS,	"sync");
354		ADD_OPTION(MNT_NOEXEC,		"noexec");
355		ADD_OPTION(MNT_NOSUID,		"nosuid");
356		ADD_OPTION(MNT_UNION,		"union");
357		ADD_OPTION(MNT_ASYNC,		"async");
358		ADD_OPTION(MNT_SUIDDIR,		"suiddir");
359		ADD_OPTION(MNT_NOSYMFOLLOW,	"nosymfollow");
360		ADD_OPTION(MNT_NOATIME,		"noatime");
361#undef ADD_OPTION
362		/* a real Linux mtab will also show NFS options */
363		sbuf_printf(sb, " 0 0\n");
364	}
365	mtx_unlock(&mountlist_mtx);
366	if (flep != NULL)
367		free(flep, M_TEMP);
368	return (error);
369}
370
371/*
372 * Filler function for proc/stat
373 */
374static int
375linprocfs_dostat(PFS_FILL_ARGS)
376{
377	int i;
378
379	sbuf_printf(sb, "cpu %ld %ld %ld %ld\n",
380	    T2J(cp_time[CP_USER]),
381	    T2J(cp_time[CP_NICE]),
382	    T2J(cp_time[CP_SYS] /*+ cp_time[CP_INTR]*/),
383	    T2J(cp_time[CP_IDLE]));
384	for (i = 0; i < mp_ncpus; ++i)
385		sbuf_printf(sb, "cpu%d %ld %ld %ld %ld\n", i,
386		    T2J(cp_time[CP_USER]) / mp_ncpus,
387		    T2J(cp_time[CP_NICE]) / mp_ncpus,
388		    T2J(cp_time[CP_SYS]) / mp_ncpus,
389		    T2J(cp_time[CP_IDLE]) / mp_ncpus);
390	sbuf_printf(sb,
391	    "disk 0 0 0 0\n"
392	    "page %u %u\n"
393	    "swap %u %u\n"
394	    "intr %u\n"
395	    "ctxt %u\n"
396	    "btime %lld\n",
397	    cnt.v_vnodepgsin,
398	    cnt.v_vnodepgsout,
399	    cnt.v_swappgsin,
400	    cnt.v_swappgsout,
401	    cnt.v_intr,
402	    cnt.v_swtch,
403	    (long long)boottime.tv_sec);
404	return (0);
405}
406
407/*
408 * Filler function for proc/uptime
409 */
410static int
411linprocfs_douptime(PFS_FILL_ARGS)
412{
413	struct timeval tv;
414
415	getmicrouptime(&tv);
416	sbuf_printf(sb, "%lld.%02ld %ld.%02ld\n",
417	    (long long)tv.tv_sec, tv.tv_usec / 10000,
418	    T2S(cp_time[CP_IDLE]), T2J(cp_time[CP_IDLE]) % 100);
419	return (0);
420}
421
422/*
423 * Get OS build date
424 */
425static void
426linprocfs_osbuild(struct thread *td, struct sbuf *sb)
427{
428#if 0
429	char osbuild[256];
430	char *cp1, *cp2;
431
432	strncpy(osbuild, version, 256);
433	osbuild[255] = '\0';
434	cp1 = strstr(osbuild, "\n");
435	cp2 = strstr(osbuild, ":");
436	if (cp1 && cp2) {
437		*cp1 = *cp2 = '\0';
438		cp1 = strstr(osbuild, "#");
439	} else
440		cp1 = NULL;
441	if (cp1)
442		sbuf_printf(sb, "%s%s", cp1, cp2 + 1);
443	else
444#endif
445		sbuf_cat(sb, "#4 Sun Dec 18 04:30:00 CET 1977");
446}
447
448/*
449 * Get OS builder
450 */
451static void
452linprocfs_osbuilder(struct thread *td, struct sbuf *sb)
453{
454#if 0
455	char builder[256];
456	char *cp;
457
458	cp = strstr(version, "\n    ");
459	if (cp) {
460		strncpy(builder, cp + 5, 256);
461		builder[255] = '\0';
462		cp = strstr(builder, ":");
463		if (cp)
464			*cp = '\0';
465	}
466	if (cp)
467		sbuf_cat(sb, builder);
468	else
469#endif
470		sbuf_cat(sb, "des@freebsd.org");
471}
472
473/*
474 * Filler function for proc/version
475 */
476static int
477linprocfs_doversion(PFS_FILL_ARGS)
478{
479	char osname[LINUX_MAX_UTSNAME];
480	char osrelease[LINUX_MAX_UTSNAME];
481
482	linux_get_osname(td, osname);
483	linux_get_osrelease(td, osrelease);
484	sbuf_printf(sb, "%s version %s (", osname, osrelease);
485	linprocfs_osbuilder(td, sb);
486	sbuf_cat(sb, ") (gcc version " __VERSION__ ") ");
487	linprocfs_osbuild(td, sb);
488	sbuf_cat(sb, "\n");
489
490	return (0);
491}
492
493/*
494 * Filler function for proc/loadavg
495 */
496static int
497linprocfs_doloadavg(PFS_FILL_ARGS)
498{
499
500	sbuf_printf(sb,
501	    "%d.%02d %d.%02d %d.%02d %d/%d %d\n",
502	    (int)(averunnable.ldavg[0] / averunnable.fscale),
503	    (int)(averunnable.ldavg[0] * 100 / averunnable.fscale % 100),
504	    (int)(averunnable.ldavg[1] / averunnable.fscale),
505	    (int)(averunnable.ldavg[1] * 100 / averunnable.fscale % 100),
506	    (int)(averunnable.ldavg[2] / averunnable.fscale),
507	    (int)(averunnable.ldavg[2] * 100 / averunnable.fscale % 100),
508	    1,				/* number of running tasks */
509	    nprocs,			/* number of tasks */
510	    lastpid			/* the last pid */
511	);
512	return (0);
513}
514
515/*
516 * Filler function for proc/pid/stat
517 */
518static int
519linprocfs_doprocstat(PFS_FILL_ARGS)
520{
521	struct kinfo_proc kp;
522	char state;
523	static int ratelimit = 0;
524
525	PROC_LOCK(p);
526	fill_kinfo_proc(p, &kp);
527	sbuf_printf(sb, "%d", p->p_pid);
528#define PS_ADD(name, fmt, arg) sbuf_printf(sb, " " fmt, arg)
529	PS_ADD("comm",		"(%s)",	p->p_comm);
530	if (kp.ki_stat > sizeof(linux_state)) {
531		state = 'R';
532
533		if (ratelimit == 0) {
534			printf("linprocfs: don't know how to handle unknown FreeBSD state %d/%zd, mapping to R\n",
535			    kp.ki_stat, sizeof(linux_state));
536			++ratelimit;
537		}
538	} else
539		state = linux_state[kp.ki_stat - 1];
540	PS_ADD("state",		"%c",	state);
541	PS_ADD("ppid",		"%d",	p->p_pptr ? p->p_pptr->p_pid : 0);
542	PS_ADD("pgrp",		"%d",	p->p_pgid);
543	PS_ADD("session",	"%d",	p->p_session->s_sid);
544	PROC_UNLOCK(p);
545	PS_ADD("tty",		"%d",	0); /* XXX */
546	PS_ADD("tpgid",		"%d",	kp.ki_tpgid);
547	PS_ADD("flags",		"%u",	0); /* XXX */
548	PS_ADD("minflt",	"%lu",	kp.ki_rusage.ru_minflt);
549	PS_ADD("cminflt",	"%lu",	kp.ki_rusage_ch.ru_minflt);
550	PS_ADD("majflt",	"%lu",	kp.ki_rusage.ru_majflt);
551	PS_ADD("cmajflt",	"%lu",	kp.ki_rusage_ch.ru_majflt);
552	PS_ADD("utime",		"%ld",	T2J(tvtohz(&kp.ki_rusage.ru_utime)));
553	PS_ADD("stime",		"%ld",	T2J(tvtohz(&kp.ki_rusage.ru_stime)));
554	PS_ADD("cutime",	"%ld",	T2J(tvtohz(&kp.ki_rusage_ch.ru_utime)));
555	PS_ADD("cstime",	"%ld",	T2J(tvtohz(&kp.ki_rusage_ch.ru_stime)));
556	PS_ADD("priority",	"%d",	kp.ki_pri.pri_user);
557	PS_ADD("nice",		"%d",	kp.ki_nice); /* 19 (nicest) to -19 */
558	PS_ADD("0",		"%d",	0); /* removed field */
559	PS_ADD("itrealvalue",	"%d",	0); /* XXX */
560	/* XXX: starttime is not right, it is the _same_ for _every_ process.
561	   It should be the number of jiffies between system boot and process
562	   start. */
563	PS_ADD("starttime",	"%lu",	T2J(tvtohz(&kp.ki_start)));
564	PS_ADD("vsize",		"%ju",	P2K((uintmax_t)kp.ki_size));
565	PS_ADD("rss",		"%ju",	(uintmax_t)kp.ki_rssize);
566	PS_ADD("rlim",		"%lu",	kp.ki_rusage.ru_maxrss);
567	PS_ADD("startcode",	"%u",	(unsigned)0);
568	PS_ADD("endcode",	"%u",	0); /* XXX */
569	PS_ADD("startstack",	"%u",	0); /* XXX */
570	PS_ADD("kstkesp",	"%u",	0); /* XXX */
571	PS_ADD("kstkeip",	"%u",	0); /* XXX */
572	PS_ADD("signal",	"%u",	0); /* XXX */
573	PS_ADD("blocked",	"%u",	0); /* XXX */
574	PS_ADD("sigignore",	"%u",	0); /* XXX */
575	PS_ADD("sigcatch",	"%u",	0); /* XXX */
576	PS_ADD("wchan",		"%u",	0); /* XXX */
577	PS_ADD("nswap",		"%lu",	kp.ki_rusage.ru_nswap);
578	PS_ADD("cnswap",	"%lu",	kp.ki_rusage_ch.ru_nswap);
579	PS_ADD("exitsignal",	"%d",	0); /* XXX */
580	PS_ADD("processor",	"%u",	kp.ki_lastcpu);
581	PS_ADD("rt_priority",	"%u",	0); /* XXX */ /* >= 2.5.19 */
582	PS_ADD("policy",	"%u",	kp.ki_pri.pri_class); /* >= 2.5.19 */
583#undef PS_ADD
584	sbuf_putc(sb, '\n');
585
586	return (0);
587}
588
589/*
590 * Filler function for proc/pid/statm
591 */
592static int
593linprocfs_doprocstatm(PFS_FILL_ARGS)
594{
595	struct kinfo_proc kp;
596	segsz_t lsize;
597
598	PROC_LOCK(p);
599	fill_kinfo_proc(p, &kp);
600	PROC_UNLOCK(p);
601
602	/*
603	 * See comments in linprocfs_doprocstatus() regarding the
604	 * computation of lsize.
605	 */
606	/* size resident share trs drs lrs dt */
607	sbuf_printf(sb, "%ju ", B2P((uintmax_t)kp.ki_size));
608	sbuf_printf(sb, "%ju ", (uintmax_t)kp.ki_rssize);
609	sbuf_printf(sb, "%ju ", (uintmax_t)0); /* XXX */
610	sbuf_printf(sb, "%ju ",	(uintmax_t)kp.ki_tsize);
611	sbuf_printf(sb, "%ju ", (uintmax_t)(kp.ki_dsize + kp.ki_ssize));
612	lsize = B2P(kp.ki_size) - kp.ki_dsize -
613	    kp.ki_ssize - kp.ki_tsize - 1;
614	sbuf_printf(sb, "%ju ", (uintmax_t)lsize);
615	sbuf_printf(sb, "%ju\n", (uintmax_t)0); /* XXX */
616
617	return (0);
618}
619
620/*
621 * Filler function for proc/pid/status
622 */
623static int
624linprocfs_doprocstatus(PFS_FILL_ARGS)
625{
626	struct kinfo_proc kp;
627	char *state;
628	segsz_t lsize;
629	struct thread *td2;
630	struct sigacts *ps;
631	int i;
632
633	PROC_LOCK(p);
634	td2 = FIRST_THREAD_IN_PROC(p); /* XXXKSE pretend only one thread */
635
636	if (P_SHOULDSTOP(p)) {
637		state = "T (stopped)";
638	} else {
639		mtx_lock_spin(&sched_lock);
640		switch(p->p_state) {
641		case PRS_NEW:
642			state = "I (idle)";
643			break;
644		case PRS_NORMAL:
645			if (p->p_flag & P_WEXIT) {
646				state = "X (exiting)";
647				break;
648			}
649			switch(td2->td_state) {
650			case TDS_INHIBITED:
651				state = "S (sleeping)";
652				break;
653			case TDS_RUNQ:
654			case TDS_RUNNING:
655				state = "R (running)";
656				break;
657			default:
658				state = "? (unknown)";
659				break;
660			}
661			break;
662		case PRS_ZOMBIE:
663			state = "Z (zombie)";
664			break;
665		default:
666			state = "? (unknown)";
667			break;
668		}
669		mtx_unlock_spin(&sched_lock);
670	}
671
672	fill_kinfo_proc(p, &kp);
673	sbuf_printf(sb, "Name:\t%s\n",		p->p_comm); /* XXX escape */
674	sbuf_printf(sb, "State:\t%s\n",		state);
675
676	/*
677	 * Credentials
678	 */
679	sbuf_printf(sb, "Pid:\t%d\n",		p->p_pid);
680	sbuf_printf(sb, "PPid:\t%d\n",		p->p_pptr ?
681						p->p_pptr->p_pid : 0);
682	sbuf_printf(sb, "Uid:\t%d %d %d %d\n",	p->p_ucred->cr_ruid,
683						p->p_ucred->cr_uid,
684						p->p_ucred->cr_svuid,
685						/* FreeBSD doesn't have fsuid */
686						p->p_ucred->cr_uid);
687	sbuf_printf(sb, "Gid:\t%d %d %d %d\n",	p->p_ucred->cr_rgid,
688						p->p_ucred->cr_gid,
689						p->p_ucred->cr_svgid,
690						/* FreeBSD doesn't have fsgid */
691						p->p_ucred->cr_gid);
692	sbuf_cat(sb, "Groups:\t");
693	for (i = 0; i < p->p_ucred->cr_ngroups; i++)
694		sbuf_printf(sb, "%d ",		p->p_ucred->cr_groups[i]);
695	PROC_UNLOCK(p);
696	sbuf_putc(sb, '\n');
697
698	/*
699	 * Memory
700	 *
701	 * While our approximation of VmLib may not be accurate (I
702	 * don't know of a simple way to verify it, and I'm not sure
703	 * it has much meaning anyway), I believe it's good enough.
704	 *
705	 * The same code that could (I think) accurately compute VmLib
706	 * could also compute VmLck, but I don't really care enough to
707	 * implement it. Submissions are welcome.
708	 */
709	sbuf_printf(sb, "VmSize:\t%8ju kB\n",	B2K((uintmax_t)kp.ki_size));
710	sbuf_printf(sb, "VmLck:\t%8u kB\n",	P2K(0)); /* XXX */
711	sbuf_printf(sb, "VmRss:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_rssize));
712	sbuf_printf(sb, "VmData:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_dsize));
713	sbuf_printf(sb, "VmStk:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_ssize));
714	sbuf_printf(sb, "VmExe:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_tsize));
715	lsize = B2P(kp.ki_size) - kp.ki_dsize -
716	    kp.ki_ssize - kp.ki_tsize - 1;
717	sbuf_printf(sb, "VmLib:\t%8ju kB\n",	P2K((uintmax_t)lsize));
718
719	/*
720	 * Signal masks
721	 *
722	 * We support up to 128 signals, while Linux supports 32,
723	 * but we only define 32 (the same 32 as Linux, to boot), so
724	 * just show the lower 32 bits of each mask. XXX hack.
725	 *
726	 * NB: on certain platforms (Sparc at least) Linux actually
727	 * supports 64 signals, but this code is a long way from
728	 * running on anything but i386, so ignore that for now.
729	 */
730	PROC_LOCK(p);
731	sbuf_printf(sb, "SigPnd:\t%08x\n",	p->p_siglist.__bits[0]);
732	/*
733	 * I can't seem to find out where the signal mask is in
734	 * relation to struct proc, so SigBlk is left unimplemented.
735	 */
736	sbuf_printf(sb, "SigBlk:\t%08x\n",	0); /* XXX */
737	ps = p->p_sigacts;
738	mtx_lock(&ps->ps_mtx);
739	sbuf_printf(sb, "SigIgn:\t%08x\n",	ps->ps_sigignore.__bits[0]);
740	sbuf_printf(sb, "SigCgt:\t%08x\n",	ps->ps_sigcatch.__bits[0]);
741	mtx_unlock(&ps->ps_mtx);
742	PROC_UNLOCK(p);
743
744	/*
745	 * Linux also prints the capability masks, but we don't have
746	 * capabilities yet, and when we do get them they're likely to
747	 * be meaningless to Linux programs, so we lie. XXX
748	 */
749	sbuf_printf(sb, "CapInh:\t%016x\n",	0);
750	sbuf_printf(sb, "CapPrm:\t%016x\n",	0);
751	sbuf_printf(sb, "CapEff:\t%016x\n",	0);
752
753	return (0);
754}
755
756
757/*
758 * Filler function for proc/pid/cwd
759 */
760static int
761linprocfs_doproccwd(PFS_FILL_ARGS)
762{
763	char *fullpath = "unknown";
764	char *freepath = NULL;
765
766	vn_fullpath(td, p->p_fd->fd_cdir, &fullpath, &freepath);
767	sbuf_printf(sb, "%s", fullpath);
768	if (freepath)
769		free(freepath, M_TEMP);
770	return (0);
771}
772
773/*
774 * Filler function for proc/pid/root
775 */
776static int
777linprocfs_doprocroot(PFS_FILL_ARGS)
778{
779	struct vnode *rvp;
780	char *fullpath = "unknown";
781	char *freepath = NULL;
782
783	rvp = jailed(p->p_ucred) ? p->p_fd->fd_jdir : p->p_fd->fd_rdir;
784	vn_fullpath(td, rvp, &fullpath, &freepath);
785	sbuf_printf(sb, "%s", fullpath);
786	if (freepath)
787		free(freepath, M_TEMP);
788	return (0);
789}
790
791/*
792 * Filler function for proc/pid/cmdline
793 */
794static int
795linprocfs_doproccmdline(PFS_FILL_ARGS)
796{
797	struct ps_strings pstr;
798	char **ps_argvstr;
799	int error, i;
800
801	/*
802	 * If we are using the ps/cmdline caching, use that.  Otherwise
803	 * revert back to the old way which only implements full cmdline
804	 * for the currept process and just p->p_comm for all other
805	 * processes.
806	 * Note that if the argv is no longer available, we deliberately
807	 * don't fall back on p->p_comm or return an error: the authentic
808	 * Linux behaviour is to return zero-length in this case.
809	 */
810
811	PROC_LOCK(p);
812	if (p->p_args && p_cansee(td, p) == 0) {
813		sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length);
814		PROC_UNLOCK(p);
815	} else if (p != td->td_proc) {
816		PROC_UNLOCK(p);
817		sbuf_printf(sb, "%.*s", MAXCOMLEN, p->p_comm);
818	} else {
819		PROC_UNLOCK(p);
820		error = copyin((void *)p->p_sysent->sv_psstrings, &pstr,
821		    sizeof(pstr));
822		if (error)
823			return (error);
824		if (pstr.ps_nargvstr > ARG_MAX)
825			return (E2BIG);
826		ps_argvstr = malloc(pstr.ps_nargvstr * sizeof(char *),
827		    M_TEMP, M_WAITOK);
828		error = copyin((void *)pstr.ps_argvstr, ps_argvstr,
829		    pstr.ps_nargvstr * sizeof(char *));
830		if (error) {
831			free(ps_argvstr, M_TEMP);
832			return (error);
833		}
834		for (i = 0; i < pstr.ps_nargvstr; i++) {
835			sbuf_copyin(sb, ps_argvstr[i], 0);
836			sbuf_printf(sb, "%c", '\0');
837		}
838		free(ps_argvstr, M_TEMP);
839	}
840
841	return (0);
842}
843
844/*
845 * Filler function for proc/pid/environ
846 */
847static int
848linprocfs_doprocenviron(PFS_FILL_ARGS)
849{
850
851	sbuf_printf(sb, "doprocenviron\n%c", '\0');
852	return (0);
853}
854
855/*
856 * Filler function for proc/pid/maps
857 */
858static int
859linprocfs_doprocmaps(PFS_FILL_ARGS)
860{
861	char mebuffer[512];
862	vm_map_t map = &p->p_vmspace->vm_map;
863	vm_map_entry_t entry, tmp_entry;
864	vm_object_t obj, tobj, lobj;
865	vm_offset_t saved_end;
866	vm_ooffset_t off = 0;
867	char *name = "", *freename = NULL;
868	size_t len;
869	ino_t ino;
870	unsigned int last_timestamp;
871	int ref_count, shadow_count, flags;
872	int error;
873	struct vnode *vp;
874	struct vattr vat;
875	int locked;
876
877	PROC_LOCK(p);
878	error = p_candebug(td, p);
879	PROC_UNLOCK(p);
880	if (error)
881		return (error);
882
883	if (uio->uio_rw != UIO_READ)
884		return (EOPNOTSUPP);
885
886	if (uio->uio_offset != 0)
887		return (0);
888
889	error = 0;
890	vm_map_lock_read(map);
891	for (entry = map->header.next;
892	    ((uio->uio_resid > 0) && (entry != &map->header));
893	    entry = entry->next) {
894		name = "";
895		freename = NULL;
896		if (entry->eflags & MAP_ENTRY_IS_SUB_MAP)
897			continue;
898		saved_end = entry->end;
899		obj = entry->object.vm_object;
900		for (lobj = tobj = obj; tobj; tobj = tobj->backing_object) {
901			VM_OBJECT_LOCK(tobj);
902			if (lobj != obj)
903				VM_OBJECT_UNLOCK(lobj);
904			lobj = tobj;
905		}
906		ino = 0;
907		if (lobj) {
908			off = IDX_TO_OFF(lobj->size);
909			if (lobj->type == OBJT_VNODE) {
910				vp = lobj->handle;
911				if (vp)
912					vref(vp);
913			}
914			else
915				vp = NULL;
916			if (lobj != obj)
917				VM_OBJECT_UNLOCK(lobj);
918			flags = obj->flags;
919			ref_count = obj->ref_count;
920			shadow_count = obj->shadow_count;
921			VM_OBJECT_UNLOCK(obj);
922			if (vp) {
923				vn_fullpath(td, vp, &name, &freename);
924				locked = VFS_LOCK_GIANT(vp->v_mount);
925				vn_lock(vp, LK_SHARED | LK_RETRY, td);
926				VOP_GETATTR(vp, &vat, td->td_ucred, td);
927				ino = vat.va_fileid;
928				vput(vp);
929				VFS_UNLOCK_GIANT(locked);
930			}
931		} else {
932			flags = 0;
933			ref_count = 0;
934			shadow_count = 0;
935		}
936
937		/*
938		 * format:
939		 *  start, end, access, offset, major, minor, inode, name.
940		 */
941		snprintf(mebuffer, sizeof mebuffer,
942		    "%08lx-%08lx %s%s%s%s %08lx %02x:%02x %lu%s%s\n",
943		    (u_long)entry->start, (u_long)entry->end,
944		    (entry->protection & VM_PROT_READ)?"r":"-",
945		    (entry->protection & VM_PROT_WRITE)?"w":"-",
946		    (entry->protection & VM_PROT_EXECUTE)?"x":"-",
947		    "p",
948		    (u_long)off,
949		    0,
950		    0,
951		    (u_long)ino,
952		    *name ? "     " : "",
953		    name
954		    );
955		if (freename)
956			free(freename, M_TEMP);
957		len = strlen(mebuffer);
958		if (len > uio->uio_resid)
959			len = uio->uio_resid; /*
960					       * XXX We should probably return
961					       * EFBIG here, as in procfs.
962					       */
963		last_timestamp = map->timestamp;
964		vm_map_unlock_read(map);
965		error = uiomove(mebuffer, len, uio);
966		vm_map_lock_read(map);
967		if (error)
968			break;
969		if (last_timestamp + 1 != map->timestamp) {
970			/*
971			 * Look again for the entry because the map was
972			 * modified while it was unlocked.  Specifically,
973			 * the entry may have been clipped, merged, or deleted.
974			 */
975			vm_map_lookup_entry(map, saved_end - 1, &tmp_entry);
976			entry = tmp_entry;
977		}
978	}
979	vm_map_unlock_read(map);
980
981	return (error);
982}
983
984/*
985 * Filler function for proc/net/dev
986 */
987static int
988linprocfs_donetdev(PFS_FILL_ARGS)
989{
990	char ifname[16]; /* XXX LINUX_IFNAMSIZ */
991	struct ifnet *ifp;
992
993	sbuf_printf(sb, "%6s|%58s|%s\n%6s|%58s|%58s\n",
994	    "Inter-", "   Receive", "  Transmit", " face",
995	    "bytes    packets errs drop fifo frame compressed",
996	    "bytes    packets errs drop fifo frame compressed");
997
998	IFNET_RLOCK();
999	TAILQ_FOREACH(ifp, &ifnet, if_link) {
1000		linux_ifname(ifp, ifname, sizeof ifname);
1001			sbuf_printf(sb, "%6.6s:", ifname);
1002		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu ",
1003		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
1004		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
1005		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
1006	}
1007	IFNET_RUNLOCK();
1008
1009	return (0);
1010}
1011
1012/*
1013 * Filler function for proc/sys/kernel/osrelease
1014 */
1015static int
1016linprocfs_doosrelease(PFS_FILL_ARGS)
1017{
1018	char osrelease[LINUX_MAX_UTSNAME];
1019
1020	linux_get_osrelease(td, osrelease);
1021	sbuf_printf(sb, "%s\n", osrelease);
1022
1023	return (0);
1024}
1025
1026/*
1027 * Filler function for proc/sys/kernel/ostype
1028 */
1029static int
1030linprocfs_doostype(PFS_FILL_ARGS)
1031{
1032	char osname[LINUX_MAX_UTSNAME];
1033
1034	linux_get_osname(td, osname);
1035	sbuf_printf(sb, "%s\n", osname);
1036
1037	return (0);
1038}
1039
1040/*
1041 * Filler function for proc/sys/kernel/version
1042 */
1043static int
1044linprocfs_doosbuild(PFS_FILL_ARGS)
1045{
1046
1047	linprocfs_osbuild(td, sb);
1048	sbuf_cat(sb, "\n");
1049	return (0);
1050}
1051
1052/*
1053 * Filler function for proc/sys/kernel/msgmni
1054 */
1055static int
1056linprocfs_domsgmni(PFS_FILL_ARGS)
1057{
1058
1059	sbuf_printf(sb, "%d\n", msginfo.msgmni);
1060	return (0);
1061}
1062
1063/*
1064 * Filler function for proc/sys/kernel/pid_max
1065 */
1066static int
1067linprocfs_dopid_max(PFS_FILL_ARGS)
1068{
1069
1070	sbuf_printf(sb, "%i\n", PID_MAX);
1071	return (0);
1072}
1073
1074/*
1075 * Filler function for proc/sys/kernel/sem
1076 */
1077static int
1078linprocfs_dosem(PFS_FILL_ARGS)
1079{
1080
1081	sbuf_printf(sb, "%d %d %d %d\n", seminfo.semmsl, seminfo.semmns,
1082	    seminfo.semopm, seminfo.semmni);
1083	return (0);
1084}
1085
1086/*
1087 * Filler function for proc/scsi/device_info
1088 */
1089static int
1090linprocfs_doscsidevinfo(PFS_FILL_ARGS)
1091{
1092
1093	return (0);
1094}
1095
1096/*
1097 * Filler function for proc/scsi/scsi
1098 */
1099static int
1100linprocfs_doscsiscsi(PFS_FILL_ARGS)
1101{
1102
1103	return (0);
1104}
1105
1106extern struct cdevsw *cdevsw[];
1107
1108/*
1109 * Filler function for proc/devices
1110 */
1111static int
1112linprocfs_dodevices(PFS_FILL_ARGS)
1113{
1114	char *char_devices;
1115	sbuf_printf(sb, "Character devices:\n");
1116
1117	char_devices = linux_get_char_devices();
1118	sbuf_printf(sb, "%s", char_devices);
1119	linux_free_get_char_devices(char_devices);
1120
1121	sbuf_printf(sb, "\nBlock devices:\n");
1122
1123	return (0);
1124}
1125
1126/*
1127 * Filler function for proc/cmdline
1128 */
1129static int
1130linprocfs_docmdline(PFS_FILL_ARGS)
1131{
1132
1133	sbuf_printf(sb, "BOOT_IMAGE=%s", kernelname);
1134	sbuf_printf(sb, " ro root=302\n");
1135	return (0);
1136}
1137
1138#if 0
1139/*
1140 * Filler function for proc/modules
1141 */
1142static int
1143linprocfs_domodules(PFS_FILL_ARGS)
1144{
1145	struct linker_file *lf;
1146
1147	TAILQ_FOREACH(lf, &linker_files, link) {
1148		sbuf_printf(sb, "%-20s%8lu%4d\n", lf->filename,
1149		    (unsigned long)lf->size, lf->refs);
1150	}
1151	return (0);
1152}
1153#endif
1154
1155/*
1156 * Constructor
1157 */
1158static int
1159linprocfs_init(PFS_INIT_ARGS)
1160{
1161	struct pfs_node *root;
1162	struct pfs_node *dir;
1163
1164	root = pi->pi_root;
1165
1166	/* /proc/... */
1167	pfs_create_file(root, "cmdline", &linprocfs_docmdline,
1168	    NULL, NULL, NULL, PFS_RD);
1169	pfs_create_file(root, "cpuinfo", &linprocfs_docpuinfo,
1170	    NULL, NULL, NULL, PFS_RD);
1171	pfs_create_file(root, "devices", &linprocfs_dodevices,
1172	    NULL, NULL, NULL, PFS_RD);
1173	pfs_create_file(root, "loadavg", &linprocfs_doloadavg,
1174	    NULL, NULL, NULL, PFS_RD);
1175	pfs_create_file(root, "meminfo", &linprocfs_domeminfo,
1176	    NULL, NULL, NULL, PFS_RD);
1177#if 0
1178	pfs_create_file(root, "modules", &linprocfs_domodules,
1179	    NULL, NULL, NULL, PFS_RD);
1180#endif
1181	pfs_create_file(root, "mounts", &linprocfs_domtab,
1182	    NULL, NULL, NULL, PFS_RD);
1183	pfs_create_file(root, "mtab", &linprocfs_domtab,
1184	    NULL, NULL, NULL, PFS_RD);
1185	pfs_create_link(root, "self", &procfs_docurproc,
1186	    NULL, NULL, NULL, 0);
1187	pfs_create_file(root, "stat", &linprocfs_dostat,
1188	    NULL, NULL, NULL, PFS_RD);
1189	pfs_create_file(root, "uptime", &linprocfs_douptime,
1190	    NULL, NULL, NULL, PFS_RD);
1191	pfs_create_file(root, "version", &linprocfs_doversion,
1192	    NULL, NULL, NULL, PFS_RD);
1193
1194	/* /proc/net/... */
1195	dir = pfs_create_dir(root, "net", NULL, NULL, NULL, 0);
1196	pfs_create_file(dir, "dev", &linprocfs_donetdev,
1197	    NULL, NULL, NULL, PFS_RD);
1198
1199	/* /proc/<pid>/... */
1200	dir = pfs_create_dir(root, "pid", NULL, NULL, NULL, PFS_PROCDEP);
1201	pfs_create_file(dir, "cmdline", &linprocfs_doproccmdline,
1202	    NULL, NULL, NULL, PFS_RD);
1203	pfs_create_link(dir, "cwd", &linprocfs_doproccwd,
1204	    NULL, NULL, NULL, 0);
1205	pfs_create_file(dir, "environ", &linprocfs_doprocenviron,
1206	    NULL, NULL, NULL, PFS_RD);
1207	pfs_create_link(dir, "exe", &procfs_doprocfile,
1208	    NULL, &procfs_notsystem, NULL, 0);
1209	pfs_create_file(dir, "maps", &linprocfs_doprocmaps,
1210	    NULL, NULL, NULL, PFS_RD);
1211	pfs_create_file(dir, "mem", &procfs_doprocmem,
1212	    &procfs_attr, &procfs_candebug, NULL, PFS_RDWR|PFS_RAW);
1213	pfs_create_link(dir, "root", &linprocfs_doprocroot,
1214	    NULL, NULL, NULL, 0);
1215	pfs_create_file(dir, "stat", &linprocfs_doprocstat,
1216	    NULL, NULL, NULL, PFS_RD);
1217	pfs_create_file(dir, "statm", &linprocfs_doprocstatm,
1218	    NULL, NULL, NULL, PFS_RD);
1219	pfs_create_file(dir, "status", &linprocfs_doprocstatus,
1220	    NULL, NULL, NULL, PFS_RD);
1221
1222	/* /proc/scsi/... */
1223	dir = pfs_create_dir(root, "scsi", NULL, NULL, NULL, 0);
1224	pfs_create_file(dir, "device_info", &linprocfs_doscsidevinfo,
1225	    NULL, NULL, NULL, PFS_RD);
1226	pfs_create_file(dir, "scsi", &linprocfs_doscsiscsi,
1227	    NULL, NULL, NULL, PFS_RD);
1228
1229	/* /proc/sys/... */
1230	dir = pfs_create_dir(root, "sys", NULL, NULL, NULL, 0);
1231	/* /proc/sys/kernel/... */
1232	dir = pfs_create_dir(dir, "kernel", NULL, NULL, NULL, 0);
1233	pfs_create_file(dir, "osrelease", &linprocfs_doosrelease,
1234	    NULL, NULL, NULL, PFS_RD);
1235	pfs_create_file(dir, "ostype", &linprocfs_doostype,
1236	    NULL, NULL, NULL, PFS_RD);
1237	pfs_create_file(dir, "version", &linprocfs_doosbuild,
1238	    NULL, NULL, NULL, PFS_RD);
1239	pfs_create_file(dir, "msgmni", &linprocfs_domsgmni,
1240	    NULL, NULL, NULL, PFS_RD);
1241	pfs_create_file(dir, "pid_max", &linprocfs_dopid_max,
1242	    NULL, NULL, NULL, PFS_RD);
1243	pfs_create_file(dir, "sem", &linprocfs_dosem,
1244	    NULL, NULL, NULL, PFS_RD);
1245
1246	return (0);
1247}
1248
1249/*
1250 * Destructor
1251 */
1252static int
1253linprocfs_uninit(PFS_INIT_ARGS)
1254{
1255
1256	/* nothing to do, pseudofs will GC */
1257	return (0);
1258}
1259
1260PSEUDOFS(linprocfs, 1);
1261MODULE_DEPEND(linprocfs, linux, 1, 1, 1);
1262MODULE_DEPEND(linprocfs, procfs, 1, 1, 1);
1263MODULE_DEPEND(linprocfs, sysvmsg, 1, 1, 1);
1264MODULE_DEPEND(linprocfs, sysvsem, 1, 1, 1);
1265