linprocfs.c revision 186563
1139743Simp/*-
2184691Sdes * 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
42182141Sjulian#include "opt_compat.h"
43182141Sjulian
44116173Sobrien#include <sys/cdefs.h>
45116173Sobrien__FBSDID("$FreeBSD: head/sys/compat/linprocfs/linprocfs.c 186563 2008-12-29 12:45:11Z kib $");
46116173Sobrien
4759412Smsmith#include <sys/param.h>
4883926Sdes#include <sys/queue.h>
4976166Smarkm#include <sys/blist.h>
5074135Sjlemon#include <sys/conf.h>
5183926Sdes#include <sys/exec.h>
52177785Skib#include <sys/fcntl.h>
53119911Sdes#include <sys/filedesc.h>
5476166Smarkm#include <sys/jail.h>
5565633Sdes#include <sys/kernel.h>
5683926Sdes#include <sys/linker.h>
5776166Smarkm#include <sys/lock.h>
5874135Sjlemon#include <sys/malloc.h>
5978025Sdes#include <sys/mount.h>
60168067Sjkim#include <sys/msg.h>
6176827Salfred#include <sys/mutex.h>
6285289Sdes#include <sys/namei.h>
6365633Sdes#include <sys/proc.h>
6465633Sdes#include <sys/resourcevar.h>
6569995Sdes#include <sys/sbuf.h>
66168067Sjkim#include <sys/sem.h>
67123246Sdes#include <sys/smp.h>
6883926Sdes#include <sys/socket.h>
6976839Sjlemon#include <sys/sysctl.h>
7083926Sdes#include <sys/systm.h>
71159995Snetchild#include <sys/time.h>
7265633Sdes#include <sys/tty.h>
7383926Sdes#include <sys/user.h>
7483926Sdes#include <sys/vmmeter.h>
7559412Smsmith#include <sys/vnode.h>
76181803Sbz#include <sys/vimage.h>
7759412Smsmith
7883926Sdes#include <net/if.h>
79185571Sbz#include <net/vnet.h>
8083926Sdes
8159412Smsmith#include <vm/vm.h>
82185984Skib#include <vm/vm_extern.h>
8359412Smsmith#include <vm/pmap.h>
8467588Sdes#include <vm/vm_map.h>
8559412Smsmith#include <vm/vm_param.h>
8660860Sdes#include <vm/vm_object.h>
8759412Smsmith#include <vm/swap_pager.h>
8869799Sdes
8967589Sdes#include <machine/clock.h>
9078113Sdes
91133822Stjr#if defined(__i386__) || defined(__amd64__)
9267589Sdes#include <machine/cputypes.h>
9359412Smsmith#include <machine/md_var.h>
94133822Stjr#endif /* __i386__ || __amd64__ */
9559412Smsmith
96140214Sobrien#ifdef COMPAT_LINUX32				/* XXX */
97140214Sobrien#include <machine/../linux32/linux.h>
98140214Sobrien#else
9987275Srwatson#include <machine/../linux/linux.h>
100133822Stjr#endif
10185129Sdes#include <compat/linux/linux_ioctl.h>
10269995Sdes#include <compat/linux/linux_mib.h>
10385289Sdes#include <compat/linux/linux_util.h>
10478025Sdes#include <fs/pseudofs/pseudofs.h>
10584248Sdes#include <fs/procfs/procfs.h>
10659412Smsmith
10767588Sdes/*
10867588Sdes * Various conversion macros
10967588Sdes */
11076405Sdes#define T2J(x) (((x) * 100UL) / (stathz ? stathz : hz))	/* ticks to jiffies */
11167588Sdes#define T2S(x) ((x) / (stathz ? stathz : hz))		/* ticks to seconds */
11267588Sdes#define B2K(x) ((x) >> 10)				/* bytes to kbytes */
11369799Sdes#define B2P(x) ((x) >> PAGE_SHIFT)			/* bytes to pages */
11467588Sdes#define P2B(x) ((x) << PAGE_SHIFT)			/* pages to bytes */
11567588Sdes#define P2K(x) ((x) << (PAGE_SHIFT - 10))		/* pages to kbytes */
11674135Sjlemon
117159995Snetchild/**
118159995Snetchild * @brief Mapping of ki_stat in struct kinfo_proc to the linux state
119159995Snetchild *
120159995Snetchild * The linux procfs state field displays one of the characters RSDZTW to
121159995Snetchild * denote running, sleeping in an interruptible wait, waiting in an
122172568Skevlo * uninterruptible disk sleep, a zombie process, process is being traced
123159995Snetchild * or stopped, or process is paging respectively.
124159995Snetchild *
125159995Snetchild * Our struct kinfo_proc contains the variable ki_stat which contains a
126159995Snetchild * value out of SIDL, SRUN, SSLEEP, SSTOP, SZOMB, SWAIT and SLOCK.
127159995Snetchild *
128159995Snetchild * This character array is used with ki_stati-1 as an index and tries to
129159995Snetchild * map our states to suitable linux states.
130159995Snetchild */
131166140Snetchildstatic char linux_state[] = "RRSTZDD";
132159995Snetchild
13378113Sdes/*
13478113Sdes * Filler function for proc/meminfo
13578113Sdes */
13678025Sdesstatic int
13778025Sdeslinprocfs_domeminfo(PFS_FILL_ARGS)
13859412Smsmith{
13959412Smsmith	unsigned long memtotal;		/* total memory in bytes */
14059412Smsmith	unsigned long memused;		/* used memory in bytes */
14159412Smsmith	unsigned long memfree;		/* free memory in bytes */
14259412Smsmith	unsigned long memshared;	/* shared memory ??? */
14359412Smsmith	unsigned long buffers, cached;	/* buffer / cache memory ??? */
144113574Sjhb	unsigned long long swaptotal;	/* total swap space in bytes */
145113574Sjhb	unsigned long long swapused;	/* used swap space in bytes */
146113574Sjhb	unsigned long long swapfree;	/* free swap space in bytes */
14760860Sdes	vm_object_t object;
148117723Sphk	int i, j;
14959412Smsmith
15059412Smsmith	memtotal = physmem * PAGE_SIZE;
15159412Smsmith	/*
15259412Smsmith	 * The correct thing here would be:
15359412Smsmith	 *
154170170Sattilio	memfree = cnt.v_free_count * PAGE_SIZE;
15559412Smsmith	memused = memtotal - memfree;
15659412Smsmith	 *
15759412Smsmith	 * but it might mislead linux binaries into thinking there
15859412Smsmith	 * is very little memory left, so we cheat and tell them that
15959412Smsmith	 * all memory that isn't wired down is free.
16059412Smsmith	 */
161170170Sattilio	memused = cnt.v_wire_count * PAGE_SIZE;
16259412Smsmith	memfree = memtotal - memused;
163117723Sphk	swap_pager_status(&i, &j);
164153310Smlaier	swaptotal = (unsigned long long)i * PAGE_SIZE;
165153310Smlaier	swapused = (unsigned long long)j * PAGE_SIZE;
166117723Sphk	swapfree = swaptotal - swapused;
16760860Sdes	memshared = 0;
168124082Salc	mtx_lock(&vm_object_list_mtx);
16971471Sjhb	TAILQ_FOREACH(object, &vm_object_list, object_list)
17060860Sdes		if (object->shadow_count > 1)
17160860Sdes			memshared += object->resident_page_count;
172124082Salc	mtx_unlock(&vm_object_list_mtx);
17360860Sdes	memshared *= PAGE_SIZE;
17459412Smsmith	/*
17559412Smsmith	 * We'd love to be able to write:
17659412Smsmith	 *
17759412Smsmith	buffers = bufspace;
17859412Smsmith	 *
17959412Smsmith	 * but bufspace is internal to vfs_bio.c and we don't feel
18059412Smsmith	 * like unstaticizing it just for linprocfs's sake.
18159412Smsmith	 */
18259412Smsmith	buffers = 0;
183170170Sattilio	cached = cnt.v_cache_count * PAGE_SIZE;
18459412Smsmith
18578025Sdes	sbuf_printf(sb,
18678031Sdes	    "	     total:    used:	free:  shared: buffers:	 cached:\n"
18769799Sdes	    "Mem:  %lu %lu %lu %lu %lu %lu\n"
18876839Sjlemon	    "Swap: %llu %llu %llu\n"
18969799Sdes	    "MemTotal: %9lu kB\n"
19069799Sdes	    "MemFree:  %9lu kB\n"
19169799Sdes	    "MemShared:%9lu kB\n"
19269799Sdes	    "Buffers:  %9lu kB\n"
19369799Sdes	    "Cached:   %9lu kB\n"
19476839Sjlemon	    "SwapTotal:%9llu kB\n"
19576839Sjlemon	    "SwapFree: %9llu kB\n",
19669799Sdes	    memtotal, memused, memfree, memshared, buffers, cached,
19769799Sdes	    swaptotal, swapused, swapfree,
19869799Sdes	    B2K(memtotal), B2K(memfree),
19969799Sdes	    B2K(memshared), B2K(buffers), B2K(cached),
20069799Sdes	    B2K(swaptotal), B2K(swapfree));
20159412Smsmith
20278025Sdes	return (0);
20359412Smsmith}
20459412Smsmith
205133822Stjr#if defined(__i386__) || defined(__amd64__)
20678113Sdes/*
207133822Stjr * Filler function for proc/cpuinfo (i386 & amd64 version)
20878113Sdes */
20978113Sdesstatic int
21078113Sdeslinprocfs_docpuinfo(PFS_FILL_ARGS)
21178113Sdes{
212159544Sdes	int hw_model[2];
213159544Sdes	char model[128];
214159544Sdes	size_t size;
215123246Sdes	int class, fqmhz, fqkhz;
216118421Sdes	int i;
21759412Smsmith
21869799Sdes	/*
21978031Sdes	 * We default the flags to include all non-conflicting flags,
22078031Sdes	 * and the Intel versions of conflicting flags.
22169799Sdes	 */
22278031Sdes	static char *flags[] = {
22378031Sdes		"fpu",	    "vme",     "de",	   "pse",      "tsc",
22478031Sdes		"msr",	    "pae",     "mce",	   "cx8",      "apic",
22578031Sdes		"sep",	    "sep",     "mtrr",	   "pge",      "mca",
22678031Sdes		"cmov",	    "pat",     "pse36",	   "pn",       "b19",
22778031Sdes		"b20",	    "b21",     "mmxext",   "mmx",      "fxsr",
228183385Scognet		"xmm",	    "sse2",    "b27",	   "b28",      "b29",
22967589Sdes		"3dnowext", "3dnow"
23067589Sdes	};
23167589Sdes
23259412Smsmith	switch (cpu_class) {
233133822Stjr#ifdef __i386__
23459412Smsmith	case CPUCLASS_286:
23567589Sdes		class = 2;
23659412Smsmith		break;
23759412Smsmith	case CPUCLASS_386:
23867589Sdes		class = 3;
23959412Smsmith		break;
24059412Smsmith	case CPUCLASS_486:
24167589Sdes		class = 4;
24259412Smsmith		break;
24359412Smsmith	case CPUCLASS_586:
24467589Sdes		class = 5;
24559412Smsmith		break;
24659412Smsmith	case CPUCLASS_686:
24767589Sdes		class = 6;
24859412Smsmith		break;
24959412Smsmith	default:
25078031Sdes		class = 0;
25159412Smsmith		break;
252159170Sdes#else /* __amd64__ */
253133822Stjr	default:
254159170Sdes		class = 15;
255133822Stjr		break;
256133822Stjr#endif
25759412Smsmith	}
25859412Smsmith
259159544Sdes	hw_model[0] = CTL_HW;
260159544Sdes	hw_model[1] = HW_MODEL;
261159544Sdes	model[0] = '\0';
262159544Sdes	size = sizeof(model);
263159544Sdes	if (kernel_sysctl(td, hw_model, 2, &model, &size, 0, 0, 0, 0) != 0)
264159544Sdes		strcpy(model, "unknown");
265123246Sdes	for (i = 0; i < mp_ncpus; ++i) {
266118421Sdes		sbuf_printf(sb,
267118421Sdes		    "processor\t: %d\n"
268118421Sdes		    "vendor_id\t: %.20s\n"
269118421Sdes		    "cpu family\t: %d\n"
270118421Sdes		    "model\t\t: %d\n"
271159544Sdes		    "model name\t: %s\n"
272118421Sdes		    "stepping\t: %d\n",
273159544Sdes		    i, cpu_vendor, class, cpu, model, cpu_id & 0xf);
274159544Sdes		/* XXX per-cpu vendor / class / model / id? */
275118421Sdes	}
27659412Smsmith
277185766Skib	sbuf_cat(sb, "flags\t\t:");
27867589Sdes
27978031Sdes	if (!strcmp(cpu_vendor, "AuthenticAMD") && (class < 6)) {
28067589Sdes		flags[16] = "fcmov";
28178031Sdes	} else if (!strcmp(cpu_vendor, "CyrixInstead")) {
28267589Sdes		flags[24] = "cxmmx";
28378031Sdes	}
284119068Sdes
28578031Sdes	for (i = 0; i < 32; i++)
28667589Sdes		if (cpu_feature & (1 << i))
28778025Sdes			sbuf_printf(sb, " %s", flags[i]);
28878025Sdes	sbuf_cat(sb, "\n");
28978031Sdes	if (class >= 5) {
29069799Sdes		fqmhz = (tsc_freq + 4999) / 1000000;
29169799Sdes		fqkhz = ((tsc_freq + 4999) / 10000) % 100;
29278025Sdes		sbuf_printf(sb,
29369799Sdes		    "cpu MHz\t\t: %d.%02d\n"
29469799Sdes		    "bogomips\t: %d.%02d\n",
29569799Sdes		    fqmhz, fqkhz, fqmhz, fqkhz);
29678031Sdes	}
29769995Sdes
29878025Sdes	return (0);
29959412Smsmith}
300133822Stjr#endif /* __i386__ || __amd64__ */
30165633Sdes
30278113Sdes/*
30385289Sdes * Filler function for proc/mtab
30485289Sdes *
30585289Sdes * This file doesn't exist in Linux' procfs, but is included here so
30685289Sdes * users can symlink /compat/linux/etc/mtab to /proc/mtab
30785289Sdes */
30885289Sdesstatic int
30985289Sdeslinprocfs_domtab(PFS_FILL_ARGS)
31085289Sdes{
31185289Sdes	struct nameidata nd;
31285289Sdes	struct mount *mp;
31391334Sjulian	const char *lep;
31491334Sjulian	char *dlep, *flep, *mntto, *mntfrom, *fstype;
31585289Sdes	size_t lep_len;
31685289Sdes	int error;
31785289Sdes
31885289Sdes	/* resolve symlinks etc. in the emulation tree prefix */
319168942Sdes	NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE, UIO_SYSSPACE, linux_emul_path, td);
32085289Sdes	flep = NULL;
321168942Sdes	error = namei(&nd);
322184649Sjhb	lep = linux_emul_path;
323184649Sjhb	if (error == 0) {
324184649Sjhb		if (vn_fullpath(td, nd.ni_vp, &dlep, &flep) != 0)
325184649Sjhb			lep = dlep;
326184649Sjhb		vrele(nd.ni_vp);
327184649Sjhb		VFS_UNLOCK_GIANT(NDHASGIANT(&nd));
328184649Sjhb	}
32985289Sdes	lep_len = strlen(lep);
330119068Sdes
33185289Sdes	mtx_lock(&mountlist_mtx);
33285289Sdes	error = 0;
33385289Sdes	TAILQ_FOREACH(mp, &mountlist, mnt_list) {
33485289Sdes		/* determine device name */
33585289Sdes		mntfrom = mp->mnt_stat.f_mntfromname;
336119068Sdes
33785289Sdes		/* determine mount point */
33885289Sdes		mntto = mp->mnt_stat.f_mntonname;
33985289Sdes		if (strncmp(mntto, lep, lep_len) == 0 &&
34085289Sdes		    mntto[lep_len] == '/')
34185289Sdes			mntto += lep_len;
34285289Sdes
34385289Sdes		/* determine fs type */
34485289Sdes		fstype = mp->mnt_stat.f_fstypename;
34585289Sdes		if (strcmp(fstype, pn->pn_info->pi_name) == 0)
34685289Sdes			mntfrom = fstype = "proc";
34785289Sdes		else if (strcmp(fstype, "procfs") == 0)
34885289Sdes			continue;
349119068Sdes
350158311Sambrisko		if (strcmp(fstype, "linsysfs") == 0) {
351158311Sambrisko			sbuf_printf(sb, "/sys %s sysfs %s", mntto,
352158311Sambrisko			    mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw");
353158311Sambrisko		} else {
354158311Sambrisko			sbuf_printf(sb, "%s %s %s %s", mntfrom, mntto, fstype,
355158311Sambrisko			    mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw");
356158311Sambrisko		}
35785289Sdes#define ADD_OPTION(opt, name) \
35885289Sdes	if (mp->mnt_stat.f_flags & (opt)) sbuf_printf(sb, "," name);
35985289Sdes		ADD_OPTION(MNT_SYNCHRONOUS,	"sync");
36085289Sdes		ADD_OPTION(MNT_NOEXEC,		"noexec");
36185289Sdes		ADD_OPTION(MNT_NOSUID,		"nosuid");
36285289Sdes		ADD_OPTION(MNT_UNION,		"union");
36385289Sdes		ADD_OPTION(MNT_ASYNC,		"async");
36485289Sdes		ADD_OPTION(MNT_SUIDDIR,		"suiddir");
36585289Sdes		ADD_OPTION(MNT_NOSYMFOLLOW,	"nosymfollow");
36685289Sdes		ADD_OPTION(MNT_NOATIME,		"noatime");
36785289Sdes#undef ADD_OPTION
36885289Sdes		/* a real Linux mtab will also show NFS options */
36985289Sdes		sbuf_printf(sb, " 0 0\n");
37085289Sdes	}
37185289Sdes	mtx_unlock(&mountlist_mtx);
37285289Sdes	if (flep != NULL)
37385289Sdes		free(flep, M_TEMP);
37485289Sdes	return (error);
37585289Sdes}
37685289Sdes
37785289Sdes/*
37878113Sdes * Filler function for proc/stat
37978113Sdes */
38078025Sdesstatic int
38178025Sdeslinprocfs_dostat(PFS_FILL_ARGS)
38265633Sdes{
383174070Speter	struct pcpu *pcpu;
384174070Speter	long cp_time[CPUSTATES];
385174070Speter	long *cp;
386123246Sdes	int i;
387120339Sdes
388174070Speter	read_cpu_time(cp_time);
389120339Sdes	sbuf_printf(sb, "cpu %ld %ld %ld %ld\n",
390120339Sdes	    T2J(cp_time[CP_USER]),
391120339Sdes	    T2J(cp_time[CP_NICE]),
392120339Sdes	    T2J(cp_time[CP_SYS] /*+ cp_time[CP_INTR]*/),
393120339Sdes	    T2J(cp_time[CP_IDLE]));
394174070Speter	for (i = 0; i <= mp_maxid; ++i) {
395174070Speter		if (CPU_ABSENT(i))
396174070Speter			continue;
397174070Speter		pcpu = pcpu_find(i);
398174070Speter		cp = pcpu->pc_cp_time;
399143194Ssobomax		sbuf_printf(sb, "cpu%d %ld %ld %ld %ld\n", i,
400174070Speter		    T2J(cp[CP_USER]),
401174070Speter		    T2J(cp[CP_NICE]),
402174070Speter		    T2J(cp[CP_SYS] /*+ cp[CP_INTR]*/),
403174070Speter		    T2J(cp[CP_IDLE]));
404174070Speter	}
40578025Sdes	sbuf_printf(sb,
40669799Sdes	    "disk 0 0 0 0\n"
40769799Sdes	    "page %u %u\n"
40869799Sdes	    "swap %u %u\n"
40969799Sdes	    "intr %u\n"
41069799Sdes	    "ctxt %u\n"
41185657Sdillon	    "btime %lld\n",
412170170Sattilio	    cnt.v_vnodepgsin,
413170170Sattilio	    cnt.v_vnodepgsout,
414170170Sattilio	    cnt.v_swappgsin,
415170170Sattilio	    cnt.v_swappgsout,
416170170Sattilio	    cnt.v_intr,
417170170Sattilio	    cnt.v_swtch,
418113574Sjhb	    (long long)boottime.tv_sec);
41978025Sdes	return (0);
42065633Sdes}
42165633Sdes
42278113Sdes/*
42378113Sdes * Filler function for proc/uptime
42478113Sdes */
42578025Sdesstatic int
42678025Sdeslinprocfs_douptime(PFS_FILL_ARGS)
42765633Sdes{
428174070Speter	long cp_time[CPUSTATES];
42965633Sdes	struct timeval tv;
43065633Sdes
43165633Sdes	getmicrouptime(&tv);
432174070Speter	read_cpu_time(cp_time);
43385657Sdillon	sbuf_printf(sb, "%lld.%02ld %ld.%02ld\n",
434113574Sjhb	    (long long)tv.tv_sec, tv.tv_usec / 10000,
43569799Sdes	    T2S(cp_time[CP_IDLE]), T2J(cp_time[CP_IDLE]) % 100);
43678025Sdes	return (0);
43765633Sdes}
43865633Sdes
43978113Sdes/*
440167159Sjkim * Get OS build date
441167159Sjkim */
442167159Sjkimstatic void
443167159Sjkimlinprocfs_osbuild(struct thread *td, struct sbuf *sb)
444167159Sjkim{
445167159Sjkim#if 0
446167159Sjkim	char osbuild[256];
447167159Sjkim	char *cp1, *cp2;
448167159Sjkim
449167159Sjkim	strncpy(osbuild, version, 256);
450167159Sjkim	osbuild[255] = '\0';
451167159Sjkim	cp1 = strstr(osbuild, "\n");
452167159Sjkim	cp2 = strstr(osbuild, ":");
453167159Sjkim	if (cp1 && cp2) {
454167159Sjkim		*cp1 = *cp2 = '\0';
455167159Sjkim		cp1 = strstr(osbuild, "#");
456167159Sjkim	} else
457167159Sjkim		cp1 = NULL;
458167159Sjkim	if (cp1)
459167159Sjkim		sbuf_printf(sb, "%s%s", cp1, cp2 + 1);
460167159Sjkim	else
461167159Sjkim#endif
462167159Sjkim		sbuf_cat(sb, "#4 Sun Dec 18 04:30:00 CET 1977");
463167159Sjkim}
464167159Sjkim
465167159Sjkim/*
466167159Sjkim * Get OS builder
467167159Sjkim */
468167159Sjkimstatic void
469167159Sjkimlinprocfs_osbuilder(struct thread *td, struct sbuf *sb)
470167159Sjkim{
471168762Sdes#if 0
472167159Sjkim	char builder[256];
473167159Sjkim	char *cp;
474167159Sjkim
475167159Sjkim	cp = strstr(version, "\n    ");
476167159Sjkim	if (cp) {
477167159Sjkim		strncpy(builder, cp + 5, 256);
478167159Sjkim		builder[255] = '\0';
479167159Sjkim		cp = strstr(builder, ":");
480167159Sjkim		if (cp)
481167159Sjkim			*cp = '\0';
482167159Sjkim	}
483167159Sjkim	if (cp)
484167159Sjkim		sbuf_cat(sb, builder);
485167159Sjkim	else
486168762Sdes#endif
487167159Sjkim		sbuf_cat(sb, "des@freebsd.org");
488167159Sjkim}
489167159Sjkim
490167159Sjkim/*
49178113Sdes * Filler function for proc/version
49278113Sdes */
49378025Sdesstatic int
49478025Sdeslinprocfs_doversion(PFS_FILL_ARGS)
49565633Sdes{
49687275Srwatson	char osname[LINUX_MAX_UTSNAME];
49787275Srwatson	char osrelease[LINUX_MAX_UTSNAME];
49887275Srwatson
499112206Sjhb	linux_get_osname(td, osname);
500112206Sjhb	linux_get_osrelease(td, osrelease);
501167159Sjkim	sbuf_printf(sb, "%s version %s (", osname, osrelease);
502167159Sjkim	linprocfs_osbuilder(td, sb);
503167159Sjkim	sbuf_cat(sb, ") (gcc version " __VERSION__ ") ");
504167159Sjkim	linprocfs_osbuild(td, sb);
505167159Sjkim	sbuf_cat(sb, "\n");
50687275Srwatson
50778025Sdes	return (0);
50865633Sdes}
50965633Sdes
51078113Sdes/*
51178113Sdes * Filler function for proc/loadavg
51278113Sdes */
51378025Sdesstatic int
51478025Sdeslinprocfs_doloadavg(PFS_FILL_ARGS)
51576839Sjlemon{
516168762Sdes
51778025Sdes	sbuf_printf(sb,
51876839Sjlemon	    "%d.%02d %d.%02d %d.%02d %d/%d %d\n",
51976839Sjlemon	    (int)(averunnable.ldavg[0] / averunnable.fscale),
52076839Sjlemon	    (int)(averunnable.ldavg[0] * 100 / averunnable.fscale % 100),
52176839Sjlemon	    (int)(averunnable.ldavg[1] / averunnable.fscale),
52276839Sjlemon	    (int)(averunnable.ldavg[1] * 100 / averunnable.fscale % 100),
52376839Sjlemon	    (int)(averunnable.ldavg[2] / averunnable.fscale),
52476839Sjlemon	    (int)(averunnable.ldavg[2] * 100 / averunnable.fscale % 100),
52576839Sjlemon	    1,				/* number of running tasks */
52676839Sjlemon	    nprocs,			/* number of tasks */
52778116Sdes	    lastpid			/* the last pid */
52876839Sjlemon	);
52978025Sdes	return (0);
53076839Sjlemon}
53176839Sjlemon
53278113Sdes/*
53378113Sdes * Filler function for proc/pid/stat
53478113Sdes */
53578025Sdesstatic int
53678025Sdeslinprocfs_doprocstat(PFS_FILL_ARGS)
53767588Sdes{
53869995Sdes	struct kinfo_proc kp;
539166140Snetchild	char state;
540166140Snetchild	static int ratelimit = 0;
54167588Sdes
54294307Sjhb	PROC_LOCK(p);
54369995Sdes	fill_kinfo_proc(p, &kp);
54478025Sdes	sbuf_printf(sb, "%d", p->p_pid);
54578025Sdes#define PS_ADD(name, fmt, arg) sbuf_printf(sb, " " fmt, arg)
54667588Sdes	PS_ADD("comm",		"(%s)",	p->p_comm);
547166140Snetchild	if (kp.ki_stat > sizeof(linux_state)) {
548166140Snetchild		state = 'R';
549166140Snetchild
550166141Snetchild		if (ratelimit == 0) {
551166162Snetchild			printf("linprocfs: don't know how to handle unknown FreeBSD state %d/%zd, mapping to R\n",
552166162Snetchild			    kp.ki_stat, sizeof(linux_state));
553166141Snetchild			++ratelimit;
554166141Snetchild		}
555166140Snetchild	} else
556166140Snetchild		state = linux_state[kp.ki_stat - 1];
557166140Snetchild	PS_ADD("state",		"%c",	state);
55873923Sjhb	PS_ADD("ppid",		"%d",	p->p_pptr ? p->p_pptr->p_pid : 0);
55967588Sdes	PS_ADD("pgrp",		"%d",	p->p_pgid);
56067588Sdes	PS_ADD("session",	"%d",	p->p_session->s_sid);
56191140Stanimura	PROC_UNLOCK(p);
56267588Sdes	PS_ADD("tty",		"%d",	0); /* XXX */
563159995Snetchild	PS_ADD("tpgid",		"%d",	kp.ki_tpgid);
56467588Sdes	PS_ADD("flags",		"%u",	0); /* XXX */
565159995Snetchild	PS_ADD("minflt",	"%lu",	kp.ki_rusage.ru_minflt);
566159995Snetchild	PS_ADD("cminflt",	"%lu",	kp.ki_rusage_ch.ru_minflt);
567159995Snetchild	PS_ADD("majflt",	"%lu",	kp.ki_rusage.ru_majflt);
568159995Snetchild	PS_ADD("cmajflt",	"%lu",	kp.ki_rusage_ch.ru_majflt);
569159995Snetchild	PS_ADD("utime",		"%ld",	T2J(tvtohz(&kp.ki_rusage.ru_utime)));
570159995Snetchild	PS_ADD("stime",		"%ld",	T2J(tvtohz(&kp.ki_rusage.ru_stime)));
571159995Snetchild	PS_ADD("cutime",	"%ld",	T2J(tvtohz(&kp.ki_rusage_ch.ru_utime)));
572159995Snetchild	PS_ADD("cstime",	"%ld",	T2J(tvtohz(&kp.ki_rusage_ch.ru_stime)));
573159995Snetchild	PS_ADD("priority",	"%d",	kp.ki_pri.pri_user);
574159995Snetchild	PS_ADD("nice",		"%d",	kp.ki_nice); /* 19 (nicest) to -19 */
575159995Snetchild	PS_ADD("0",		"%d",	0); /* removed field */
576159995Snetchild	PS_ADD("itrealvalue",	"%d",	0); /* XXX */
577159995Snetchild	/* XXX: starttime is not right, it is the _same_ for _every_ process.
578159995Snetchild	   It should be the number of jiffies between system boot and process
579159995Snetchild	   start. */
580159995Snetchild	PS_ADD("starttime",	"%lu",	T2J(tvtohz(&kp.ki_start)));
581159995Snetchild	PS_ADD("vsize",		"%ju",	P2K((uintmax_t)kp.ki_size));
582159995Snetchild	PS_ADD("rss",		"%ju",	(uintmax_t)kp.ki_rssize);
583159995Snetchild	PS_ADD("rlim",		"%lu",	kp.ki_rusage.ru_maxrss);
58469995Sdes	PS_ADD("startcode",	"%u",	(unsigned)0);
58567588Sdes	PS_ADD("endcode",	"%u",	0); /* XXX */
58667588Sdes	PS_ADD("startstack",	"%u",	0); /* XXX */
587159995Snetchild	PS_ADD("kstkesp",	"%u",	0); /* XXX */
588159995Snetchild	PS_ADD("kstkeip",	"%u",	0); /* XXX */
589159995Snetchild	PS_ADD("signal",	"%u",	0); /* XXX */
590159995Snetchild	PS_ADD("blocked",	"%u",	0); /* XXX */
591159995Snetchild	PS_ADD("sigignore",	"%u",	0); /* XXX */
592159995Snetchild	PS_ADD("sigcatch",	"%u",	0); /* XXX */
59367588Sdes	PS_ADD("wchan",		"%u",	0); /* XXX */
594159995Snetchild	PS_ADD("nswap",		"%lu",	kp.ki_rusage.ru_nswap);
595159995Snetchild	PS_ADD("cnswap",	"%lu",	kp.ki_rusage_ch.ru_nswap);
59669799Sdes	PS_ADD("exitsignal",	"%d",	0); /* XXX */
597159995Snetchild	PS_ADD("processor",	"%u",	kp.ki_lastcpu);
598159995Snetchild	PS_ADD("rt_priority",	"%u",	0); /* XXX */ /* >= 2.5.19 */
599159995Snetchild	PS_ADD("policy",	"%u",	kp.ki_pri.pri_class); /* >= 2.5.19 */
60067588Sdes#undef PS_ADD
60178025Sdes	sbuf_putc(sb, '\n');
602119068Sdes
60378025Sdes	return (0);
60467588Sdes}
60567588Sdes
60667588Sdes/*
607119911Sdes * Filler function for proc/pid/statm
608119911Sdes */
609119911Sdesstatic int
610119911Sdeslinprocfs_doprocstatm(PFS_FILL_ARGS)
611119911Sdes{
612119911Sdes	struct kinfo_proc kp;
613119911Sdes	segsz_t lsize;
614120340Sdes
615119911Sdes	PROC_LOCK(p);
616119911Sdes	fill_kinfo_proc(p, &kp);
617119911Sdes	PROC_UNLOCK(p);
618119911Sdes
619119911Sdes	/*
620119911Sdes	 * See comments in linprocfs_doprocstatus() regarding the
621119911Sdes	 * computation of lsize.
622119911Sdes	 */
623119911Sdes	/* size resident share trs drs lrs dt */
624119911Sdes	sbuf_printf(sb, "%ju ", B2P((uintmax_t)kp.ki_size));
625119911Sdes	sbuf_printf(sb, "%ju ", (uintmax_t)kp.ki_rssize);
626119911Sdes	sbuf_printf(sb, "%ju ", (uintmax_t)0); /* XXX */
627119911Sdes	sbuf_printf(sb, "%ju ",	(uintmax_t)kp.ki_tsize);
628119911Sdes	sbuf_printf(sb, "%ju ", (uintmax_t)(kp.ki_dsize + kp.ki_ssize));
629119911Sdes	lsize = B2P(kp.ki_size) - kp.ki_dsize -
630119911Sdes	    kp.ki_ssize - kp.ki_tsize - 1;
631119911Sdes	sbuf_printf(sb, "%ju ", (uintmax_t)lsize);
632119911Sdes	sbuf_printf(sb, "%ju\n", (uintmax_t)0); /* XXX */
633119911Sdes
634119911Sdes	return (0);
635119911Sdes}
636119911Sdes
637119911Sdes/*
63878113Sdes * Filler function for proc/pid/status
63978113Sdes */
64078025Sdesstatic int
64178025Sdeslinprocfs_doprocstatus(PFS_FILL_ARGS)
64267588Sdes{
64369995Sdes	struct kinfo_proc kp;
64467588Sdes	char *state;
64569799Sdes	segsz_t lsize;
64699072Sjulian	struct thread *td2;
647114983Sjhb	struct sigacts *ps;
64874135Sjlemon	int i;
64967588Sdes
650113611Sjhb	PROC_LOCK(p);
65199072Sjulian	td2 = FIRST_THREAD_IN_PROC(p); /* XXXKSE pretend only one thread */
65299072Sjulian
65399072Sjulian	if (P_SHOULDSTOP(p)) {
65499072Sjulian		state = "T (stopped)";
65599072Sjulian	} else {
656170307Sjeff		PROC_SLOCK(p);
65799072Sjulian		switch(p->p_state) {
65899072Sjulian		case PRS_NEW:
65999072Sjulian			state = "I (idle)";
66099072Sjulian			break;
66199072Sjulian		case PRS_NORMAL:
66299072Sjulian			if (p->p_flag & P_WEXIT) {
66399072Sjulian				state = "X (exiting)";
66499072Sjulian				break;
66599072Sjulian			}
66699072Sjulian			switch(td2->td_state) {
667103216Sjulian			case TDS_INHIBITED:
66899072Sjulian				state = "S (sleeping)";
66999072Sjulian				break;
67099072Sjulian			case TDS_RUNQ:
67199072Sjulian			case TDS_RUNNING:
67299072Sjulian				state = "R (running)";
67399072Sjulian				break;
67499072Sjulian			default:
67599072Sjulian				state = "? (unknown)";
67699072Sjulian				break;
67799072Sjulian			}
67899072Sjulian			break;
67999072Sjulian		case PRS_ZOMBIE:
68099072Sjulian			state = "Z (zombie)";
68199072Sjulian			break;
68299072Sjulian		default:
68399072Sjulian			state = "? (unknown)";
68499072Sjulian			break;
68599072Sjulian		}
686170307Sjeff		PROC_SUNLOCK(p);
68799072Sjulian	}
68867588Sdes
68969995Sdes	fill_kinfo_proc(p, &kp);
69078025Sdes	sbuf_printf(sb, "Name:\t%s\n",		p->p_comm); /* XXX escape */
69178031Sdes	sbuf_printf(sb, "State:\t%s\n",		state);
69267588Sdes
69367588Sdes	/*
69467588Sdes	 * Credentials
69567588Sdes	 */
69678025Sdes	sbuf_printf(sb, "Pid:\t%d\n",		p->p_pid);
69778025Sdes	sbuf_printf(sb, "PPid:\t%d\n",		p->p_pptr ?
69873923Sjhb						p->p_pptr->p_pid : 0);
69978031Sdes	sbuf_printf(sb, "Uid:\t%d %d %d %d\n",	p->p_ucred->cr_ruid,
70078031Sdes						p->p_ucred->cr_uid,
70178031Sdes						p->p_ucred->cr_svuid,
70278031Sdes						/* FreeBSD doesn't have fsuid */
70378031Sdes						p->p_ucred->cr_uid);
70478031Sdes	sbuf_printf(sb, "Gid:\t%d %d %d %d\n",	p->p_ucred->cr_rgid,
70578031Sdes						p->p_ucred->cr_gid,
70678031Sdes						p->p_ucred->cr_svgid,
70778031Sdes						/* FreeBSD doesn't have fsgid */
70878031Sdes						p->p_ucred->cr_gid);
70978025Sdes	sbuf_cat(sb, "Groups:\t");
71067588Sdes	for (i = 0; i < p->p_ucred->cr_ngroups; i++)
71178031Sdes		sbuf_printf(sb, "%d ",		p->p_ucred->cr_groups[i]);
71271471Sjhb	PROC_UNLOCK(p);
71378025Sdes	sbuf_putc(sb, '\n');
714119068Sdes
71567588Sdes	/*
71667588Sdes	 * Memory
71769799Sdes	 *
71869799Sdes	 * While our approximation of VmLib may not be accurate (I
71969799Sdes	 * don't know of a simple way to verify it, and I'm not sure
72069799Sdes	 * it has much meaning anyway), I believe it's good enough.
72169799Sdes	 *
72269799Sdes	 * The same code that could (I think) accurately compute VmLib
72369799Sdes	 * could also compute VmLck, but I don't really care enough to
72469799Sdes	 * implement it. Submissions are welcome.
72567588Sdes	 */
726113574Sjhb	sbuf_printf(sb, "VmSize:\t%8ju kB\n",	B2K((uintmax_t)kp.ki_size));
72778025Sdes	sbuf_printf(sb, "VmLck:\t%8u kB\n",	P2K(0)); /* XXX */
728113574Sjhb	sbuf_printf(sb, "VmRss:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_rssize));
729113574Sjhb	sbuf_printf(sb, "VmData:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_dsize));
730113574Sjhb	sbuf_printf(sb, "VmStk:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_ssize));
731113574Sjhb	sbuf_printf(sb, "VmExe:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_tsize));
73269995Sdes	lsize = B2P(kp.ki_size) - kp.ki_dsize -
73369995Sdes	    kp.ki_ssize - kp.ki_tsize - 1;
734113574Sjhb	sbuf_printf(sb, "VmLib:\t%8ju kB\n",	P2K((uintmax_t)lsize));
73567588Sdes
73667588Sdes	/*
73767588Sdes	 * Signal masks
73867588Sdes	 *
73967588Sdes	 * We support up to 128 signals, while Linux supports 32,
74067588Sdes	 * but we only define 32 (the same 32 as Linux, to boot), so
74167588Sdes	 * just show the lower 32 bits of each mask. XXX hack.
74267588Sdes	 *
74367588Sdes	 * NB: on certain platforms (Sparc at least) Linux actually
74467588Sdes	 * supports 64 signals, but this code is a long way from
74567588Sdes	 * running on anything but i386, so ignore that for now.
74667588Sdes	 */
74771471Sjhb	PROC_LOCK(p);
748104306Sjmallett	sbuf_printf(sb, "SigPnd:\t%08x\n",	p->p_siglist.__bits[0]);
74969799Sdes	/*
75069799Sdes	 * I can't seem to find out where the signal mask is in
75169799Sdes	 * relation to struct proc, so SigBlk is left unimplemented.
75269799Sdes	 */
75378025Sdes	sbuf_printf(sb, "SigBlk:\t%08x\n",	0); /* XXX */
754114983Sjhb	ps = p->p_sigacts;
755114983Sjhb	mtx_lock(&ps->ps_mtx);
756114983Sjhb	sbuf_printf(sb, "SigIgn:\t%08x\n",	ps->ps_sigignore.__bits[0]);
757114983Sjhb	sbuf_printf(sb, "SigCgt:\t%08x\n",	ps->ps_sigcatch.__bits[0]);
758114983Sjhb	mtx_unlock(&ps->ps_mtx);
75971471Sjhb	PROC_UNLOCK(p);
760119068Sdes
76167588Sdes	/*
76267588Sdes	 * Linux also prints the capability masks, but we don't have
76367588Sdes	 * capabilities yet, and when we do get them they're likely to
76467588Sdes	 * be meaningless to Linux programs, so we lie. XXX
76567588Sdes	 */
76678025Sdes	sbuf_printf(sb, "CapInh:\t%016x\n",	0);
76778025Sdes	sbuf_printf(sb, "CapPrm:\t%016x\n",	0);
76878025Sdes	sbuf_printf(sb, "CapEff:\t%016x\n",	0);
769119068Sdes
77078025Sdes	return (0);
77167588Sdes}
77274135Sjlemon
773119911Sdes
77478113Sdes/*
775119911Sdes * Filler function for proc/pid/cwd
776119911Sdes */
777119911Sdesstatic int
778119911Sdeslinprocfs_doproccwd(PFS_FILL_ARGS)
779119911Sdes{
780119911Sdes	char *fullpath = "unknown";
781119911Sdes	char *freepath = NULL;
782119911Sdes
783119911Sdes	vn_fullpath(td, p->p_fd->fd_cdir, &fullpath, &freepath);
784119911Sdes	sbuf_printf(sb, "%s", fullpath);
785119911Sdes	if (freepath)
786119911Sdes		free(freepath, M_TEMP);
787119911Sdes	return (0);
788119911Sdes}
789119911Sdes
790119911Sdes/*
791119911Sdes * Filler function for proc/pid/root
792119911Sdes */
793119911Sdesstatic int
794119911Sdeslinprocfs_doprocroot(PFS_FILL_ARGS)
795119911Sdes{
796119911Sdes	struct vnode *rvp;
797119911Sdes	char *fullpath = "unknown";
798119911Sdes	char *freepath = NULL;
799119911Sdes
800119911Sdes	rvp = jailed(p->p_ucred) ? p->p_fd->fd_jdir : p->p_fd->fd_rdir;
801119911Sdes	vn_fullpath(td, rvp, &fullpath, &freepath);
802119911Sdes	sbuf_printf(sb, "%s", fullpath);
803119911Sdes	if (freepath)
804119911Sdes		free(freepath, M_TEMP);
805119911Sdes	return (0);
806119911Sdes}
807119911Sdes
808119911Sdes/*
80978113Sdes * Filler function for proc/pid/cmdline
81078113Sdes */
81178025Sdesstatic int
81278113Sdeslinprocfs_doproccmdline(PFS_FILL_ARGS)
81378113Sdes{
81478113Sdes	struct ps_strings pstr;
815138281Scperciva	char **ps_argvstr;
81678113Sdes	int error, i;
81778113Sdes
81878113Sdes	/*
81978113Sdes	 * If we are using the ps/cmdline caching, use that.  Otherwise
82078113Sdes	 * revert back to the old way which only implements full cmdline
82178113Sdes	 * for the currept process and just p->p_comm for all other
82278113Sdes	 * processes.
82378113Sdes	 * Note that if the argv is no longer available, we deliberately
82478113Sdes	 * don't fall back on p->p_comm or return an error: the authentic
82578113Sdes	 * Linux behaviour is to return zero-length in this case.
82678113Sdes	 */
82778113Sdes
82894620Sjhb	PROC_LOCK(p);
829127694Spjd	if (p->p_args && p_cansee(td, p) == 0) {
83094620Sjhb		sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length);
83194620Sjhb		PROC_UNLOCK(p);
83294620Sjhb	} else if (p != td->td_proc) {
83394620Sjhb		PROC_UNLOCK(p);
83494620Sjhb		sbuf_printf(sb, "%.*s", MAXCOMLEN, p->p_comm);
83594620Sjhb	} else {
83694620Sjhb		PROC_UNLOCK(p);
837103767Sjake		error = copyin((void *)p->p_sysent->sv_psstrings, &pstr,
838103767Sjake		    sizeof(pstr));
83994620Sjhb		if (error)
84094620Sjhb			return (error);
841138281Scperciva		if (pstr.ps_nargvstr > ARG_MAX)
842138281Scperciva			return (E2BIG);
843138281Scperciva		ps_argvstr = malloc(pstr.ps_nargvstr * sizeof(char *),
844138281Scperciva		    M_TEMP, M_WAITOK);
845138281Scperciva		error = copyin((void *)pstr.ps_argvstr, ps_argvstr,
846138281Scperciva		    pstr.ps_nargvstr * sizeof(char *));
847138281Scperciva		if (error) {
848138281Scperciva			free(ps_argvstr, M_TEMP);
849138281Scperciva			return (error);
850138281Scperciva		}
85194620Sjhb		for (i = 0; i < pstr.ps_nargvstr; i++) {
852138281Scperciva			sbuf_copyin(sb, ps_argvstr[i], 0);
85394620Sjhb			sbuf_printf(sb, "%c", '\0');
85478113Sdes		}
855138281Scperciva		free(ps_argvstr, M_TEMP);
85678113Sdes	}
85778113Sdes
85878113Sdes	return (0);
85978113Sdes}
86078113Sdes
86178113Sdes/*
862116173Sobrien * Filler function for proc/pid/environ
863116173Sobrien */
864116173Sobrienstatic int
865116173Sobrienlinprocfs_doprocenviron(PFS_FILL_ARGS)
866116173Sobrien{
867168762Sdes
868116173Sobrien	sbuf_printf(sb, "doprocenviron\n%c", '\0');
869116173Sobrien	return (0);
870116173Sobrien}
871116173Sobrien
872116173Sobrien/*
873116173Sobrien * Filler function for proc/pid/maps
874116173Sobrien */
875116173Sobrienstatic int
876116173Sobrienlinprocfs_doprocmaps(PFS_FILL_ARGS)
877116173Sobrien{
878185984Skib	struct vmspace *vm;
879185984Skib	vm_map_t map;
880185765Skib	vm_map_entry_t entry, tmp_entry;
881121265Scognet	vm_object_t obj, tobj, lobj;
882185765Skib	vm_offset_t e_start, e_end;
883121265Scognet	vm_ooffset_t off = 0;
884185765Skib	vm_prot_t e_prot;
885185765Skib	unsigned int last_timestamp;
886121265Scognet	char *name = "", *freename = NULL;
887121265Scognet	ino_t ino;
888121265Scognet	int ref_count, shadow_count, flags;
889121265Scognet	int error;
890137507Sphk	struct vnode *vp;
891137507Sphk	struct vattr vat;
892161094Skib	int locked;
893168762Sdes
894121246Scognet	PROC_LOCK(p);
895121246Scognet	error = p_candebug(td, p);
896121246Scognet	PROC_UNLOCK(p);
897121246Scognet	if (error)
898121246Scognet		return (error);
899168762Sdes
900121246Scognet	if (uio->uio_rw != UIO_READ)
901121246Scognet		return (EOPNOTSUPP);
902168762Sdes
903121246Scognet	error = 0;
904185984Skib	vm = vmspace_acquire_ref(p);
905185984Skib	if (vm == NULL)
906185984Skib		return (ESRCH);
907185984Skib	map = &vm->vm_map;
908169156Salc	vm_map_lock_read(map);
909183600Skib	for (entry = map->header.next; entry != &map->header;
910121246Scognet	    entry = entry->next) {
911121265Scognet		name = "";
912121265Scognet		freename = NULL;
913121246Scognet		if (entry->eflags & MAP_ENTRY_IS_SUB_MAP)
914121246Scognet			continue;
915185765Skib		e_prot = entry->protection;
916185765Skib		e_start = entry->start;
917185765Skib		e_end = entry->end;
918121246Scognet		obj = entry->object.vm_object;
919169156Salc		for (lobj = tobj = obj; tobj; tobj = tobj->backing_object) {
920169156Salc			VM_OBJECT_LOCK(tobj);
921169156Salc			if (lobj != obj)
922169156Salc				VM_OBJECT_UNLOCK(lobj);
923121246Scognet			lobj = tobj;
924169156Salc		}
925185765Skib		last_timestamp = map->timestamp;
926185765Skib		vm_map_unlock_read(map);
927121246Scognet		ino = 0;
928121246Scognet		if (lobj) {
929121246Scognet			off = IDX_TO_OFF(lobj->size);
930161094Skib			if (lobj->type == OBJT_VNODE) {
931161094Skib				vp = lobj->handle;
932161094Skib				if (vp)
933161094Skib					vref(vp);
934121246Scognet			}
935161094Skib			else
936161094Skib				vp = NULL;
937169156Salc			if (lobj != obj)
938169156Salc				VM_OBJECT_UNLOCK(lobj);
939121246Scognet			flags = obj->flags;
940121246Scognet			ref_count = obj->ref_count;
941121246Scognet			shadow_count = obj->shadow_count;
942169156Salc			VM_OBJECT_UNLOCK(obj);
943161094Skib			if (vp) {
944161094Skib				vn_fullpath(td, vp, &name, &freename);
945161094Skib				locked = VFS_LOCK_GIANT(vp->v_mount);
946175202Sattilio				vn_lock(vp, LK_SHARED | LK_RETRY);
947182371Sattilio				VOP_GETATTR(vp, &vat, td->td_ucred);
948161094Skib				ino = vat.va_fileid;
949161094Skib				vput(vp);
950161094Skib				VFS_UNLOCK_GIANT(locked);
951161094Skib			}
952121246Scognet		} else {
953121246Scognet			flags = 0;
954121246Scognet			ref_count = 0;
955121246Scognet			shadow_count = 0;
956121246Scognet		}
957168762Sdes
958121246Scognet		/*
959168762Sdes		 * format:
960121246Scognet		 *  start, end, access, offset, major, minor, inode, name.
961121246Scognet		 */
962183600Skib		error = sbuf_printf(sb,
963121246Scognet		    "%08lx-%08lx %s%s%s%s %08lx %02x:%02x %lu%s%s\n",
964185765Skib		    (u_long)e_start, (u_long)e_end,
965185765Skib		    (e_prot & VM_PROT_READ)?"r":"-",
966185765Skib		    (e_prot & VM_PROT_WRITE)?"w":"-",
967185765Skib		    (e_prot & VM_PROT_EXECUTE)?"x":"-",
968121246Scognet		    "p",
969121265Scognet		    (u_long)off,
970121246Scognet		    0,
971121246Scognet		    0,
972121265Scognet		    (u_long)ino,
973121246Scognet		    *name ? "     " : "",
974121246Scognet		    name
975121246Scognet		    );
976121246Scognet		if (freename)
977121246Scognet			free(freename, M_TEMP);
978185864Skib		vm_map_lock_read(map);
979183600Skib		if (error == -1) {
980183600Skib			error = 0;
981121246Scognet			break;
982169156Salc		}
983186563Skib		if (last_timestamp != map->timestamp) {
984185765Skib			/*
985185765Skib			 * Look again for the entry because the map was
986185765Skib			 * modified while it was unlocked.  Specifically,
987185765Skib			 * the entry may have been clipped, merged, or deleted.
988185765Skib			 */
989185765Skib			vm_map_lookup_entry(map, e_end - 1, &tmp_entry);
990185765Skib			entry = tmp_entry;
991185765Skib		}
992121246Scognet	}
993169156Salc	vm_map_unlock_read(map);
994185984Skib	vmspace_free(vm);
995168762Sdes
996121246Scognet	return (error);
997168762Sdes}
998168762Sdes
999116173Sobrien/*
100078113Sdes * Filler function for proc/net/dev
100178113Sdes */
100278025Sdesstatic int
100378025Sdeslinprocfs_donetdev(PFS_FILL_ARGS)
100474135Sjlemon{
1005183550Szec	INIT_VNET_NET(TD_TO_VNET(curthread));
100685129Sdes	char ifname[16]; /* XXX LINUX_IFNAMSIZ */
100774135Sjlemon	struct ifnet *ifp;
100874135Sjlemon
100985129Sdes	sbuf_printf(sb, "%6s|%58s|%s\n%6s|%58s|%58s\n",
101083926Sdes	    "Inter-", "   Receive", "  Transmit", " face",
101185129Sdes	    "bytes    packets errs drop fifo frame compressed",
101283926Sdes	    "bytes    packets errs drop fifo frame compressed");
101374135Sjlemon
1014108172Shsu	IFNET_RLOCK();
1015181803Sbz	TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
101685129Sdes		linux_ifname(ifp, ifname, sizeof ifname);
101785129Sdes			sbuf_printf(sb, "%6.6s:", ifname);
101883926Sdes		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu ",
101983926Sdes		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
102083926Sdes		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
102183926Sdes		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
102274135Sjlemon	}
1023108172Shsu	IFNET_RUNLOCK();
1024119068Sdes
102578025Sdes	return (0);
102674135Sjlemon}
102774135Sjlemon
1028158311Sambrisko/*
1029167159Sjkim * Filler function for proc/sys/kernel/osrelease
1030167159Sjkim */
1031167159Sjkimstatic int
1032167159Sjkimlinprocfs_doosrelease(PFS_FILL_ARGS)
1033167159Sjkim{
1034167159Sjkim	char osrelease[LINUX_MAX_UTSNAME];
1035167159Sjkim
1036167159Sjkim	linux_get_osrelease(td, osrelease);
1037167159Sjkim	sbuf_printf(sb, "%s\n", osrelease);
1038167159Sjkim
1039167159Sjkim	return (0);
1040167159Sjkim}
1041167159Sjkim
1042167159Sjkim/*
1043167159Sjkim * Filler function for proc/sys/kernel/ostype
1044167159Sjkim */
1045167159Sjkimstatic int
1046167159Sjkimlinprocfs_doostype(PFS_FILL_ARGS)
1047167159Sjkim{
1048167159Sjkim	char osname[LINUX_MAX_UTSNAME];
1049167159Sjkim
1050167159Sjkim	linux_get_osname(td, osname);
1051167159Sjkim	sbuf_printf(sb, "%s\n", osname);
1052167159Sjkim
1053167159Sjkim	return (0);
1054167159Sjkim}
1055167159Sjkim
1056167159Sjkim/*
1057167159Sjkim * Filler function for proc/sys/kernel/version
1058167159Sjkim */
1059167159Sjkimstatic int
1060167159Sjkimlinprocfs_doosbuild(PFS_FILL_ARGS)
1061167159Sjkim{
1062168762Sdes
1063167159Sjkim	linprocfs_osbuild(td, sb);
1064167159Sjkim	sbuf_cat(sb, "\n");
1065167159Sjkim	return (0);
1066167159Sjkim}
1067167159Sjkim
1068167159Sjkim/*
1069164692Sjkim * Filler function for proc/sys/kernel/msgmni
1070164692Sjkim */
1071164692Sjkimstatic int
1072164692Sjkimlinprocfs_domsgmni(PFS_FILL_ARGS)
1073164692Sjkim{
1074164692Sjkim
1075168067Sjkim	sbuf_printf(sb, "%d\n", msginfo.msgmni);
1076164692Sjkim	return (0);
1077164692Sjkim}
1078164692Sjkim
1079164692Sjkim/*
1080163251Skeramida * Filler function for proc/sys/kernel/pid_max
1081163129Snetchild */
1082163129Snetchildstatic int
1083163129Snetchildlinprocfs_dopid_max(PFS_FILL_ARGS)
1084163129Snetchild{
1085163757Snetchild
1086163129Snetchild	sbuf_printf(sb, "%i\n", PID_MAX);
1087163129Snetchild	return (0);
1088163129Snetchild}
1089163129Snetchild
1090163129Snetchild/*
1091164692Sjkim * Filler function for proc/sys/kernel/sem
1092164692Sjkim */
1093164692Sjkimstatic int
1094164692Sjkimlinprocfs_dosem(PFS_FILL_ARGS)
1095164692Sjkim{
1096164692Sjkim
1097168067Sjkim	sbuf_printf(sb, "%d %d %d %d\n", seminfo.semmsl, seminfo.semmns,
1098168067Sjkim	    seminfo.semopm, seminfo.semmni);
1099164692Sjkim	return (0);
1100164692Sjkim}
1101164692Sjkim
1102164692Sjkim/*
1103158311Sambrisko * Filler function for proc/scsi/device_info
1104158311Sambrisko */
1105158311Sambriskostatic int
1106158311Sambriskolinprocfs_doscsidevinfo(PFS_FILL_ARGS)
1107158311Sambrisko{
1108168762Sdes
1109158311Sambrisko	return (0);
1110158311Sambrisko}
1111158311Sambrisko
1112158311Sambrisko/*
1113158311Sambrisko * Filler function for proc/scsi/scsi
1114158311Sambrisko */
1115158311Sambriskostatic int
1116158311Sambriskolinprocfs_doscsiscsi(PFS_FILL_ARGS)
1117158311Sambrisko{
1118168762Sdes
1119158311Sambrisko	return (0);
1120158311Sambrisko}
1121158311Sambrisko
112285538Sphkextern struct cdevsw *cdevsw[];
112385538Sphk
112478113Sdes/*
112578113Sdes * Filler function for proc/devices
112678113Sdes */
112778025Sdesstatic int
112878025Sdeslinprocfs_dodevices(PFS_FILL_ARGS)
112974135Sjlemon{
1130158311Sambrisko	char *char_devices;
113178025Sdes	sbuf_printf(sb, "Character devices:\n");
113274135Sjlemon
1133158311Sambrisko	char_devices = linux_get_char_devices();
1134158311Sambrisko	sbuf_printf(sb, "%s", char_devices);
1135158311Sambrisko	linux_free_get_char_devices(char_devices);
113674135Sjlemon
113778025Sdes	sbuf_printf(sb, "\nBlock devices:\n");
1138119068Sdes
113978025Sdes	return (0);
114074135Sjlemon}
114174135Sjlemon
114278113Sdes/*
114378113Sdes * Filler function for proc/cmdline
114478113Sdes */
114578025Sdesstatic int
114678025Sdeslinprocfs_docmdline(PFS_FILL_ARGS)
114774135Sjlemon{
1148168762Sdes
114978025Sdes	sbuf_printf(sb, "BOOT_IMAGE=%s", kernelname);
115078025Sdes	sbuf_printf(sb, " ro root=302\n");
115178025Sdes	return (0);
115278025Sdes}
115374135Sjlemon
115483926Sdes#if 0
115578025Sdes/*
115683926Sdes * Filler function for proc/modules
115783926Sdes */
115883926Sdesstatic int
115983926Sdeslinprocfs_domodules(PFS_FILL_ARGS)
116083926Sdes{
116183926Sdes	struct linker_file *lf;
1162119068Sdes
116383926Sdes	TAILQ_FOREACH(lf, &linker_files, link) {
116483926Sdes		sbuf_printf(sb, "%-20s%8lu%4d\n", lf->filename,
116583926Sdes		    (unsigned long)lf->size, lf->refs);
116683926Sdes	}
116783926Sdes	return (0);
116883926Sdes}
116983926Sdes#endif
117083926Sdes
117183926Sdes/*
117285129Sdes * Constructor
117378025Sdes */
117485129Sdesstatic int
117585129Sdeslinprocfs_init(PFS_INIT_ARGS)
117685129Sdes{
117785129Sdes	struct pfs_node *root;
117885129Sdes	struct pfs_node *dir;
117974135Sjlemon
118085129Sdes	root = pi->pi_root;
118178025Sdes
1182119923Sdes	/* /proc/... */
1183119911Sdes	pfs_create_file(root, "cmdline", &linprocfs_docmdline,
1184167482Sdes	    NULL, NULL, NULL, PFS_RD);
1185119911Sdes	pfs_create_file(root, "cpuinfo", &linprocfs_docpuinfo,
1186167482Sdes	    NULL, NULL, NULL, PFS_RD);
1187119911Sdes	pfs_create_file(root, "devices", &linprocfs_dodevices,
1188167482Sdes	    NULL, NULL, NULL, PFS_RD);
1189119911Sdes	pfs_create_file(root, "loadavg", &linprocfs_doloadavg,
1190167482Sdes	    NULL, NULL, NULL, PFS_RD);
1191119911Sdes	pfs_create_file(root, "meminfo", &linprocfs_domeminfo,
1192167482Sdes	    NULL, NULL, NULL, PFS_RD);
119383926Sdes#if 0
1194119911Sdes	pfs_create_file(root, "modules", &linprocfs_domodules,
1195167482Sdes	    NULL, NULL, NULL, PFS_RD);
119683926Sdes#endif
1197158311Sambrisko	pfs_create_file(root, "mounts", &linprocfs_domtab,
1198167482Sdes	    NULL, NULL, NULL, PFS_RD);
1199119911Sdes	pfs_create_file(root, "mtab", &linprocfs_domtab,
1200167482Sdes	    NULL, NULL, NULL, PFS_RD);
120187543Sdes	pfs_create_link(root, "self", &procfs_docurproc,
1202167482Sdes	    NULL, NULL, NULL, 0);
1203119911Sdes	pfs_create_file(root, "stat", &linprocfs_dostat,
1204167482Sdes	    NULL, NULL, NULL, PFS_RD);
1205119911Sdes	pfs_create_file(root, "uptime", &linprocfs_douptime,
1206167482Sdes	    NULL, NULL, NULL, PFS_RD);
1207119911Sdes	pfs_create_file(root, "version", &linprocfs_doversion,
1208167482Sdes	    NULL, NULL, NULL, PFS_RD);
120978025Sdes
1210119923Sdes	/* /proc/net/... */
1211167482Sdes	dir = pfs_create_dir(root, "net", NULL, NULL, NULL, 0);
121285129Sdes	pfs_create_file(dir, "dev", &linprocfs_donetdev,
1213167482Sdes	    NULL, NULL, NULL, PFS_RD);
121478025Sdes
1215119923Sdes	/* /proc/<pid>/... */
1216167482Sdes	dir = pfs_create_dir(root, "pid", NULL, NULL, NULL, PFS_PROCDEP);
121785129Sdes	pfs_create_file(dir, "cmdline", &linprocfs_doproccmdline,
1218167482Sdes	    NULL, NULL, NULL, PFS_RD);
1219119911Sdes	pfs_create_link(dir, "cwd", &linprocfs_doproccwd,
1220167482Sdes	    NULL, NULL, NULL, 0);
1221116173Sobrien	pfs_create_file(dir, "environ", &linprocfs_doprocenviron,
1222167482Sdes	    NULL, NULL, NULL, PFS_RD);
122387543Sdes	pfs_create_link(dir, "exe", &procfs_doprocfile,
1224167482Sdes	    NULL, &procfs_notsystem, NULL, 0);
1225116173Sobrien	pfs_create_file(dir, "maps", &linprocfs_doprocmaps,
1226167482Sdes	    NULL, NULL, NULL, PFS_RD);
122785129Sdes	pfs_create_file(dir, "mem", &procfs_doprocmem,
1228167482Sdes	    &procfs_attr, &procfs_candebug, NULL, PFS_RDWR|PFS_RAW);
1229119911Sdes	pfs_create_link(dir, "root", &linprocfs_doprocroot,
1230167482Sdes	    NULL, NULL, NULL, 0);
123185129Sdes	pfs_create_file(dir, "stat", &linprocfs_doprocstat,
1232167482Sdes	    NULL, NULL, NULL, PFS_RD);
1233119911Sdes	pfs_create_file(dir, "statm", &linprocfs_doprocstatm,
1234167482Sdes	    NULL, NULL, NULL, PFS_RD);
123585129Sdes	pfs_create_file(dir, "status", &linprocfs_doprocstatus,
1236167482Sdes	    NULL, NULL, NULL, PFS_RD);
123785129Sdes
1238158311Sambrisko	/* /proc/scsi/... */
1239167482Sdes	dir = pfs_create_dir(root, "scsi", NULL, NULL, NULL, 0);
1240158311Sambrisko	pfs_create_file(dir, "device_info", &linprocfs_doscsidevinfo,
1241167482Sdes	    NULL, NULL, NULL, PFS_RD);
1242158311Sambrisko	pfs_create_file(dir, "scsi", &linprocfs_doscsiscsi,
1243167482Sdes	    NULL, NULL, NULL, PFS_RD);
1244163129Snetchild
1245163129Snetchild	/* /proc/sys/... */
1246167482Sdes	dir = pfs_create_dir(root, "sys", NULL, NULL, NULL, 0);
1247163129Snetchild	/* /proc/sys/kernel/... */
1248167482Sdes	dir = pfs_create_dir(dir, "kernel", NULL, NULL, NULL, 0);
1249167159Sjkim	pfs_create_file(dir, "osrelease", &linprocfs_doosrelease,
1250167482Sdes	    NULL, NULL, NULL, PFS_RD);
1251167159Sjkim	pfs_create_file(dir, "ostype", &linprocfs_doostype,
1252167482Sdes	    NULL, NULL, NULL, PFS_RD);
1253167159Sjkim	pfs_create_file(dir, "version", &linprocfs_doosbuild,
1254167482Sdes	    NULL, NULL, NULL, PFS_RD);
1255164692Sjkim	pfs_create_file(dir, "msgmni", &linprocfs_domsgmni,
1256167482Sdes	    NULL, NULL, NULL, PFS_RD);
1257163129Snetchild	pfs_create_file(dir, "pid_max", &linprocfs_dopid_max,
1258167482Sdes	    NULL, NULL, NULL, PFS_RD);
1259164692Sjkim	pfs_create_file(dir, "sem", &linprocfs_dosem,
1260167482Sdes	    NULL, NULL, NULL, PFS_RD);
1261163129Snetchild
126285129Sdes	return (0);
126385129Sdes}
126485129Sdes
126585129Sdes/*
126685129Sdes * Destructor
126785129Sdes */
126885129Sdesstatic int
126985129Sdeslinprocfs_uninit(PFS_INIT_ARGS)
127085129Sdes{
127185129Sdes
127285129Sdes	/* nothing to do, pseudofs will GC */
127385129Sdes	return (0);
127485129Sdes}
127585129Sdes
127685129SdesPSEUDOFS(linprocfs, 1);
127778025SdesMODULE_DEPEND(linprocfs, linux, 1, 1, 1);
127878025SdesMODULE_DEPEND(linprocfs, procfs, 1, 1, 1);
1279168440SjkimMODULE_DEPEND(linprocfs, sysvmsg, 1, 1, 1);
1280168440SjkimMODULE_DEPEND(linprocfs, sysvsem, 1, 1, 1);
1281