linprocfs.c revision 174070
1139743Simp/*-
265577Sdes * Copyright (c) 2000 Dag-Erling Co�dan Sm�rgrav
365577Sdes * Copyright (c) 1999 Pierre Beyssac
459412Smsmith * Copyright (c) 1993 Jan-Simon Pendry
559412Smsmith * Copyright (c) 1993
659412Smsmith *	The Regents of the University of California.  All rights reserved.
759412Smsmith *
859412Smsmith * This code is derived from software contributed to Berkeley by
959412Smsmith * Jan-Simon Pendry.
1059412Smsmith *
1159412Smsmith * Redistribution and use in source and binary forms, with or without
1259412Smsmith * modification, are permitted provided that the following conditions
1359412Smsmith * are met:
1459412Smsmith * 1. Redistributions of source code must retain the above copyright
1559412Smsmith *    notice, this list of conditions and the following disclaimer.
1659412Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1759412Smsmith *    notice, this list of conditions and the following disclaimer in the
1859412Smsmith *    documentation and/or other materials provided with the distribution.
1959412Smsmith * 3. All advertising materials mentioning features or use of this software
2059412Smsmith *    must display the following acknowledgement:
2159412Smsmith *	This product includes software developed by the University of
2259412Smsmith *	California, Berkeley and its contributors.
2359412Smsmith * 4. Neither the name of the University nor the names of its contributors
2459412Smsmith *    may be used to endorse or promote products derived from this software
2559412Smsmith *    without specific prior written permission.
2659412Smsmith *
2759412Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2859412Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2959412Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3059412Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3159412Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3259412Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3359412Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3459412Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3559412Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3659412Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3759412Smsmith * SUCH DAMAGE.
3859412Smsmith *
3959412Smsmith *	@(#)procfs_status.c	8.4 (Berkeley) 6/15/94
4059412Smsmith */
4159412Smsmith
42116173Sobrien#include <sys/cdefs.h>
43116173Sobrien__FBSDID("$FreeBSD: head/sys/compat/linprocfs/linprocfs.c 174070 2007-11-29 06:34:30Z peter $");
44116173Sobrien
4559412Smsmith#include <sys/param.h>
4683926Sdes#include <sys/queue.h>
4776166Smarkm#include <sys/blist.h>
4874135Sjlemon#include <sys/conf.h>
4983926Sdes#include <sys/exec.h>
50119911Sdes#include <sys/filedesc.h>
5176166Smarkm#include <sys/jail.h>
5265633Sdes#include <sys/kernel.h>
5383926Sdes#include <sys/linker.h>
5476166Smarkm#include <sys/lock.h>
5574135Sjlemon#include <sys/malloc.h>
5678025Sdes#include <sys/mount.h>
57168067Sjkim#include <sys/msg.h>
5876827Salfred#include <sys/mutex.h>
5985289Sdes#include <sys/namei.h>
6065633Sdes#include <sys/proc.h>
6165633Sdes#include <sys/resourcevar.h>
6269995Sdes#include <sys/sbuf.h>
63168067Sjkim#include <sys/sem.h>
64123246Sdes#include <sys/smp.h>
6583926Sdes#include <sys/socket.h>
6676839Sjlemon#include <sys/sysctl.h>
6783926Sdes#include <sys/systm.h>
68159995Snetchild#include <sys/time.h>
6965633Sdes#include <sys/tty.h>
7083926Sdes#include <sys/user.h>
7183926Sdes#include <sys/vmmeter.h>
7259412Smsmith#include <sys/vnode.h>
7359412Smsmith
7483926Sdes#include <net/if.h>
7583926Sdes
7659412Smsmith#include <vm/vm.h>
7759412Smsmith#include <vm/pmap.h>
7867588Sdes#include <vm/vm_map.h>
7959412Smsmith#include <vm/vm_param.h>
8060860Sdes#include <vm/vm_object.h>
8159412Smsmith#include <vm/swap_pager.h>
8269799Sdes
8367589Sdes#include <machine/clock.h>
8478113Sdes
85133822Stjr#if defined(__i386__) || defined(__amd64__)
8667589Sdes#include <machine/cputypes.h>
8759412Smsmith#include <machine/md_var.h>
88133822Stjr#endif /* __i386__ || __amd64__ */
8959412Smsmith
90133822Stjr#include "opt_compat.h"
91140214Sobrien#ifdef COMPAT_LINUX32				/* XXX */
92140214Sobrien#include <machine/../linux32/linux.h>
93140214Sobrien#else
9487275Srwatson#include <machine/../linux/linux.h>
95133822Stjr#endif
9685129Sdes#include <compat/linux/linux_ioctl.h>
9769995Sdes#include <compat/linux/linux_mib.h>
9885289Sdes#include <compat/linux/linux_util.h>
9978025Sdes#include <fs/pseudofs/pseudofs.h>
10084248Sdes#include <fs/procfs/procfs.h>
10159412Smsmith
10267588Sdes/*
10367588Sdes * Various conversion macros
10467588Sdes */
10576405Sdes#define T2J(x) (((x) * 100UL) / (stathz ? stathz : hz))	/* ticks to jiffies */
10667588Sdes#define T2S(x) ((x) / (stathz ? stathz : hz))		/* ticks to seconds */
10767588Sdes#define B2K(x) ((x) >> 10)				/* bytes to kbytes */
10869799Sdes#define B2P(x) ((x) >> PAGE_SHIFT)			/* bytes to pages */
10967588Sdes#define P2B(x) ((x) << PAGE_SHIFT)			/* pages to bytes */
11067588Sdes#define P2K(x) ((x) << (PAGE_SHIFT - 10))		/* pages to kbytes */
11174135Sjlemon
112159995Snetchild/**
113159995Snetchild * @brief Mapping of ki_stat in struct kinfo_proc to the linux state
114159995Snetchild *
115159995Snetchild * The linux procfs state field displays one of the characters RSDZTW to
116159995Snetchild * denote running, sleeping in an interruptible wait, waiting in an
117172568Skevlo * uninterruptible disk sleep, a zombie process, process is being traced
118159995Snetchild * or stopped, or process is paging respectively.
119159995Snetchild *
120159995Snetchild * Our struct kinfo_proc contains the variable ki_stat which contains a
121159995Snetchild * value out of SIDL, SRUN, SSLEEP, SSTOP, SZOMB, SWAIT and SLOCK.
122159995Snetchild *
123159995Snetchild * This character array is used with ki_stati-1 as an index and tries to
124159995Snetchild * map our states to suitable linux states.
125159995Snetchild */
126166140Snetchildstatic char linux_state[] = "RRSTZDD";
127159995Snetchild
12878113Sdes/*
12978113Sdes * Filler function for proc/meminfo
13078113Sdes */
13178025Sdesstatic int
13278025Sdeslinprocfs_domeminfo(PFS_FILL_ARGS)
13359412Smsmith{
13459412Smsmith	unsigned long memtotal;		/* total memory in bytes */
13559412Smsmith	unsigned long memused;		/* used memory in bytes */
13659412Smsmith	unsigned long memfree;		/* free memory in bytes */
13759412Smsmith	unsigned long memshared;	/* shared memory ??? */
13859412Smsmith	unsigned long buffers, cached;	/* buffer / cache memory ??? */
139113574Sjhb	unsigned long long swaptotal;	/* total swap space in bytes */
140113574Sjhb	unsigned long long swapused;	/* used swap space in bytes */
141113574Sjhb	unsigned long long swapfree;	/* free swap space in bytes */
14260860Sdes	vm_object_t object;
143117723Sphk	int i, j;
14459412Smsmith
14559412Smsmith	memtotal = physmem * PAGE_SIZE;
14659412Smsmith	/*
14759412Smsmith	 * The correct thing here would be:
14859412Smsmith	 *
149170170Sattilio	memfree = cnt.v_free_count * PAGE_SIZE;
15059412Smsmith	memused = memtotal - memfree;
15159412Smsmith	 *
15259412Smsmith	 * but it might mislead linux binaries into thinking there
15359412Smsmith	 * is very little memory left, so we cheat and tell them that
15459412Smsmith	 * all memory that isn't wired down is free.
15559412Smsmith	 */
156170170Sattilio	memused = cnt.v_wire_count * PAGE_SIZE;
15759412Smsmith	memfree = memtotal - memused;
158117723Sphk	swap_pager_status(&i, &j);
159153310Smlaier	swaptotal = (unsigned long long)i * PAGE_SIZE;
160153310Smlaier	swapused = (unsigned long long)j * PAGE_SIZE;
161117723Sphk	swapfree = swaptotal - swapused;
16260860Sdes	memshared = 0;
163124082Salc	mtx_lock(&vm_object_list_mtx);
16471471Sjhb	TAILQ_FOREACH(object, &vm_object_list, object_list)
16560860Sdes		if (object->shadow_count > 1)
16660860Sdes			memshared += object->resident_page_count;
167124082Salc	mtx_unlock(&vm_object_list_mtx);
16860860Sdes	memshared *= PAGE_SIZE;
16959412Smsmith	/*
17059412Smsmith	 * We'd love to be able to write:
17159412Smsmith	 *
17259412Smsmith	buffers = bufspace;
17359412Smsmith	 *
17459412Smsmith	 * but bufspace is internal to vfs_bio.c and we don't feel
17559412Smsmith	 * like unstaticizing it just for linprocfs's sake.
17659412Smsmith	 */
17759412Smsmith	buffers = 0;
178170170Sattilio	cached = cnt.v_cache_count * PAGE_SIZE;
17959412Smsmith
18078025Sdes	sbuf_printf(sb,
18178031Sdes	    "	     total:    used:	free:  shared: buffers:	 cached:\n"
18269799Sdes	    "Mem:  %lu %lu %lu %lu %lu %lu\n"
18376839Sjlemon	    "Swap: %llu %llu %llu\n"
18469799Sdes	    "MemTotal: %9lu kB\n"
18569799Sdes	    "MemFree:  %9lu kB\n"
18669799Sdes	    "MemShared:%9lu kB\n"
18769799Sdes	    "Buffers:  %9lu kB\n"
18869799Sdes	    "Cached:   %9lu kB\n"
18976839Sjlemon	    "SwapTotal:%9llu kB\n"
19076839Sjlemon	    "SwapFree: %9llu kB\n",
19169799Sdes	    memtotal, memused, memfree, memshared, buffers, cached,
19269799Sdes	    swaptotal, swapused, swapfree,
19369799Sdes	    B2K(memtotal), B2K(memfree),
19469799Sdes	    B2K(memshared), B2K(buffers), B2K(cached),
19569799Sdes	    B2K(swaptotal), B2K(swapfree));
19659412Smsmith
19778025Sdes	return (0);
19859412Smsmith}
19959412Smsmith
200133822Stjr#if defined(__i386__) || defined(__amd64__)
20178113Sdes/*
202133822Stjr * Filler function for proc/cpuinfo (i386 & amd64 version)
20378113Sdes */
20478113Sdesstatic int
20578113Sdeslinprocfs_docpuinfo(PFS_FILL_ARGS)
20678113Sdes{
207159544Sdes	int hw_model[2];
208159544Sdes	char model[128];
209159544Sdes	size_t size;
210123246Sdes	int class, fqmhz, fqkhz;
211118421Sdes	int i;
21259412Smsmith
21369799Sdes	/*
21478031Sdes	 * We default the flags to include all non-conflicting flags,
21578031Sdes	 * and the Intel versions of conflicting flags.
21669799Sdes	 */
21778031Sdes	static char *flags[] = {
21878031Sdes		"fpu",	    "vme",     "de",	   "pse",      "tsc",
21978031Sdes		"msr",	    "pae",     "mce",	   "cx8",      "apic",
22078031Sdes		"sep",	    "sep",     "mtrr",	   "pge",      "mca",
22178031Sdes		"cmov",	    "pat",     "pse36",	   "pn",       "b19",
22278031Sdes		"b20",	    "b21",     "mmxext",   "mmx",      "fxsr",
22378031Sdes		"xmm",	    "b26",     "b27",	   "b28",      "b29",
22467589Sdes		"3dnowext", "3dnow"
22567589Sdes	};
22667589Sdes
22759412Smsmith	switch (cpu_class) {
228133822Stjr#ifdef __i386__
22959412Smsmith	case CPUCLASS_286:
23067589Sdes		class = 2;
23159412Smsmith		break;
23259412Smsmith	case CPUCLASS_386:
23367589Sdes		class = 3;
23459412Smsmith		break;
23559412Smsmith	case CPUCLASS_486:
23667589Sdes		class = 4;
23759412Smsmith		break;
23859412Smsmith	case CPUCLASS_586:
23967589Sdes		class = 5;
24059412Smsmith		break;
24159412Smsmith	case CPUCLASS_686:
24267589Sdes		class = 6;
24359412Smsmith		break;
24459412Smsmith	default:
24578031Sdes		class = 0;
24659412Smsmith		break;
247159170Sdes#else /* __amd64__ */
248133822Stjr	default:
249159170Sdes		class = 15;
250133822Stjr		break;
251133822Stjr#endif
25259412Smsmith	}
25359412Smsmith
254159544Sdes	hw_model[0] = CTL_HW;
255159544Sdes	hw_model[1] = HW_MODEL;
256159544Sdes	model[0] = '\0';
257159544Sdes	size = sizeof(model);
258159544Sdes	if (kernel_sysctl(td, hw_model, 2, &model, &size, 0, 0, 0, 0) != 0)
259159544Sdes		strcpy(model, "unknown");
260123246Sdes	for (i = 0; i < mp_ncpus; ++i) {
261118421Sdes		sbuf_printf(sb,
262118421Sdes		    "processor\t: %d\n"
263118421Sdes		    "vendor_id\t: %.20s\n"
264118421Sdes		    "cpu family\t: %d\n"
265118421Sdes		    "model\t\t: %d\n"
266159544Sdes		    "model name\t: %s\n"
267118421Sdes		    "stepping\t: %d\n",
268159544Sdes		    i, cpu_vendor, class, cpu, model, cpu_id & 0xf);
269159544Sdes		/* XXX per-cpu vendor / class / model / id? */
270118421Sdes	}
27159412Smsmith
27278031Sdes	sbuf_cat(sb,
27378031Sdes	    "flags\t\t:");
27467589Sdes
27578031Sdes	if (!strcmp(cpu_vendor, "AuthenticAMD") && (class < 6)) {
27667589Sdes		flags[16] = "fcmov";
27778031Sdes	} else if (!strcmp(cpu_vendor, "CyrixInstead")) {
27867589Sdes		flags[24] = "cxmmx";
27978031Sdes	}
280119068Sdes
28178031Sdes	for (i = 0; i < 32; i++)
28267589Sdes		if (cpu_feature & (1 << i))
28378025Sdes			sbuf_printf(sb, " %s", flags[i]);
28478025Sdes	sbuf_cat(sb, "\n");
28578031Sdes	if (class >= 5) {
28669799Sdes		fqmhz = (tsc_freq + 4999) / 1000000;
28769799Sdes		fqkhz = ((tsc_freq + 4999) / 10000) % 100;
28878025Sdes		sbuf_printf(sb,
28969799Sdes		    "cpu MHz\t\t: %d.%02d\n"
29069799Sdes		    "bogomips\t: %d.%02d\n",
29169799Sdes		    fqmhz, fqkhz, fqmhz, fqkhz);
29278031Sdes	}
29369995Sdes
29478025Sdes	return (0);
29559412Smsmith}
296133822Stjr#endif /* __i386__ || __amd64__ */
29765633Sdes
29878113Sdes/*
29985289Sdes * Filler function for proc/mtab
30085289Sdes *
30185289Sdes * This file doesn't exist in Linux' procfs, but is included here so
30285289Sdes * users can symlink /compat/linux/etc/mtab to /proc/mtab
30385289Sdes */
30485289Sdesstatic int
30585289Sdeslinprocfs_domtab(PFS_FILL_ARGS)
30685289Sdes{
30785289Sdes	struct nameidata nd;
30885289Sdes	struct mount *mp;
30991334Sjulian	const char *lep;
31091334Sjulian	char *dlep, *flep, *mntto, *mntfrom, *fstype;
31185289Sdes	size_t lep_len;
31285289Sdes	int error;
31385289Sdes
31485289Sdes	/* resolve symlinks etc. in the emulation tree prefix */
315168942Sdes	NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE, UIO_SYSSPACE, linux_emul_path, td);
31685289Sdes	flep = NULL;
317168942Sdes	error = namei(&nd);
318168942Sdes	VFS_UNLOCK_GIANT(NDHASGIANT(&nd));
319168942Sdes	if (error != 0 || vn_fullpath(td, nd.ni_vp, &dlep, &flep) != 0)
32085289Sdes		lep = linux_emul_path;
32191334Sjulian	else
32291334Sjulian		lep = dlep;
32385289Sdes	lep_len = strlen(lep);
324119068Sdes
32585289Sdes	mtx_lock(&mountlist_mtx);
32685289Sdes	error = 0;
32785289Sdes	TAILQ_FOREACH(mp, &mountlist, mnt_list) {
32885289Sdes		/* determine device name */
32985289Sdes		mntfrom = mp->mnt_stat.f_mntfromname;
330119068Sdes
33185289Sdes		/* determine mount point */
33285289Sdes		mntto = mp->mnt_stat.f_mntonname;
33385289Sdes		if (strncmp(mntto, lep, lep_len) == 0 &&
33485289Sdes		    mntto[lep_len] == '/')
33585289Sdes			mntto += lep_len;
33685289Sdes
33785289Sdes		/* determine fs type */
33885289Sdes		fstype = mp->mnt_stat.f_fstypename;
33985289Sdes		if (strcmp(fstype, pn->pn_info->pi_name) == 0)
34085289Sdes			mntfrom = fstype = "proc";
34185289Sdes		else if (strcmp(fstype, "procfs") == 0)
34285289Sdes			continue;
343119068Sdes
344158311Sambrisko		if (strcmp(fstype, "linsysfs") == 0) {
345158311Sambrisko			sbuf_printf(sb, "/sys %s sysfs %s", mntto,
346158311Sambrisko			    mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw");
347158311Sambrisko		} else {
348158311Sambrisko			sbuf_printf(sb, "%s %s %s %s", mntfrom, mntto, fstype,
349158311Sambrisko			    mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw");
350158311Sambrisko		}
35185289Sdes#define ADD_OPTION(opt, name) \
35285289Sdes	if (mp->mnt_stat.f_flags & (opt)) sbuf_printf(sb, "," name);
35385289Sdes		ADD_OPTION(MNT_SYNCHRONOUS,	"sync");
35485289Sdes		ADD_OPTION(MNT_NOEXEC,		"noexec");
35585289Sdes		ADD_OPTION(MNT_NOSUID,		"nosuid");
35685289Sdes		ADD_OPTION(MNT_UNION,		"union");
35785289Sdes		ADD_OPTION(MNT_ASYNC,		"async");
35885289Sdes		ADD_OPTION(MNT_SUIDDIR,		"suiddir");
35985289Sdes		ADD_OPTION(MNT_NOSYMFOLLOW,	"nosymfollow");
36085289Sdes		ADD_OPTION(MNT_NOATIME,		"noatime");
36185289Sdes#undef ADD_OPTION
36285289Sdes		/* a real Linux mtab will also show NFS options */
36385289Sdes		sbuf_printf(sb, " 0 0\n");
36485289Sdes	}
36585289Sdes	mtx_unlock(&mountlist_mtx);
36685289Sdes	if (flep != NULL)
36785289Sdes		free(flep, M_TEMP);
36885289Sdes	return (error);
36985289Sdes}
37085289Sdes
37185289Sdes/*
37278113Sdes * Filler function for proc/stat
37378113Sdes */
37478025Sdesstatic int
37578025Sdeslinprocfs_dostat(PFS_FILL_ARGS)
37665633Sdes{
377174070Speter	struct pcpu *pcpu;
378174070Speter	long cp_time[CPUSTATES];
379174070Speter	long *cp;
380123246Sdes	int i;
381120339Sdes
382174070Speter	read_cpu_time(cp_time);
383120339Sdes	sbuf_printf(sb, "cpu %ld %ld %ld %ld\n",
384120339Sdes	    T2J(cp_time[CP_USER]),
385120339Sdes	    T2J(cp_time[CP_NICE]),
386120339Sdes	    T2J(cp_time[CP_SYS] /*+ cp_time[CP_INTR]*/),
387120339Sdes	    T2J(cp_time[CP_IDLE]));
388174070Speter	for (i = 0; i <= mp_maxid; ++i) {
389174070Speter		if (CPU_ABSENT(i))
390174070Speter			continue;
391174070Speter		pcpu = pcpu_find(i);
392174070Speter		cp = pcpu->pc_cp_time;
393143194Ssobomax		sbuf_printf(sb, "cpu%d %ld %ld %ld %ld\n", i,
394174070Speter		    T2J(cp[CP_USER]),
395174070Speter		    T2J(cp[CP_NICE]),
396174070Speter		    T2J(cp[CP_SYS] /*+ cp[CP_INTR]*/),
397174070Speter		    T2J(cp[CP_IDLE]));
398174070Speter	}
39978025Sdes	sbuf_printf(sb,
40069799Sdes	    "disk 0 0 0 0\n"
40169799Sdes	    "page %u %u\n"
40269799Sdes	    "swap %u %u\n"
40369799Sdes	    "intr %u\n"
40469799Sdes	    "ctxt %u\n"
40585657Sdillon	    "btime %lld\n",
406170170Sattilio	    cnt.v_vnodepgsin,
407170170Sattilio	    cnt.v_vnodepgsout,
408170170Sattilio	    cnt.v_swappgsin,
409170170Sattilio	    cnt.v_swappgsout,
410170170Sattilio	    cnt.v_intr,
411170170Sattilio	    cnt.v_swtch,
412113574Sjhb	    (long long)boottime.tv_sec);
41378025Sdes	return (0);
41465633Sdes}
41565633Sdes
41678113Sdes/*
41778113Sdes * Filler function for proc/uptime
41878113Sdes */
41978025Sdesstatic int
42078025Sdeslinprocfs_douptime(PFS_FILL_ARGS)
42165633Sdes{
422174070Speter	long cp_time[CPUSTATES];
42365633Sdes	struct timeval tv;
42465633Sdes
42565633Sdes	getmicrouptime(&tv);
426174070Speter	read_cpu_time(cp_time);
42785657Sdillon	sbuf_printf(sb, "%lld.%02ld %ld.%02ld\n",
428113574Sjhb	    (long long)tv.tv_sec, tv.tv_usec / 10000,
42969799Sdes	    T2S(cp_time[CP_IDLE]), T2J(cp_time[CP_IDLE]) % 100);
43078025Sdes	return (0);
43165633Sdes}
43265633Sdes
43378113Sdes/*
434167159Sjkim * Get OS build date
435167159Sjkim */
436167159Sjkimstatic void
437167159Sjkimlinprocfs_osbuild(struct thread *td, struct sbuf *sb)
438167159Sjkim{
439167159Sjkim#if 0
440167159Sjkim	char osbuild[256];
441167159Sjkim	char *cp1, *cp2;
442167159Sjkim
443167159Sjkim	strncpy(osbuild, version, 256);
444167159Sjkim	osbuild[255] = '\0';
445167159Sjkim	cp1 = strstr(osbuild, "\n");
446167159Sjkim	cp2 = strstr(osbuild, ":");
447167159Sjkim	if (cp1 && cp2) {
448167159Sjkim		*cp1 = *cp2 = '\0';
449167159Sjkim		cp1 = strstr(osbuild, "#");
450167159Sjkim	} else
451167159Sjkim		cp1 = NULL;
452167159Sjkim	if (cp1)
453167159Sjkim		sbuf_printf(sb, "%s%s", cp1, cp2 + 1);
454167159Sjkim	else
455167159Sjkim#endif
456167159Sjkim		sbuf_cat(sb, "#4 Sun Dec 18 04:30:00 CET 1977");
457167159Sjkim}
458167159Sjkim
459167159Sjkim/*
460167159Sjkim * Get OS builder
461167159Sjkim */
462167159Sjkimstatic void
463167159Sjkimlinprocfs_osbuilder(struct thread *td, struct sbuf *sb)
464167159Sjkim{
465168762Sdes#if 0
466167159Sjkim	char builder[256];
467167159Sjkim	char *cp;
468167159Sjkim
469167159Sjkim	cp = strstr(version, "\n    ");
470167159Sjkim	if (cp) {
471167159Sjkim		strncpy(builder, cp + 5, 256);
472167159Sjkim		builder[255] = '\0';
473167159Sjkim		cp = strstr(builder, ":");
474167159Sjkim		if (cp)
475167159Sjkim			*cp = '\0';
476167159Sjkim	}
477167159Sjkim	if (cp)
478167159Sjkim		sbuf_cat(sb, builder);
479167159Sjkim	else
480168762Sdes#endif
481167159Sjkim		sbuf_cat(sb, "des@freebsd.org");
482167159Sjkim}
483167159Sjkim
484167159Sjkim/*
48578113Sdes * Filler function for proc/version
48678113Sdes */
48778025Sdesstatic int
48878025Sdeslinprocfs_doversion(PFS_FILL_ARGS)
48965633Sdes{
49087275Srwatson	char osname[LINUX_MAX_UTSNAME];
49187275Srwatson	char osrelease[LINUX_MAX_UTSNAME];
49287275Srwatson
493112206Sjhb	linux_get_osname(td, osname);
494112206Sjhb	linux_get_osrelease(td, osrelease);
495167159Sjkim	sbuf_printf(sb, "%s version %s (", osname, osrelease);
496167159Sjkim	linprocfs_osbuilder(td, sb);
497167159Sjkim	sbuf_cat(sb, ") (gcc version " __VERSION__ ") ");
498167159Sjkim	linprocfs_osbuild(td, sb);
499167159Sjkim	sbuf_cat(sb, "\n");
50087275Srwatson
50178025Sdes	return (0);
50265633Sdes}
50365633Sdes
50478113Sdes/*
50578113Sdes * Filler function for proc/loadavg
50678113Sdes */
50778025Sdesstatic int
50878025Sdeslinprocfs_doloadavg(PFS_FILL_ARGS)
50976839Sjlemon{
510168762Sdes
51178025Sdes	sbuf_printf(sb,
51276839Sjlemon	    "%d.%02d %d.%02d %d.%02d %d/%d %d\n",
51376839Sjlemon	    (int)(averunnable.ldavg[0] / averunnable.fscale),
51476839Sjlemon	    (int)(averunnable.ldavg[0] * 100 / averunnable.fscale % 100),
51576839Sjlemon	    (int)(averunnable.ldavg[1] / averunnable.fscale),
51676839Sjlemon	    (int)(averunnable.ldavg[1] * 100 / averunnable.fscale % 100),
51776839Sjlemon	    (int)(averunnable.ldavg[2] / averunnable.fscale),
51876839Sjlemon	    (int)(averunnable.ldavg[2] * 100 / averunnable.fscale % 100),
51976839Sjlemon	    1,				/* number of running tasks */
52076839Sjlemon	    nprocs,			/* number of tasks */
52178116Sdes	    lastpid			/* the last pid */
52276839Sjlemon	);
52378025Sdes	return (0);
52476839Sjlemon}
52576839Sjlemon
52678113Sdes/*
52778113Sdes * Filler function for proc/pid/stat
52878113Sdes */
52978025Sdesstatic int
53078025Sdeslinprocfs_doprocstat(PFS_FILL_ARGS)
53167588Sdes{
53269995Sdes	struct kinfo_proc kp;
533166140Snetchild	char state;
534166140Snetchild	static int ratelimit = 0;
53567588Sdes
53694307Sjhb	PROC_LOCK(p);
53769995Sdes	fill_kinfo_proc(p, &kp);
53878025Sdes	sbuf_printf(sb, "%d", p->p_pid);
53978025Sdes#define PS_ADD(name, fmt, arg) sbuf_printf(sb, " " fmt, arg)
54067588Sdes	PS_ADD("comm",		"(%s)",	p->p_comm);
541166140Snetchild	if (kp.ki_stat > sizeof(linux_state)) {
542166140Snetchild		state = 'R';
543166140Snetchild
544166141Snetchild		if (ratelimit == 0) {
545166162Snetchild			printf("linprocfs: don't know how to handle unknown FreeBSD state %d/%zd, mapping to R\n",
546166162Snetchild			    kp.ki_stat, sizeof(linux_state));
547166141Snetchild			++ratelimit;
548166141Snetchild		}
549166140Snetchild	} else
550166140Snetchild		state = linux_state[kp.ki_stat - 1];
551166140Snetchild	PS_ADD("state",		"%c",	state);
55273923Sjhb	PS_ADD("ppid",		"%d",	p->p_pptr ? p->p_pptr->p_pid : 0);
55367588Sdes	PS_ADD("pgrp",		"%d",	p->p_pgid);
55467588Sdes	PS_ADD("session",	"%d",	p->p_session->s_sid);
55591140Stanimura	PROC_UNLOCK(p);
55667588Sdes	PS_ADD("tty",		"%d",	0); /* XXX */
557159995Snetchild	PS_ADD("tpgid",		"%d",	kp.ki_tpgid);
55867588Sdes	PS_ADD("flags",		"%u",	0); /* XXX */
559159995Snetchild	PS_ADD("minflt",	"%lu",	kp.ki_rusage.ru_minflt);
560159995Snetchild	PS_ADD("cminflt",	"%lu",	kp.ki_rusage_ch.ru_minflt);
561159995Snetchild	PS_ADD("majflt",	"%lu",	kp.ki_rusage.ru_majflt);
562159995Snetchild	PS_ADD("cmajflt",	"%lu",	kp.ki_rusage_ch.ru_majflt);
563159995Snetchild	PS_ADD("utime",		"%ld",	T2J(tvtohz(&kp.ki_rusage.ru_utime)));
564159995Snetchild	PS_ADD("stime",		"%ld",	T2J(tvtohz(&kp.ki_rusage.ru_stime)));
565159995Snetchild	PS_ADD("cutime",	"%ld",	T2J(tvtohz(&kp.ki_rusage_ch.ru_utime)));
566159995Snetchild	PS_ADD("cstime",	"%ld",	T2J(tvtohz(&kp.ki_rusage_ch.ru_stime)));
567159995Snetchild	PS_ADD("priority",	"%d",	kp.ki_pri.pri_user);
568159995Snetchild	PS_ADD("nice",		"%d",	kp.ki_nice); /* 19 (nicest) to -19 */
569159995Snetchild	PS_ADD("0",		"%d",	0); /* removed field */
570159995Snetchild	PS_ADD("itrealvalue",	"%d",	0); /* XXX */
571159995Snetchild	/* XXX: starttime is not right, it is the _same_ for _every_ process.
572159995Snetchild	   It should be the number of jiffies between system boot and process
573159995Snetchild	   start. */
574159995Snetchild	PS_ADD("starttime",	"%lu",	T2J(tvtohz(&kp.ki_start)));
575159995Snetchild	PS_ADD("vsize",		"%ju",	P2K((uintmax_t)kp.ki_size));
576159995Snetchild	PS_ADD("rss",		"%ju",	(uintmax_t)kp.ki_rssize);
577159995Snetchild	PS_ADD("rlim",		"%lu",	kp.ki_rusage.ru_maxrss);
57869995Sdes	PS_ADD("startcode",	"%u",	(unsigned)0);
57967588Sdes	PS_ADD("endcode",	"%u",	0); /* XXX */
58067588Sdes	PS_ADD("startstack",	"%u",	0); /* XXX */
581159995Snetchild	PS_ADD("kstkesp",	"%u",	0); /* XXX */
582159995Snetchild	PS_ADD("kstkeip",	"%u",	0); /* XXX */
583159995Snetchild	PS_ADD("signal",	"%u",	0); /* XXX */
584159995Snetchild	PS_ADD("blocked",	"%u",	0); /* XXX */
585159995Snetchild	PS_ADD("sigignore",	"%u",	0); /* XXX */
586159995Snetchild	PS_ADD("sigcatch",	"%u",	0); /* XXX */
58767588Sdes	PS_ADD("wchan",		"%u",	0); /* XXX */
588159995Snetchild	PS_ADD("nswap",		"%lu",	kp.ki_rusage.ru_nswap);
589159995Snetchild	PS_ADD("cnswap",	"%lu",	kp.ki_rusage_ch.ru_nswap);
59069799Sdes	PS_ADD("exitsignal",	"%d",	0); /* XXX */
591159995Snetchild	PS_ADD("processor",	"%u",	kp.ki_lastcpu);
592159995Snetchild	PS_ADD("rt_priority",	"%u",	0); /* XXX */ /* >= 2.5.19 */
593159995Snetchild	PS_ADD("policy",	"%u",	kp.ki_pri.pri_class); /* >= 2.5.19 */
59467588Sdes#undef PS_ADD
59578025Sdes	sbuf_putc(sb, '\n');
596119068Sdes
59778025Sdes	return (0);
59867588Sdes}
59967588Sdes
60067588Sdes/*
601119911Sdes * Filler function for proc/pid/statm
602119911Sdes */
603119911Sdesstatic int
604119911Sdeslinprocfs_doprocstatm(PFS_FILL_ARGS)
605119911Sdes{
606119911Sdes	struct kinfo_proc kp;
607119911Sdes	segsz_t lsize;
608120340Sdes
609119911Sdes	PROC_LOCK(p);
610119911Sdes	fill_kinfo_proc(p, &kp);
611119911Sdes	PROC_UNLOCK(p);
612119911Sdes
613119911Sdes	/*
614119911Sdes	 * See comments in linprocfs_doprocstatus() regarding the
615119911Sdes	 * computation of lsize.
616119911Sdes	 */
617119911Sdes	/* size resident share trs drs lrs dt */
618119911Sdes	sbuf_printf(sb, "%ju ", B2P((uintmax_t)kp.ki_size));
619119911Sdes	sbuf_printf(sb, "%ju ", (uintmax_t)kp.ki_rssize);
620119911Sdes	sbuf_printf(sb, "%ju ", (uintmax_t)0); /* XXX */
621119911Sdes	sbuf_printf(sb, "%ju ",	(uintmax_t)kp.ki_tsize);
622119911Sdes	sbuf_printf(sb, "%ju ", (uintmax_t)(kp.ki_dsize + kp.ki_ssize));
623119911Sdes	lsize = B2P(kp.ki_size) - kp.ki_dsize -
624119911Sdes	    kp.ki_ssize - kp.ki_tsize - 1;
625119911Sdes	sbuf_printf(sb, "%ju ", (uintmax_t)lsize);
626119911Sdes	sbuf_printf(sb, "%ju\n", (uintmax_t)0); /* XXX */
627119911Sdes
628119911Sdes	return (0);
629119911Sdes}
630119911Sdes
631119911Sdes/*
63278113Sdes * Filler function for proc/pid/status
63378113Sdes */
63478025Sdesstatic int
63578025Sdeslinprocfs_doprocstatus(PFS_FILL_ARGS)
63667588Sdes{
63769995Sdes	struct kinfo_proc kp;
63867588Sdes	char *state;
63969799Sdes	segsz_t lsize;
64099072Sjulian	struct thread *td2;
641114983Sjhb	struct sigacts *ps;
64274135Sjlemon	int i;
64367588Sdes
644113611Sjhb	PROC_LOCK(p);
64599072Sjulian	td2 = FIRST_THREAD_IN_PROC(p); /* XXXKSE pretend only one thread */
64699072Sjulian
64799072Sjulian	if (P_SHOULDSTOP(p)) {
64899072Sjulian		state = "T (stopped)";
64999072Sjulian	} else {
650170307Sjeff		PROC_SLOCK(p);
65199072Sjulian		switch(p->p_state) {
65299072Sjulian		case PRS_NEW:
65399072Sjulian			state = "I (idle)";
65499072Sjulian			break;
65599072Sjulian		case PRS_NORMAL:
65699072Sjulian			if (p->p_flag & P_WEXIT) {
65799072Sjulian				state = "X (exiting)";
65899072Sjulian				break;
65999072Sjulian			}
66099072Sjulian			switch(td2->td_state) {
661103216Sjulian			case TDS_INHIBITED:
66299072Sjulian				state = "S (sleeping)";
66399072Sjulian				break;
66499072Sjulian			case TDS_RUNQ:
66599072Sjulian			case TDS_RUNNING:
66699072Sjulian				state = "R (running)";
66799072Sjulian				break;
66899072Sjulian			default:
66999072Sjulian				state = "? (unknown)";
67099072Sjulian				break;
67199072Sjulian			}
67299072Sjulian			break;
67399072Sjulian		case PRS_ZOMBIE:
67499072Sjulian			state = "Z (zombie)";
67599072Sjulian			break;
67699072Sjulian		default:
67799072Sjulian			state = "? (unknown)";
67899072Sjulian			break;
67999072Sjulian		}
680170307Sjeff		PROC_SUNLOCK(p);
68199072Sjulian	}
68267588Sdes
68369995Sdes	fill_kinfo_proc(p, &kp);
68478025Sdes	sbuf_printf(sb, "Name:\t%s\n",		p->p_comm); /* XXX escape */
68578031Sdes	sbuf_printf(sb, "State:\t%s\n",		state);
68667588Sdes
68767588Sdes	/*
68867588Sdes	 * Credentials
68967588Sdes	 */
69078025Sdes	sbuf_printf(sb, "Pid:\t%d\n",		p->p_pid);
69178025Sdes	sbuf_printf(sb, "PPid:\t%d\n",		p->p_pptr ?
69273923Sjhb						p->p_pptr->p_pid : 0);
69378031Sdes	sbuf_printf(sb, "Uid:\t%d %d %d %d\n",	p->p_ucred->cr_ruid,
69478031Sdes						p->p_ucred->cr_uid,
69578031Sdes						p->p_ucred->cr_svuid,
69678031Sdes						/* FreeBSD doesn't have fsuid */
69778031Sdes						p->p_ucred->cr_uid);
69878031Sdes	sbuf_printf(sb, "Gid:\t%d %d %d %d\n",	p->p_ucred->cr_rgid,
69978031Sdes						p->p_ucred->cr_gid,
70078031Sdes						p->p_ucred->cr_svgid,
70178031Sdes						/* FreeBSD doesn't have fsgid */
70278031Sdes						p->p_ucred->cr_gid);
70378025Sdes	sbuf_cat(sb, "Groups:\t");
70467588Sdes	for (i = 0; i < p->p_ucred->cr_ngroups; i++)
70578031Sdes		sbuf_printf(sb, "%d ",		p->p_ucred->cr_groups[i]);
70671471Sjhb	PROC_UNLOCK(p);
70778025Sdes	sbuf_putc(sb, '\n');
708119068Sdes
70967588Sdes	/*
71067588Sdes	 * Memory
71169799Sdes	 *
71269799Sdes	 * While our approximation of VmLib may not be accurate (I
71369799Sdes	 * don't know of a simple way to verify it, and I'm not sure
71469799Sdes	 * it has much meaning anyway), I believe it's good enough.
71569799Sdes	 *
71669799Sdes	 * The same code that could (I think) accurately compute VmLib
71769799Sdes	 * could also compute VmLck, but I don't really care enough to
71869799Sdes	 * implement it. Submissions are welcome.
71967588Sdes	 */
720113574Sjhb	sbuf_printf(sb, "VmSize:\t%8ju kB\n",	B2K((uintmax_t)kp.ki_size));
72178025Sdes	sbuf_printf(sb, "VmLck:\t%8u kB\n",	P2K(0)); /* XXX */
722113574Sjhb	sbuf_printf(sb, "VmRss:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_rssize));
723113574Sjhb	sbuf_printf(sb, "VmData:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_dsize));
724113574Sjhb	sbuf_printf(sb, "VmStk:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_ssize));
725113574Sjhb	sbuf_printf(sb, "VmExe:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_tsize));
72669995Sdes	lsize = B2P(kp.ki_size) - kp.ki_dsize -
72769995Sdes	    kp.ki_ssize - kp.ki_tsize - 1;
728113574Sjhb	sbuf_printf(sb, "VmLib:\t%8ju kB\n",	P2K((uintmax_t)lsize));
72967588Sdes
73067588Sdes	/*
73167588Sdes	 * Signal masks
73267588Sdes	 *
73367588Sdes	 * We support up to 128 signals, while Linux supports 32,
73467588Sdes	 * but we only define 32 (the same 32 as Linux, to boot), so
73567588Sdes	 * just show the lower 32 bits of each mask. XXX hack.
73667588Sdes	 *
73767588Sdes	 * NB: on certain platforms (Sparc at least) Linux actually
73867588Sdes	 * supports 64 signals, but this code is a long way from
73967588Sdes	 * running on anything but i386, so ignore that for now.
74067588Sdes	 */
74171471Sjhb	PROC_LOCK(p);
742104306Sjmallett	sbuf_printf(sb, "SigPnd:\t%08x\n",	p->p_siglist.__bits[0]);
74369799Sdes	/*
74469799Sdes	 * I can't seem to find out where the signal mask is in
74569799Sdes	 * relation to struct proc, so SigBlk is left unimplemented.
74669799Sdes	 */
74778025Sdes	sbuf_printf(sb, "SigBlk:\t%08x\n",	0); /* XXX */
748114983Sjhb	ps = p->p_sigacts;
749114983Sjhb	mtx_lock(&ps->ps_mtx);
750114983Sjhb	sbuf_printf(sb, "SigIgn:\t%08x\n",	ps->ps_sigignore.__bits[0]);
751114983Sjhb	sbuf_printf(sb, "SigCgt:\t%08x\n",	ps->ps_sigcatch.__bits[0]);
752114983Sjhb	mtx_unlock(&ps->ps_mtx);
75371471Sjhb	PROC_UNLOCK(p);
754119068Sdes
75567588Sdes	/*
75667588Sdes	 * Linux also prints the capability masks, but we don't have
75767588Sdes	 * capabilities yet, and when we do get them they're likely to
75867588Sdes	 * be meaningless to Linux programs, so we lie. XXX
75967588Sdes	 */
76078025Sdes	sbuf_printf(sb, "CapInh:\t%016x\n",	0);
76178025Sdes	sbuf_printf(sb, "CapPrm:\t%016x\n",	0);
76278025Sdes	sbuf_printf(sb, "CapEff:\t%016x\n",	0);
763119068Sdes
76478025Sdes	return (0);
76567588Sdes}
76674135Sjlemon
767119911Sdes
76878113Sdes/*
769119911Sdes * Filler function for proc/pid/cwd
770119911Sdes */
771119911Sdesstatic int
772119911Sdeslinprocfs_doproccwd(PFS_FILL_ARGS)
773119911Sdes{
774119911Sdes	char *fullpath = "unknown";
775119911Sdes	char *freepath = NULL;
776119911Sdes
777119911Sdes	vn_fullpath(td, p->p_fd->fd_cdir, &fullpath, &freepath);
778119911Sdes	sbuf_printf(sb, "%s", fullpath);
779119911Sdes	if (freepath)
780119911Sdes		free(freepath, M_TEMP);
781119911Sdes	return (0);
782119911Sdes}
783119911Sdes
784119911Sdes/*
785119911Sdes * Filler function for proc/pid/root
786119911Sdes */
787119911Sdesstatic int
788119911Sdeslinprocfs_doprocroot(PFS_FILL_ARGS)
789119911Sdes{
790119911Sdes	struct vnode *rvp;
791119911Sdes	char *fullpath = "unknown";
792119911Sdes	char *freepath = NULL;
793119911Sdes
794119911Sdes	rvp = jailed(p->p_ucred) ? p->p_fd->fd_jdir : p->p_fd->fd_rdir;
795119911Sdes	vn_fullpath(td, rvp, &fullpath, &freepath);
796119911Sdes	sbuf_printf(sb, "%s", fullpath);
797119911Sdes	if (freepath)
798119911Sdes		free(freepath, M_TEMP);
799119911Sdes	return (0);
800119911Sdes}
801119911Sdes
802119911Sdes/*
80378113Sdes * Filler function for proc/pid/cmdline
80478113Sdes */
80578025Sdesstatic int
80678113Sdeslinprocfs_doproccmdline(PFS_FILL_ARGS)
80778113Sdes{
80878113Sdes	struct ps_strings pstr;
809138281Scperciva	char **ps_argvstr;
81078113Sdes	int error, i;
81178113Sdes
81278113Sdes	/*
81378113Sdes	 * If we are using the ps/cmdline caching, use that.  Otherwise
81478113Sdes	 * revert back to the old way which only implements full cmdline
81578113Sdes	 * for the currept process and just p->p_comm for all other
81678113Sdes	 * processes.
81778113Sdes	 * Note that if the argv is no longer available, we deliberately
81878113Sdes	 * don't fall back on p->p_comm or return an error: the authentic
81978113Sdes	 * Linux behaviour is to return zero-length in this case.
82078113Sdes	 */
82178113Sdes
82294620Sjhb	PROC_LOCK(p);
823127694Spjd	if (p->p_args && p_cansee(td, p) == 0) {
82494620Sjhb		sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length);
82594620Sjhb		PROC_UNLOCK(p);
82694620Sjhb	} else if (p != td->td_proc) {
82794620Sjhb		PROC_UNLOCK(p);
82894620Sjhb		sbuf_printf(sb, "%.*s", MAXCOMLEN, p->p_comm);
82994620Sjhb	} else {
83094620Sjhb		PROC_UNLOCK(p);
831103767Sjake		error = copyin((void *)p->p_sysent->sv_psstrings, &pstr,
832103767Sjake		    sizeof(pstr));
83394620Sjhb		if (error)
83494620Sjhb			return (error);
835138281Scperciva		if (pstr.ps_nargvstr > ARG_MAX)
836138281Scperciva			return (E2BIG);
837138281Scperciva		ps_argvstr = malloc(pstr.ps_nargvstr * sizeof(char *),
838138281Scperciva		    M_TEMP, M_WAITOK);
839138281Scperciva		error = copyin((void *)pstr.ps_argvstr, ps_argvstr,
840138281Scperciva		    pstr.ps_nargvstr * sizeof(char *));
841138281Scperciva		if (error) {
842138281Scperciva			free(ps_argvstr, M_TEMP);
843138281Scperciva			return (error);
844138281Scperciva		}
84594620Sjhb		for (i = 0; i < pstr.ps_nargvstr; i++) {
846138281Scperciva			sbuf_copyin(sb, ps_argvstr[i], 0);
84794620Sjhb			sbuf_printf(sb, "%c", '\0');
84878113Sdes		}
849138281Scperciva		free(ps_argvstr, M_TEMP);
85078113Sdes	}
85178113Sdes
85278113Sdes	return (0);
85378113Sdes}
85478113Sdes
85578113Sdes/*
856116173Sobrien * Filler function for proc/pid/environ
857116173Sobrien */
858116173Sobrienstatic int
859116173Sobrienlinprocfs_doprocenviron(PFS_FILL_ARGS)
860116173Sobrien{
861168762Sdes
862116173Sobrien	sbuf_printf(sb, "doprocenviron\n%c", '\0');
863116173Sobrien	return (0);
864116173Sobrien}
865116173Sobrien
866116173Sobrien/*
867116173Sobrien * Filler function for proc/pid/maps
868116173Sobrien */
869116173Sobrienstatic int
870116173Sobrienlinprocfs_doprocmaps(PFS_FILL_ARGS)
871116173Sobrien{
872121265Scognet	char mebuffer[512];
873121246Scognet	vm_map_t map = &p->p_vmspace->vm_map;
874169156Salc	vm_map_entry_t entry, tmp_entry;
875121265Scognet	vm_object_t obj, tobj, lobj;
876169156Salc	vm_offset_t saved_end;
877121265Scognet	vm_ooffset_t off = 0;
878121265Scognet	char *name = "", *freename = NULL;
879121265Scognet	size_t len;
880121265Scognet	ino_t ino;
881169156Salc	unsigned int last_timestamp;
882121265Scognet	int ref_count, shadow_count, flags;
883121265Scognet	int error;
884137507Sphk	struct vnode *vp;
885137507Sphk	struct vattr vat;
886161094Skib	int locked;
887168762Sdes
888121246Scognet	PROC_LOCK(p);
889121246Scognet	error = p_candebug(td, p);
890121246Scognet	PROC_UNLOCK(p);
891121246Scognet	if (error)
892121246Scognet		return (error);
893168762Sdes
894121246Scognet	if (uio->uio_rw != UIO_READ)
895121246Scognet		return (EOPNOTSUPP);
896168762Sdes
897121246Scognet	if (uio->uio_offset != 0)
898121246Scognet		return (0);
899168762Sdes
900121246Scognet	error = 0;
901169156Salc	vm_map_lock_read(map);
902168762Sdes	for (entry = map->header.next;
903121246Scognet	    ((uio->uio_resid > 0) && (entry != &map->header));
904121246Scognet	    entry = entry->next) {
905121265Scognet		name = "";
906121265Scognet		freename = NULL;
907121246Scognet		if (entry->eflags & MAP_ENTRY_IS_SUB_MAP)
908121246Scognet			continue;
909169156Salc		saved_end = entry->end;
910121246Scognet		obj = entry->object.vm_object;
911169156Salc		for (lobj = tobj = obj; tobj; tobj = tobj->backing_object) {
912169156Salc			VM_OBJECT_LOCK(tobj);
913169156Salc			if (lobj != obj)
914169156Salc				VM_OBJECT_UNLOCK(lobj);
915121246Scognet			lobj = tobj;
916169156Salc		}
917121246Scognet		ino = 0;
918121246Scognet		if (lobj) {
919121246Scognet			off = IDX_TO_OFF(lobj->size);
920161094Skib			if (lobj->type == OBJT_VNODE) {
921161094Skib				vp = lobj->handle;
922161094Skib				if (vp)
923161094Skib					vref(vp);
924121246Scognet			}
925161094Skib			else
926161094Skib				vp = NULL;
927169156Salc			if (lobj != obj)
928169156Salc				VM_OBJECT_UNLOCK(lobj);
929121246Scognet			flags = obj->flags;
930121246Scognet			ref_count = obj->ref_count;
931121246Scognet			shadow_count = obj->shadow_count;
932169156Salc			VM_OBJECT_UNLOCK(obj);
933161094Skib			if (vp) {
934161094Skib				vn_fullpath(td, vp, &name, &freename);
935161094Skib				locked = VFS_LOCK_GIANT(vp->v_mount);
936161094Skib				vn_lock(vp, LK_SHARED | LK_RETRY, td);
937161094Skib				VOP_GETATTR(vp, &vat, td->td_ucred, td);
938161094Skib				ino = vat.va_fileid;
939161094Skib				vput(vp);
940161094Skib				VFS_UNLOCK_GIANT(locked);
941161094Skib			}
942121246Scognet		} else {
943121246Scognet			flags = 0;
944121246Scognet			ref_count = 0;
945121246Scognet			shadow_count = 0;
946121246Scognet		}
947168762Sdes
948121246Scognet		/*
949168762Sdes		 * format:
950121246Scognet		 *  start, end, access, offset, major, minor, inode, name.
951121246Scognet		 */
952121246Scognet		snprintf(mebuffer, sizeof mebuffer,
953121246Scognet		    "%08lx-%08lx %s%s%s%s %08lx %02x:%02x %lu%s%s\n",
954121246Scognet		    (u_long)entry->start, (u_long)entry->end,
955121246Scognet		    (entry->protection & VM_PROT_READ)?"r":"-",
956121246Scognet		    (entry->protection & VM_PROT_WRITE)?"w":"-",
957121246Scognet		    (entry->protection & VM_PROT_EXECUTE)?"x":"-",
958121246Scognet		    "p",
959121265Scognet		    (u_long)off,
960121246Scognet		    0,
961121246Scognet		    0,
962121265Scognet		    (u_long)ino,
963121246Scognet		    *name ? "     " : "",
964121246Scognet		    name
965121246Scognet		    );
966121246Scognet		if (freename)
967121246Scognet			free(freename, M_TEMP);
968121246Scognet		len = strlen(mebuffer);
969121246Scognet		if (len > uio->uio_resid)
970121246Scognet			len = uio->uio_resid; /*
971121246Scognet					       * XXX We should probably return
972121246Scognet					       * EFBIG here, as in procfs.
973121246Scognet					       */
974169156Salc		last_timestamp = map->timestamp;
975169156Salc		vm_map_unlock_read(map);
976121246Scognet		error = uiomove(mebuffer, len, uio);
977169156Salc		vm_map_lock_read(map);
978121246Scognet		if (error)
979121246Scognet			break;
980169156Salc		if (last_timestamp + 1 != map->timestamp) {
981169156Salc			/*
982169156Salc			 * Look again for the entry because the map was
983169156Salc			 * modified while it was unlocked.  Specifically,
984169156Salc			 * the entry may have been clipped, merged, or deleted.
985169156Salc			 */
986169156Salc			vm_map_lookup_entry(map, saved_end - 1, &tmp_entry);
987169156Salc			entry = tmp_entry;
988169156Salc		}
989121246Scognet	}
990169156Salc	vm_map_unlock_read(map);
991168762Sdes
992121246Scognet	return (error);
993168762Sdes}
994168762Sdes
995116173Sobrien/*
99678113Sdes * Filler function for proc/net/dev
99778113Sdes */
99878025Sdesstatic int
99978025Sdeslinprocfs_donetdev(PFS_FILL_ARGS)
100074135Sjlemon{
100185129Sdes	char ifname[16]; /* XXX LINUX_IFNAMSIZ */
100274135Sjlemon	struct ifnet *ifp;
100374135Sjlemon
100485129Sdes	sbuf_printf(sb, "%6s|%58s|%s\n%6s|%58s|%58s\n",
100583926Sdes	    "Inter-", "   Receive", "  Transmit", " face",
100685129Sdes	    "bytes    packets errs drop fifo frame compressed",
100783926Sdes	    "bytes    packets errs drop fifo frame compressed");
100874135Sjlemon
1009108172Shsu	IFNET_RLOCK();
101074135Sjlemon	TAILQ_FOREACH(ifp, &ifnet, if_link) {
101185129Sdes		linux_ifname(ifp, ifname, sizeof ifname);
101285129Sdes			sbuf_printf(sb, "%6.6s:", ifname);
101383926Sdes		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu ",
101483926Sdes		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
101583926Sdes		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
101683926Sdes		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
101774135Sjlemon	}
1018108172Shsu	IFNET_RUNLOCK();
1019119068Sdes
102078025Sdes	return (0);
102174135Sjlemon}
102274135Sjlemon
1023158311Sambrisko/*
1024167159Sjkim * Filler function for proc/sys/kernel/osrelease
1025167159Sjkim */
1026167159Sjkimstatic int
1027167159Sjkimlinprocfs_doosrelease(PFS_FILL_ARGS)
1028167159Sjkim{
1029167159Sjkim	char osrelease[LINUX_MAX_UTSNAME];
1030167159Sjkim
1031167159Sjkim	linux_get_osrelease(td, osrelease);
1032167159Sjkim	sbuf_printf(sb, "%s\n", osrelease);
1033167159Sjkim
1034167159Sjkim	return (0);
1035167159Sjkim}
1036167159Sjkim
1037167159Sjkim/*
1038167159Sjkim * Filler function for proc/sys/kernel/ostype
1039167159Sjkim */
1040167159Sjkimstatic int
1041167159Sjkimlinprocfs_doostype(PFS_FILL_ARGS)
1042167159Sjkim{
1043167159Sjkim	char osname[LINUX_MAX_UTSNAME];
1044167159Sjkim
1045167159Sjkim	linux_get_osname(td, osname);
1046167159Sjkim	sbuf_printf(sb, "%s\n", osname);
1047167159Sjkim
1048167159Sjkim	return (0);
1049167159Sjkim}
1050167159Sjkim
1051167159Sjkim/*
1052167159Sjkim * Filler function for proc/sys/kernel/version
1053167159Sjkim */
1054167159Sjkimstatic int
1055167159Sjkimlinprocfs_doosbuild(PFS_FILL_ARGS)
1056167159Sjkim{
1057168762Sdes
1058167159Sjkim	linprocfs_osbuild(td, sb);
1059167159Sjkim	sbuf_cat(sb, "\n");
1060167159Sjkim	return (0);
1061167159Sjkim}
1062167159Sjkim
1063167159Sjkim/*
1064164692Sjkim * Filler function for proc/sys/kernel/msgmni
1065164692Sjkim */
1066164692Sjkimstatic int
1067164692Sjkimlinprocfs_domsgmni(PFS_FILL_ARGS)
1068164692Sjkim{
1069164692Sjkim
1070168067Sjkim	sbuf_printf(sb, "%d\n", msginfo.msgmni);
1071164692Sjkim	return (0);
1072164692Sjkim}
1073164692Sjkim
1074164692Sjkim/*
1075163251Skeramida * Filler function for proc/sys/kernel/pid_max
1076163129Snetchild */
1077163129Snetchildstatic int
1078163129Snetchildlinprocfs_dopid_max(PFS_FILL_ARGS)
1079163129Snetchild{
1080163757Snetchild
1081163129Snetchild	sbuf_printf(sb, "%i\n", PID_MAX);
1082163129Snetchild	return (0);
1083163129Snetchild}
1084163129Snetchild
1085163129Snetchild/*
1086164692Sjkim * Filler function for proc/sys/kernel/sem
1087164692Sjkim */
1088164692Sjkimstatic int
1089164692Sjkimlinprocfs_dosem(PFS_FILL_ARGS)
1090164692Sjkim{
1091164692Sjkim
1092168067Sjkim	sbuf_printf(sb, "%d %d %d %d\n", seminfo.semmsl, seminfo.semmns,
1093168067Sjkim	    seminfo.semopm, seminfo.semmni);
1094164692Sjkim	return (0);
1095164692Sjkim}
1096164692Sjkim
1097164692Sjkim/*
1098158311Sambrisko * Filler function for proc/scsi/device_info
1099158311Sambrisko */
1100158311Sambriskostatic int
1101158311Sambriskolinprocfs_doscsidevinfo(PFS_FILL_ARGS)
1102158311Sambrisko{
1103168762Sdes
1104158311Sambrisko	return (0);
1105158311Sambrisko}
1106158311Sambrisko
1107158311Sambrisko/*
1108158311Sambrisko * Filler function for proc/scsi/scsi
1109158311Sambrisko */
1110158311Sambriskostatic int
1111158311Sambriskolinprocfs_doscsiscsi(PFS_FILL_ARGS)
1112158311Sambrisko{
1113168762Sdes
1114158311Sambrisko	return (0);
1115158311Sambrisko}
1116158311Sambrisko
111785538Sphkextern struct cdevsw *cdevsw[];
111885538Sphk
111978113Sdes/*
112078113Sdes * Filler function for proc/devices
112178113Sdes */
112278025Sdesstatic int
112378025Sdeslinprocfs_dodevices(PFS_FILL_ARGS)
112474135Sjlemon{
1125158311Sambrisko	char *char_devices;
112678025Sdes	sbuf_printf(sb, "Character devices:\n");
112774135Sjlemon
1128158311Sambrisko	char_devices = linux_get_char_devices();
1129158311Sambrisko	sbuf_printf(sb, "%s", char_devices);
1130158311Sambrisko	linux_free_get_char_devices(char_devices);
113174135Sjlemon
113278025Sdes	sbuf_printf(sb, "\nBlock devices:\n");
1133119068Sdes
113478025Sdes	return (0);
113574135Sjlemon}
113674135Sjlemon
113778113Sdes/*
113878113Sdes * Filler function for proc/cmdline
113978113Sdes */
114078025Sdesstatic int
114178025Sdeslinprocfs_docmdline(PFS_FILL_ARGS)
114274135Sjlemon{
1143168762Sdes
114478025Sdes	sbuf_printf(sb, "BOOT_IMAGE=%s", kernelname);
114578025Sdes	sbuf_printf(sb, " ro root=302\n");
114678025Sdes	return (0);
114778025Sdes}
114874135Sjlemon
114983926Sdes#if 0
115078025Sdes/*
115183926Sdes * Filler function for proc/modules
115283926Sdes */
115383926Sdesstatic int
115483926Sdeslinprocfs_domodules(PFS_FILL_ARGS)
115583926Sdes{
115683926Sdes	struct linker_file *lf;
1157119068Sdes
115883926Sdes	TAILQ_FOREACH(lf, &linker_files, link) {
115983926Sdes		sbuf_printf(sb, "%-20s%8lu%4d\n", lf->filename,
116083926Sdes		    (unsigned long)lf->size, lf->refs);
116183926Sdes	}
116283926Sdes	return (0);
116383926Sdes}
116483926Sdes#endif
116583926Sdes
116683926Sdes/*
116785129Sdes * Constructor
116878025Sdes */
116985129Sdesstatic int
117085129Sdeslinprocfs_init(PFS_INIT_ARGS)
117185129Sdes{
117285129Sdes	struct pfs_node *root;
117385129Sdes	struct pfs_node *dir;
117474135Sjlemon
117585129Sdes	root = pi->pi_root;
117678025Sdes
1177119923Sdes	/* /proc/... */
1178119911Sdes	pfs_create_file(root, "cmdline", &linprocfs_docmdline,
1179167482Sdes	    NULL, NULL, NULL, PFS_RD);
1180119911Sdes	pfs_create_file(root, "cpuinfo", &linprocfs_docpuinfo,
1181167482Sdes	    NULL, NULL, NULL, PFS_RD);
1182119911Sdes	pfs_create_file(root, "devices", &linprocfs_dodevices,
1183167482Sdes	    NULL, NULL, NULL, PFS_RD);
1184119911Sdes	pfs_create_file(root, "loadavg", &linprocfs_doloadavg,
1185167482Sdes	    NULL, NULL, NULL, PFS_RD);
1186119911Sdes	pfs_create_file(root, "meminfo", &linprocfs_domeminfo,
1187167482Sdes	    NULL, NULL, NULL, PFS_RD);
118883926Sdes#if 0
1189119911Sdes	pfs_create_file(root, "modules", &linprocfs_domodules,
1190167482Sdes	    NULL, NULL, NULL, PFS_RD);
119183926Sdes#endif
1192158311Sambrisko	pfs_create_file(root, "mounts", &linprocfs_domtab,
1193167482Sdes	    NULL, NULL, NULL, PFS_RD);
1194119911Sdes	pfs_create_file(root, "mtab", &linprocfs_domtab,
1195167482Sdes	    NULL, NULL, NULL, PFS_RD);
119687543Sdes	pfs_create_link(root, "self", &procfs_docurproc,
1197167482Sdes	    NULL, NULL, NULL, 0);
1198119911Sdes	pfs_create_file(root, "stat", &linprocfs_dostat,
1199167482Sdes	    NULL, NULL, NULL, PFS_RD);
1200119911Sdes	pfs_create_file(root, "uptime", &linprocfs_douptime,
1201167482Sdes	    NULL, NULL, NULL, PFS_RD);
1202119911Sdes	pfs_create_file(root, "version", &linprocfs_doversion,
1203167482Sdes	    NULL, NULL, NULL, PFS_RD);
120478025Sdes
1205119923Sdes	/* /proc/net/... */
1206167482Sdes	dir = pfs_create_dir(root, "net", NULL, NULL, NULL, 0);
120785129Sdes	pfs_create_file(dir, "dev", &linprocfs_donetdev,
1208167482Sdes	    NULL, NULL, NULL, PFS_RD);
120978025Sdes
1210119923Sdes	/* /proc/<pid>/... */
1211167482Sdes	dir = pfs_create_dir(root, "pid", NULL, NULL, NULL, PFS_PROCDEP);
121285129Sdes	pfs_create_file(dir, "cmdline", &linprocfs_doproccmdline,
1213167482Sdes	    NULL, NULL, NULL, PFS_RD);
1214119911Sdes	pfs_create_link(dir, "cwd", &linprocfs_doproccwd,
1215167482Sdes	    NULL, NULL, NULL, 0);
1216116173Sobrien	pfs_create_file(dir, "environ", &linprocfs_doprocenviron,
1217167482Sdes	    NULL, NULL, NULL, PFS_RD);
121887543Sdes	pfs_create_link(dir, "exe", &procfs_doprocfile,
1219167482Sdes	    NULL, &procfs_notsystem, NULL, 0);
1220116173Sobrien	pfs_create_file(dir, "maps", &linprocfs_doprocmaps,
1221167482Sdes	    NULL, NULL, NULL, PFS_RD);
122285129Sdes	pfs_create_file(dir, "mem", &procfs_doprocmem,
1223167482Sdes	    &procfs_attr, &procfs_candebug, NULL, PFS_RDWR|PFS_RAW);
1224119911Sdes	pfs_create_link(dir, "root", &linprocfs_doprocroot,
1225167482Sdes	    NULL, NULL, NULL, 0);
122685129Sdes	pfs_create_file(dir, "stat", &linprocfs_doprocstat,
1227167482Sdes	    NULL, NULL, NULL, PFS_RD);
1228119911Sdes	pfs_create_file(dir, "statm", &linprocfs_doprocstatm,
1229167482Sdes	    NULL, NULL, NULL, PFS_RD);
123085129Sdes	pfs_create_file(dir, "status", &linprocfs_doprocstatus,
1231167482Sdes	    NULL, NULL, NULL, PFS_RD);
123285129Sdes
1233158311Sambrisko	/* /proc/scsi/... */
1234167482Sdes	dir = pfs_create_dir(root, "scsi", NULL, NULL, NULL, 0);
1235158311Sambrisko	pfs_create_file(dir, "device_info", &linprocfs_doscsidevinfo,
1236167482Sdes	    NULL, NULL, NULL, PFS_RD);
1237158311Sambrisko	pfs_create_file(dir, "scsi", &linprocfs_doscsiscsi,
1238167482Sdes	    NULL, NULL, NULL, PFS_RD);
1239163129Snetchild
1240163129Snetchild	/* /proc/sys/... */
1241167482Sdes	dir = pfs_create_dir(root, "sys", NULL, NULL, NULL, 0);
1242163129Snetchild	/* /proc/sys/kernel/... */
1243167482Sdes	dir = pfs_create_dir(dir, "kernel", NULL, NULL, NULL, 0);
1244167159Sjkim	pfs_create_file(dir, "osrelease", &linprocfs_doosrelease,
1245167482Sdes	    NULL, NULL, NULL, PFS_RD);
1246167159Sjkim	pfs_create_file(dir, "ostype", &linprocfs_doostype,
1247167482Sdes	    NULL, NULL, NULL, PFS_RD);
1248167159Sjkim	pfs_create_file(dir, "version", &linprocfs_doosbuild,
1249167482Sdes	    NULL, NULL, NULL, PFS_RD);
1250164692Sjkim	pfs_create_file(dir, "msgmni", &linprocfs_domsgmni,
1251167482Sdes	    NULL, NULL, NULL, PFS_RD);
1252163129Snetchild	pfs_create_file(dir, "pid_max", &linprocfs_dopid_max,
1253167482Sdes	    NULL, NULL, NULL, PFS_RD);
1254164692Sjkim	pfs_create_file(dir, "sem", &linprocfs_dosem,
1255167482Sdes	    NULL, NULL, NULL, PFS_RD);
1256163129Snetchild
125785129Sdes	return (0);
125885129Sdes}
125985129Sdes
126085129Sdes/*
126185129Sdes * Destructor
126285129Sdes */
126385129Sdesstatic int
126485129Sdeslinprocfs_uninit(PFS_INIT_ARGS)
126585129Sdes{
126685129Sdes
126785129Sdes	/* nothing to do, pseudofs will GC */
126885129Sdes	return (0);
126985129Sdes}
127085129Sdes
127185129SdesPSEUDOFS(linprocfs, 1);
127278025SdesMODULE_DEPEND(linprocfs, linux, 1, 1, 1);
127378025SdesMODULE_DEPEND(linprocfs, procfs, 1, 1, 1);
1274168440SjkimMODULE_DEPEND(linprocfs, sysvmsg, 1, 1, 1);
1275168440SjkimMODULE_DEPEND(linprocfs, sysvsem, 1, 1, 1);
1276