linprocfs.c revision 153310
1/*-
2 * Copyright (c) 2000 Dag-Erling Co�dan Sm�rgrav
3 * Copyright (c) 1999 Pierre Beyssac
4 * Copyright (c) 1993 Jan-Simon Pendry
5 * Copyright (c) 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *	This product includes software developed by the University of
22 *	California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 *    may be used to endorse or promote products derived from this software
25 *    without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 *	@(#)procfs_status.c	8.4 (Berkeley) 6/15/94
40 */
41
42#include <sys/cdefs.h>
43__FBSDID("$FreeBSD: head/sys/compat/linprocfs/linprocfs.c 153310 2005-12-11 21:37:42Z mlaier $");
44
45#include <sys/param.h>
46#include <sys/queue.h>
47#include <sys/blist.h>
48#include <sys/conf.h>
49#include <sys/exec.h>
50#include <sys/filedesc.h>
51#include <sys/jail.h>
52#include <sys/kernel.h>
53#include <sys/linker.h>
54#include <sys/lock.h>
55#include <sys/malloc.h>
56#include <sys/mount.h>
57#include <sys/mutex.h>
58#include <sys/namei.h>
59#include <sys/proc.h>
60#include <sys/resourcevar.h>
61#include <sys/sbuf.h>
62#include <sys/smp.h>
63#include <sys/socket.h>
64#include <sys/sysctl.h>
65#include <sys/systm.h>
66#include <sys/tty.h>
67#include <sys/user.h>
68#include <sys/vmmeter.h>
69#include <sys/vnode.h>
70
71#include <net/if.h>
72
73#include <vm/vm.h>
74#include <vm/pmap.h>
75#include <vm/vm_map.h>
76#include <vm/vm_param.h>
77#include <vm/vm_object.h>
78#include <vm/swap_pager.h>
79
80#include <machine/clock.h>
81
82#ifdef __alpha__
83#include <machine/alpha_cpu.h>
84#include <machine/cpuconf.h>
85#include <machine/rpb.h>
86extern int ncpus;
87#endif /* __alpha__ */
88
89#if defined(__i386__) || defined(__amd64__)
90#include <machine/cputypes.h>
91#include <machine/md_var.h>
92#endif /* __i386__ || __amd64__ */
93
94#include "opt_compat.h"
95#ifdef COMPAT_LINUX32				/* XXX */
96#include <machine/../linux32/linux.h>
97#else
98#include <machine/../linux/linux.h>
99#endif
100#include <compat/linux/linux_ioctl.h>
101#include <compat/linux/linux_mib.h>
102#include <compat/linux/linux_util.h>
103#include <fs/pseudofs/pseudofs.h>
104#include <fs/procfs/procfs.h>
105
106/*
107 * Various conversion macros
108 */
109#define T2J(x) (((x) * 100UL) / (stathz ? stathz : hz))	/* ticks to jiffies */
110#define T2S(x) ((x) / (stathz ? stathz : hz))		/* ticks to seconds */
111#define B2K(x) ((x) >> 10)				/* bytes to kbytes */
112#define B2P(x) ((x) >> PAGE_SHIFT)			/* bytes to pages */
113#define P2B(x) ((x) << PAGE_SHIFT)			/* pages to bytes */
114#define P2K(x) ((x) << (PAGE_SHIFT - 10))		/* pages to kbytes */
115
116/*
117 * Filler function for proc/meminfo
118 */
119static int
120linprocfs_domeminfo(PFS_FILL_ARGS)
121{
122	unsigned long memtotal;		/* total memory in bytes */
123	unsigned long memused;		/* used memory in bytes */
124	unsigned long memfree;		/* free memory in bytes */
125	unsigned long memshared;	/* shared memory ??? */
126	unsigned long buffers, cached;	/* buffer / cache memory ??? */
127	unsigned long long swaptotal;	/* total swap space in bytes */
128	unsigned long long swapused;	/* used swap space in bytes */
129	unsigned long long swapfree;	/* free swap space in bytes */
130	vm_object_t object;
131	int i, j;
132
133	memtotal = physmem * PAGE_SIZE;
134	/*
135	 * The correct thing here would be:
136	 *
137	memfree = cnt.v_free_count * PAGE_SIZE;
138	memused = memtotal - memfree;
139	 *
140	 * but it might mislead linux binaries into thinking there
141	 * is very little memory left, so we cheat and tell them that
142	 * all memory that isn't wired down is free.
143	 */
144	memused = cnt.v_wire_count * PAGE_SIZE;
145	memfree = memtotal - memused;
146	swap_pager_status(&i, &j);
147	swaptotal = (unsigned long long)i * PAGE_SIZE;
148	swapused = (unsigned long long)j * PAGE_SIZE;
149	swapfree = swaptotal - swapused;
150	memshared = 0;
151	mtx_lock(&vm_object_list_mtx);
152	TAILQ_FOREACH(object, &vm_object_list, object_list)
153		if (object->shadow_count > 1)
154			memshared += object->resident_page_count;
155	mtx_unlock(&vm_object_list_mtx);
156	memshared *= PAGE_SIZE;
157	/*
158	 * We'd love to be able to write:
159	 *
160	buffers = bufspace;
161	 *
162	 * but bufspace is internal to vfs_bio.c and we don't feel
163	 * like unstaticizing it just for linprocfs's sake.
164	 */
165	buffers = 0;
166	cached = cnt.v_cache_count * PAGE_SIZE;
167
168	sbuf_printf(sb,
169	    "	     total:    used:	free:  shared: buffers:	 cached:\n"
170	    "Mem:  %lu %lu %lu %lu %lu %lu\n"
171	    "Swap: %llu %llu %llu\n"
172	    "MemTotal: %9lu kB\n"
173	    "MemFree:  %9lu kB\n"
174	    "MemShared:%9lu kB\n"
175	    "Buffers:  %9lu kB\n"
176	    "Cached:   %9lu kB\n"
177	    "SwapTotal:%9llu kB\n"
178	    "SwapFree: %9llu kB\n",
179	    memtotal, memused, memfree, memshared, buffers, cached,
180	    swaptotal, swapused, swapfree,
181	    B2K(memtotal), B2K(memfree),
182	    B2K(memshared), B2K(buffers), B2K(cached),
183	    B2K(swaptotal), B2K(swapfree));
184
185	return (0);
186}
187
188#ifdef __alpha__
189extern struct rpb *hwrpb;
190/*
191 * Filler function for proc/cpuinfo (Alpha version)
192 */
193static int
194linprocfs_docpuinfo(PFS_FILL_ARGS)
195{
196	u_int64_t type, major;
197	struct pcs *pcsp;
198	const char *model, *sysname;
199
200	static const char *cpuname[] = {
201		"EV3", "EV4", "Simulate", "LCA4", "EV5", "EV45", "EV56",
202		"EV6", "PCA56", "PCA57", "EV67", "EV68CB", "EV68AL"
203	};
204
205	pcsp = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id);
206	type = pcsp->pcs_proc_type;
207	major = (type & PCS_PROC_MAJOR) >> PCS_PROC_MAJORSHIFT;
208	if (major < sizeof(cpuname)/sizeof(char *)) {
209		model = cpuname[major - 1];
210	} else {
211		model = "unknown";
212	}
213
214	sysname = alpha_dsr_sysname();
215
216	sbuf_printf(sb,
217	    "cpu\t\t\t: Alpha\n"
218	    "cpu model\t\t: %s\n"
219	    "cpu variation\t\t: %ld\n"
220	    "cpu revision\t\t: %d\n"
221	    "cpu serial number\t: %s\n"
222	    "system type\t\t: %s\n"
223	    "system variation\t: %s\n"
224	    "system revision\t\t: %d\n"
225	    "system serial number\t: %s\n"
226	    "cycle frequency [Hz]\t: %lu\n"
227	    "timer frequency [Hz]\t: %u\n"
228	    "page size [bytes]\t: %ld\n"
229	    "phys. address bits\t: %ld\n"
230	    "max. addr. space #\t: %ld\n"
231	    "BogoMIPS\t\t: %u.%02u\n"
232	    "kernel unaligned acc\t: %d (pc=%x,va=%x)\n"
233	    "user unaligned acc\t: %d (pc=%x,va=%x)\n"
234	    "platform string\t\t: %s\n"
235	    "cpus detected\t\t: %d\n"
236	    ,
237	    model,
238	    pcsp->pcs_proc_var,
239	    *(int *)hwrpb->rpb_revision,
240	    " ",
241	    " ",
242	    "0",
243	    0,
244	    " ",
245	    hwrpb->rpb_cc_freq,
246	    hz,
247	    hwrpb->rpb_page_size,
248	    hwrpb->rpb_phys_addr_size,
249	    hwrpb->rpb_max_asn,
250	    0, 0,
251	    0, 0, 0,
252	    0, 0, 0,
253	    sysname,
254	    ncpus);
255	return (0);
256}
257#endif /* __alpha__ */
258
259#if defined(__i386__) || defined(__amd64__)
260/*
261 * Filler function for proc/cpuinfo (i386 & amd64 version)
262 */
263static int
264linprocfs_docpuinfo(PFS_FILL_ARGS)
265{
266	int class, fqmhz, fqkhz;
267	int i;
268
269	/*
270	 * We default the flags to include all non-conflicting flags,
271	 * and the Intel versions of conflicting flags.
272	 */
273	static char *flags[] = {
274		"fpu",	    "vme",     "de",	   "pse",      "tsc",
275		"msr",	    "pae",     "mce",	   "cx8",      "apic",
276		"sep",	    "sep",     "mtrr",	   "pge",      "mca",
277		"cmov",	    "pat",     "pse36",	   "pn",       "b19",
278		"b20",	    "b21",     "mmxext",   "mmx",      "fxsr",
279		"xmm",	    "b26",     "b27",	   "b28",      "b29",
280		"3dnowext", "3dnow"
281	};
282
283	switch (cpu_class) {
284#ifdef __i386__
285	case CPUCLASS_286:
286		class = 2;
287		break;
288	case CPUCLASS_386:
289		class = 3;
290		break;
291	case CPUCLASS_486:
292		class = 4;
293		break;
294	case CPUCLASS_586:
295		class = 5;
296		break;
297	case CPUCLASS_686:
298		class = 6;
299		break;
300	default:
301		class = 0;
302		break;
303#else
304	default:
305		class = 6;
306		break;
307#endif
308	}
309
310	for (i = 0; i < mp_ncpus; ++i) {
311		sbuf_printf(sb,
312		    "processor\t: %d\n"
313		    "vendor_id\t: %.20s\n"
314		    "cpu family\t: %d\n"
315		    "model\t\t: %d\n"
316		    "stepping\t: %d\n",
317		    i, cpu_vendor, class, cpu, cpu_id & 0xf);
318		/* XXX per-cpu vendor / class / id? */
319	}
320
321	sbuf_cat(sb,
322	    "flags\t\t:");
323
324	if (!strcmp(cpu_vendor, "AuthenticAMD") && (class < 6)) {
325		flags[16] = "fcmov";
326	} else if (!strcmp(cpu_vendor, "CyrixInstead")) {
327		flags[24] = "cxmmx";
328	}
329
330	for (i = 0; i < 32; i++)
331		if (cpu_feature & (1 << i))
332			sbuf_printf(sb, " %s", flags[i]);
333	sbuf_cat(sb, "\n");
334	if (class >= 5) {
335		fqmhz = (tsc_freq + 4999) / 1000000;
336		fqkhz = ((tsc_freq + 4999) / 10000) % 100;
337		sbuf_printf(sb,
338		    "cpu MHz\t\t: %d.%02d\n"
339		    "bogomips\t: %d.%02d\n",
340		    fqmhz, fqkhz, fqmhz, fqkhz);
341	}
342
343	return (0);
344}
345#endif /* __i386__ || __amd64__ */
346
347/*
348 * Filler function for proc/mtab
349 *
350 * This file doesn't exist in Linux' procfs, but is included here so
351 * users can symlink /compat/linux/etc/mtab to /proc/mtab
352 */
353static int
354linprocfs_domtab(PFS_FILL_ARGS)
355{
356	struct nameidata nd;
357	struct mount *mp;
358	const char *lep;
359	char *dlep, *flep, *mntto, *mntfrom, *fstype;
360	size_t lep_len;
361	int error;
362
363	/* resolve symlinks etc. in the emulation tree prefix */
364	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, linux_emul_path, td);
365	flep = NULL;
366	if (namei(&nd) != 0 || vn_fullpath(td, nd.ni_vp, &dlep, &flep) != 0)
367		lep = linux_emul_path;
368	else
369		lep = dlep;
370	lep_len = strlen(lep);
371
372	mtx_lock(&mountlist_mtx);
373	error = 0;
374	TAILQ_FOREACH(mp, &mountlist, mnt_list) {
375		/* determine device name */
376		mntfrom = mp->mnt_stat.f_mntfromname;
377
378		/* determine mount point */
379		mntto = mp->mnt_stat.f_mntonname;
380		if (strncmp(mntto, lep, lep_len) == 0 &&
381		    mntto[lep_len] == '/')
382			mntto += lep_len;
383
384		/* determine fs type */
385		fstype = mp->mnt_stat.f_fstypename;
386		if (strcmp(fstype, pn->pn_info->pi_name) == 0)
387			mntfrom = fstype = "proc";
388		else if (strcmp(fstype, "procfs") == 0)
389			continue;
390
391		sbuf_printf(sb, "%s %s %s %s", mntfrom, mntto, fstype,
392		    mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw");
393#define ADD_OPTION(opt, name) \
394	if (mp->mnt_stat.f_flags & (opt)) sbuf_printf(sb, "," name);
395		ADD_OPTION(MNT_SYNCHRONOUS,	"sync");
396		ADD_OPTION(MNT_NOEXEC,		"noexec");
397		ADD_OPTION(MNT_NOSUID,		"nosuid");
398		ADD_OPTION(MNT_UNION,		"union");
399		ADD_OPTION(MNT_ASYNC,		"async");
400		ADD_OPTION(MNT_SUIDDIR,		"suiddir");
401		ADD_OPTION(MNT_NOSYMFOLLOW,	"nosymfollow");
402		ADD_OPTION(MNT_NOATIME,		"noatime");
403#undef ADD_OPTION
404		/* a real Linux mtab will also show NFS options */
405		sbuf_printf(sb, " 0 0\n");
406	}
407	mtx_unlock(&mountlist_mtx);
408	if (flep != NULL)
409		free(flep, M_TEMP);
410	return (error);
411}
412
413/*
414 * Filler function for proc/stat
415 */
416static int
417linprocfs_dostat(PFS_FILL_ARGS)
418{
419	int i;
420
421	sbuf_printf(sb, "cpu %ld %ld %ld %ld\n",
422	    T2J(cp_time[CP_USER]),
423	    T2J(cp_time[CP_NICE]),
424	    T2J(cp_time[CP_SYS] /*+ cp_time[CP_INTR]*/),
425	    T2J(cp_time[CP_IDLE]));
426	for (i = 0; i < mp_ncpus; ++i)
427		sbuf_printf(sb, "cpu%d %ld %ld %ld %ld\n", i,
428		    T2J(cp_time[CP_USER]) / mp_ncpus,
429		    T2J(cp_time[CP_NICE]) / mp_ncpus,
430		    T2J(cp_time[CP_SYS]) / mp_ncpus,
431		    T2J(cp_time[CP_IDLE]) / mp_ncpus);
432	sbuf_printf(sb,
433	    "disk 0 0 0 0\n"
434	    "page %u %u\n"
435	    "swap %u %u\n"
436	    "intr %u\n"
437	    "ctxt %u\n"
438	    "btime %lld\n",
439	    cnt.v_vnodepgsin,
440	    cnt.v_vnodepgsout,
441	    cnt.v_swappgsin,
442	    cnt.v_swappgsout,
443	    cnt.v_intr,
444	    cnt.v_swtch,
445	    (long long)boottime.tv_sec);
446	return (0);
447}
448
449/*
450 * Filler function for proc/uptime
451 */
452static int
453linprocfs_douptime(PFS_FILL_ARGS)
454{
455	struct timeval tv;
456
457	getmicrouptime(&tv);
458	sbuf_printf(sb, "%lld.%02ld %ld.%02ld\n",
459	    (long long)tv.tv_sec, tv.tv_usec / 10000,
460	    T2S(cp_time[CP_IDLE]), T2J(cp_time[CP_IDLE]) % 100);
461	return (0);
462}
463
464/*
465 * Filler function for proc/version
466 */
467static int
468linprocfs_doversion(PFS_FILL_ARGS)
469{
470	char osname[LINUX_MAX_UTSNAME];
471	char osrelease[LINUX_MAX_UTSNAME];
472
473	linux_get_osname(td, osname);
474	linux_get_osrelease(td, osrelease);
475
476	sbuf_printf(sb,
477	    "%s version %s (des@freebsd.org) (gcc version " __VERSION__ ")"
478	    " #4 Sun Dec 18 04:30:00 CET 1977\n", osname, osrelease);
479	return (0);
480}
481
482/*
483 * Filler function for proc/loadavg
484 */
485static int
486linprocfs_doloadavg(PFS_FILL_ARGS)
487{
488	sbuf_printf(sb,
489	    "%d.%02d %d.%02d %d.%02d %d/%d %d\n",
490	    (int)(averunnable.ldavg[0] / averunnable.fscale),
491	    (int)(averunnable.ldavg[0] * 100 / averunnable.fscale % 100),
492	    (int)(averunnable.ldavg[1] / averunnable.fscale),
493	    (int)(averunnable.ldavg[1] * 100 / averunnable.fscale % 100),
494	    (int)(averunnable.ldavg[2] / averunnable.fscale),
495	    (int)(averunnable.ldavg[2] * 100 / averunnable.fscale % 100),
496	    1,				/* number of running tasks */
497	    nprocs,			/* number of tasks */
498	    lastpid			/* the last pid */
499	);
500
501	return (0);
502}
503
504/*
505 * Filler function for proc/pid/stat
506 */
507static int
508linprocfs_doprocstat(PFS_FILL_ARGS)
509{
510	struct kinfo_proc kp;
511
512	PROC_LOCK(p);
513	fill_kinfo_proc(p, &kp);
514	sbuf_printf(sb, "%d", p->p_pid);
515#define PS_ADD(name, fmt, arg) sbuf_printf(sb, " " fmt, arg)
516	PS_ADD("comm",		"(%s)",	p->p_comm);
517	PS_ADD("statr",		"%c",	'0'); /* XXX */
518	PS_ADD("ppid",		"%d",	p->p_pptr ? p->p_pptr->p_pid : 0);
519	PS_ADD("pgrp",		"%d",	p->p_pgid);
520	PS_ADD("session",	"%d",	p->p_session->s_sid);
521	PROC_UNLOCK(p);
522	PS_ADD("tty",		"%d",	0); /* XXX */
523	PS_ADD("tpgid",		"%d",	0); /* XXX */
524	PS_ADD("flags",		"%u",	0); /* XXX */
525	PS_ADD("minflt",	"%u",	0); /* XXX */
526	PS_ADD("cminflt",	"%u",	0); /* XXX */
527	PS_ADD("majflt",	"%u",	0); /* XXX */
528	PS_ADD("cminflt",	"%u",	0); /* XXX */
529	PS_ADD("utime",		"%d",	0); /* XXX */
530	PS_ADD("stime",		"%d",	0); /* XXX */
531	PS_ADD("cutime",	"%d",	0); /* XXX */
532	PS_ADD("cstime",	"%d",	0); /* XXX */
533	PS_ADD("counter",	"%d",	0); /* XXX */
534	PS_ADD("priority",	"%d",	0); /* XXX */
535	PS_ADD("timeout",	"%u",	0); /* XXX */
536	PS_ADD("itrealvalue",	"%u",	0); /* XXX */
537	PS_ADD("starttime",	"%d",	0); /* XXX */
538	PS_ADD("vsize",		"%ju",	(uintmax_t)kp.ki_size);
539	PS_ADD("rss",		"%ju",	P2K((uintmax_t)kp.ki_rssize));
540	PS_ADD("rlim",		"%u",	0); /* XXX */
541	PS_ADD("startcode",	"%u",	(unsigned)0);
542	PS_ADD("endcode",	"%u",	0); /* XXX */
543	PS_ADD("startstack",	"%u",	0); /* XXX */
544	PS_ADD("esp",		"%u",	0); /* XXX */
545	PS_ADD("eip",		"%u",	0); /* XXX */
546	PS_ADD("signal",	"%d",	0); /* XXX */
547	PS_ADD("blocked",	"%d",	0); /* XXX */
548	PS_ADD("sigignore",	"%d",	0); /* XXX */
549	PS_ADD("sigcatch",	"%d",	0); /* XXX */
550	PS_ADD("wchan",		"%u",	0); /* XXX */
551	PS_ADD("nswap",		"%lu",	(long unsigned)0); /* XXX */
552	PS_ADD("cnswap",	"%lu",	(long unsigned)0); /* XXX */
553	PS_ADD("exitsignal",	"%d",	0); /* XXX */
554	PS_ADD("processor",	"%d",	0); /* XXX */
555#undef PS_ADD
556	sbuf_putc(sb, '\n');
557
558	return (0);
559}
560
561/*
562 * Filler function for proc/pid/statm
563 */
564static int
565linprocfs_doprocstatm(PFS_FILL_ARGS)
566{
567	struct kinfo_proc kp;
568	segsz_t lsize;
569
570	PROC_LOCK(p);
571	fill_kinfo_proc(p, &kp);
572	PROC_UNLOCK(p);
573
574	/*
575	 * See comments in linprocfs_doprocstatus() regarding the
576	 * computation of lsize.
577	 */
578	/* size resident share trs drs lrs dt */
579	sbuf_printf(sb, "%ju ", B2P((uintmax_t)kp.ki_size));
580	sbuf_printf(sb, "%ju ", (uintmax_t)kp.ki_rssize);
581	sbuf_printf(sb, "%ju ", (uintmax_t)0); /* XXX */
582	sbuf_printf(sb, "%ju ",	(uintmax_t)kp.ki_tsize);
583	sbuf_printf(sb, "%ju ", (uintmax_t)(kp.ki_dsize + kp.ki_ssize));
584	lsize = B2P(kp.ki_size) - kp.ki_dsize -
585	    kp.ki_ssize - kp.ki_tsize - 1;
586	sbuf_printf(sb, "%ju ", (uintmax_t)lsize);
587	sbuf_printf(sb, "%ju\n", (uintmax_t)0); /* XXX */
588
589	return (0);
590}
591
592/*
593 * Filler function for proc/pid/status
594 */
595static int
596linprocfs_doprocstatus(PFS_FILL_ARGS)
597{
598	struct kinfo_proc kp;
599	char *state;
600	segsz_t lsize;
601	struct thread *td2;
602	struct sigacts *ps;
603	int i;
604
605	PROC_LOCK(p);
606	td2 = FIRST_THREAD_IN_PROC(p); /* XXXKSE pretend only one thread */
607
608	if (P_SHOULDSTOP(p)) {
609		state = "T (stopped)";
610	} else {
611		mtx_lock_spin(&sched_lock);
612		switch(p->p_state) {
613		case PRS_NEW:
614			state = "I (idle)";
615			break;
616		case PRS_NORMAL:
617			if (p->p_flag & P_WEXIT) {
618				state = "X (exiting)";
619				break;
620			}
621			switch(td2->td_state) {
622			case TDS_INHIBITED:
623				state = "S (sleeping)";
624				break;
625			case TDS_RUNQ:
626			case TDS_RUNNING:
627				state = "R (running)";
628				break;
629			default:
630				state = "? (unknown)";
631				break;
632			}
633			break;
634		case PRS_ZOMBIE:
635			state = "Z (zombie)";
636			break;
637		default:
638			state = "? (unknown)";
639			break;
640		}
641		mtx_unlock_spin(&sched_lock);
642	}
643
644	fill_kinfo_proc(p, &kp);
645	sbuf_printf(sb, "Name:\t%s\n",		p->p_comm); /* XXX escape */
646	sbuf_printf(sb, "State:\t%s\n",		state);
647
648	/*
649	 * Credentials
650	 */
651	sbuf_printf(sb, "Pid:\t%d\n",		p->p_pid);
652	sbuf_printf(sb, "PPid:\t%d\n",		p->p_pptr ?
653						p->p_pptr->p_pid : 0);
654	sbuf_printf(sb, "Uid:\t%d %d %d %d\n",	p->p_ucred->cr_ruid,
655						p->p_ucred->cr_uid,
656						p->p_ucred->cr_svuid,
657						/* FreeBSD doesn't have fsuid */
658						p->p_ucred->cr_uid);
659	sbuf_printf(sb, "Gid:\t%d %d %d %d\n",	p->p_ucred->cr_rgid,
660						p->p_ucred->cr_gid,
661						p->p_ucred->cr_svgid,
662						/* FreeBSD doesn't have fsgid */
663						p->p_ucred->cr_gid);
664	sbuf_cat(sb, "Groups:\t");
665	for (i = 0; i < p->p_ucred->cr_ngroups; i++)
666		sbuf_printf(sb, "%d ",		p->p_ucred->cr_groups[i]);
667	PROC_UNLOCK(p);
668	sbuf_putc(sb, '\n');
669
670	/*
671	 * Memory
672	 *
673	 * While our approximation of VmLib may not be accurate (I
674	 * don't know of a simple way to verify it, and I'm not sure
675	 * it has much meaning anyway), I believe it's good enough.
676	 *
677	 * The same code that could (I think) accurately compute VmLib
678	 * could also compute VmLck, but I don't really care enough to
679	 * implement it. Submissions are welcome.
680	 */
681	sbuf_printf(sb, "VmSize:\t%8ju kB\n",	B2K((uintmax_t)kp.ki_size));
682	sbuf_printf(sb, "VmLck:\t%8u kB\n",	P2K(0)); /* XXX */
683	sbuf_printf(sb, "VmRss:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_rssize));
684	sbuf_printf(sb, "VmData:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_dsize));
685	sbuf_printf(sb, "VmStk:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_ssize));
686	sbuf_printf(sb, "VmExe:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_tsize));
687	lsize = B2P(kp.ki_size) - kp.ki_dsize -
688	    kp.ki_ssize - kp.ki_tsize - 1;
689	sbuf_printf(sb, "VmLib:\t%8ju kB\n",	P2K((uintmax_t)lsize));
690
691	/*
692	 * Signal masks
693	 *
694	 * We support up to 128 signals, while Linux supports 32,
695	 * but we only define 32 (the same 32 as Linux, to boot), so
696	 * just show the lower 32 bits of each mask. XXX hack.
697	 *
698	 * NB: on certain platforms (Sparc at least) Linux actually
699	 * supports 64 signals, but this code is a long way from
700	 * running on anything but i386, so ignore that for now.
701	 */
702	PROC_LOCK(p);
703	sbuf_printf(sb, "SigPnd:\t%08x\n",	p->p_siglist.__bits[0]);
704	/*
705	 * I can't seem to find out where the signal mask is in
706	 * relation to struct proc, so SigBlk is left unimplemented.
707	 */
708	sbuf_printf(sb, "SigBlk:\t%08x\n",	0); /* XXX */
709	ps = p->p_sigacts;
710	mtx_lock(&ps->ps_mtx);
711	sbuf_printf(sb, "SigIgn:\t%08x\n",	ps->ps_sigignore.__bits[0]);
712	sbuf_printf(sb, "SigCgt:\t%08x\n",	ps->ps_sigcatch.__bits[0]);
713	mtx_unlock(&ps->ps_mtx);
714	PROC_UNLOCK(p);
715
716	/*
717	 * Linux also prints the capability masks, but we don't have
718	 * capabilities yet, and when we do get them they're likely to
719	 * be meaningless to Linux programs, so we lie. XXX
720	 */
721	sbuf_printf(sb, "CapInh:\t%016x\n",	0);
722	sbuf_printf(sb, "CapPrm:\t%016x\n",	0);
723	sbuf_printf(sb, "CapEff:\t%016x\n",	0);
724
725	return (0);
726}
727
728
729/*
730 * Filler function for proc/pid/cwd
731 */
732static int
733linprocfs_doproccwd(PFS_FILL_ARGS)
734{
735	char *fullpath = "unknown";
736	char *freepath = NULL;
737
738	vn_fullpath(td, p->p_fd->fd_cdir, &fullpath, &freepath);
739	sbuf_printf(sb, "%s", fullpath);
740	if (freepath)
741		free(freepath, M_TEMP);
742	return (0);
743}
744
745/*
746 * Filler function for proc/pid/root
747 */
748static int
749linprocfs_doprocroot(PFS_FILL_ARGS)
750{
751	struct vnode *rvp;
752	char *fullpath = "unknown";
753	char *freepath = NULL;
754
755	rvp = jailed(p->p_ucred) ? p->p_fd->fd_jdir : p->p_fd->fd_rdir;
756	vn_fullpath(td, rvp, &fullpath, &freepath);
757	sbuf_printf(sb, "%s", fullpath);
758	if (freepath)
759		free(freepath, M_TEMP);
760	return (0);
761}
762
763/*
764 * Filler function for proc/pid/cmdline
765 */
766static int
767linprocfs_doproccmdline(PFS_FILL_ARGS)
768{
769	struct ps_strings pstr;
770	char **ps_argvstr;
771	int error, i;
772
773	/*
774	 * If we are using the ps/cmdline caching, use that.  Otherwise
775	 * revert back to the old way which only implements full cmdline
776	 * for the currept process and just p->p_comm for all other
777	 * processes.
778	 * Note that if the argv is no longer available, we deliberately
779	 * don't fall back on p->p_comm or return an error: the authentic
780	 * Linux behaviour is to return zero-length in this case.
781	 */
782
783	PROC_LOCK(p);
784	if (p->p_args && p_cansee(td, p) == 0) {
785		sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length);
786		PROC_UNLOCK(p);
787	} else if (p != td->td_proc) {
788		PROC_UNLOCK(p);
789		sbuf_printf(sb, "%.*s", MAXCOMLEN, p->p_comm);
790	} else {
791		PROC_UNLOCK(p);
792		error = copyin((void *)p->p_sysent->sv_psstrings, &pstr,
793		    sizeof(pstr));
794		if (error)
795			return (error);
796		if (pstr.ps_nargvstr > ARG_MAX)
797			return (E2BIG);
798		ps_argvstr = malloc(pstr.ps_nargvstr * sizeof(char *),
799		    M_TEMP, M_WAITOK);
800		error = copyin((void *)pstr.ps_argvstr, ps_argvstr,
801		    pstr.ps_nargvstr * sizeof(char *));
802		if (error) {
803			free(ps_argvstr, M_TEMP);
804			return (error);
805		}
806		for (i = 0; i < pstr.ps_nargvstr; i++) {
807			sbuf_copyin(sb, ps_argvstr[i], 0);
808			sbuf_printf(sb, "%c", '\0');
809		}
810		free(ps_argvstr, M_TEMP);
811	}
812
813	return (0);
814}
815
816/*
817 * Filler function for proc/pid/environ
818 */
819static int
820linprocfs_doprocenviron(PFS_FILL_ARGS)
821{
822	sbuf_printf(sb, "doprocenviron\n%c", '\0');
823
824	return (0);
825}
826
827/*
828 * Filler function for proc/pid/maps
829 */
830static int
831linprocfs_doprocmaps(PFS_FILL_ARGS)
832{
833	char mebuffer[512];
834	vm_map_t map = &p->p_vmspace->vm_map;
835	vm_map_entry_t entry;
836	vm_object_t obj, tobj, lobj;
837	vm_ooffset_t off = 0;
838	char *name = "", *freename = NULL;
839	size_t len;
840	ino_t ino;
841	int ref_count, shadow_count, flags;
842	int error;
843	struct vnode *vp;
844	struct vattr vat;
845
846	PROC_LOCK(p);
847	error = p_candebug(td, p);
848	PROC_UNLOCK(p);
849	if (error)
850		return (error);
851
852	if (uio->uio_rw != UIO_READ)
853		return (EOPNOTSUPP);
854
855	if (uio->uio_offset != 0)
856		return (0);
857
858	error = 0;
859	if (map != &curthread->td_proc->p_vmspace->vm_map)
860		vm_map_lock_read(map);
861        for (entry = map->header.next;
862	    ((uio->uio_resid > 0) && (entry != &map->header));
863	    entry = entry->next) {
864		name = "";
865		freename = NULL;
866		if (entry->eflags & MAP_ENTRY_IS_SUB_MAP)
867			continue;
868		obj = entry->object.vm_object;
869		for (lobj = tobj = obj; tobj; tobj = tobj->backing_object)
870			lobj = tobj;
871		ino = 0;
872		if (lobj) {
873			vp = lobj->handle;
874			VM_OBJECT_LOCK(lobj);
875			off = IDX_TO_OFF(lobj->size);
876			if (lobj->type == OBJT_VNODE && lobj->handle) {
877				vn_fullpath(td, vp, &name, &freename);
878				VOP_GETATTR(vp, &vat, td->td_ucred, td);
879				ino = vat.va_fileid;
880			}
881			flags = obj->flags;
882			ref_count = obj->ref_count;
883			shadow_count = obj->shadow_count;
884			VM_OBJECT_UNLOCK(lobj);
885		} else {
886			flags = 0;
887			ref_count = 0;
888			shadow_count = 0;
889		}
890
891		/*
892	     	 * format:
893		 *  start, end, access, offset, major, minor, inode, name.
894		 */
895		snprintf(mebuffer, sizeof mebuffer,
896		    "%08lx-%08lx %s%s%s%s %08lx %02x:%02x %lu%s%s\n",
897		    (u_long)entry->start, (u_long)entry->end,
898		    (entry->protection & VM_PROT_READ)?"r":"-",
899		    (entry->protection & VM_PROT_WRITE)?"w":"-",
900		    (entry->protection & VM_PROT_EXECUTE)?"x":"-",
901		    "p",
902		    (u_long)off,
903		    0,
904		    0,
905		    (u_long)ino,
906		    *name ? "     " : "",
907		    name
908		    );
909		if (freename)
910			free(freename, M_TEMP);
911		len = strlen(mebuffer);
912		if (len > uio->uio_resid)
913			len = uio->uio_resid; /*
914					       * XXX We should probably return
915					       * EFBIG here, as in procfs.
916					       */
917		error = uiomove(mebuffer, len, uio);
918		if (error)
919			break;
920	}
921	if (map != &curthread->td_proc->p_vmspace->vm_map)
922		vm_map_unlock_read(map);
923
924	return (error);
925}
926
927/*
928 * Filler function for proc/net/dev
929 */
930static int
931linprocfs_donetdev(PFS_FILL_ARGS)
932{
933	char ifname[16]; /* XXX LINUX_IFNAMSIZ */
934	struct ifnet *ifp;
935
936	sbuf_printf(sb, "%6s|%58s|%s\n%6s|%58s|%58s\n",
937	    "Inter-", "   Receive", "  Transmit", " face",
938	    "bytes    packets errs drop fifo frame compressed",
939	    "bytes    packets errs drop fifo frame compressed");
940
941	IFNET_RLOCK();
942	TAILQ_FOREACH(ifp, &ifnet, if_link) {
943		linux_ifname(ifp, ifname, sizeof ifname);
944			sbuf_printf(sb, "%6.6s:", ifname);
945		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu ",
946		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
947		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
948		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
949	}
950	IFNET_RUNLOCK();
951
952	return (0);
953}
954
955#if 0
956extern struct cdevsw *cdevsw[];
957
958/*
959 * Filler function for proc/devices
960 */
961static int
962linprocfs_dodevices(PFS_FILL_ARGS)
963{
964	int i;
965
966	sbuf_printf(sb, "Character devices:\n");
967
968	for (i = 0; i < NUMCDEVSW; i++)
969		if (cdevsw[i] != NULL)
970			sbuf_printf(sb, "%3d %s\n", i, cdevsw[i]->d_name);
971
972	sbuf_printf(sb, "\nBlock devices:\n");
973
974	return (0);
975}
976#endif
977
978/*
979 * Filler function for proc/cmdline
980 */
981static int
982linprocfs_docmdline(PFS_FILL_ARGS)
983{
984	sbuf_printf(sb, "BOOT_IMAGE=%s", kernelname);
985	sbuf_printf(sb, " ro root=302\n");
986	return (0);
987}
988
989#if 0
990/*
991 * Filler function for proc/modules
992 */
993static int
994linprocfs_domodules(PFS_FILL_ARGS)
995{
996	struct linker_file *lf;
997
998	TAILQ_FOREACH(lf, &linker_files, link) {
999		sbuf_printf(sb, "%-20s%8lu%4d\n", lf->filename,
1000		    (unsigned long)lf->size, lf->refs);
1001	}
1002	return (0);
1003}
1004#endif
1005
1006/*
1007 * Constructor
1008 */
1009static int
1010linprocfs_init(PFS_INIT_ARGS)
1011{
1012	struct pfs_node *root;
1013	struct pfs_node *dir;
1014
1015	root = pi->pi_root;
1016
1017	/* /proc/... */
1018	pfs_create_file(root, "cmdline", &linprocfs_docmdline,
1019	    NULL, NULL, PFS_RD);
1020	pfs_create_file(root, "cpuinfo", &linprocfs_docpuinfo,
1021	    NULL, NULL, PFS_RD);
1022#if 0
1023	pfs_create_file(root, "devices", &linprocfs_dodevices,
1024	    NULL, NULL, PFS_RD);
1025#endif
1026	pfs_create_file(root, "loadavg", &linprocfs_doloadavg,
1027	    NULL, NULL, PFS_RD);
1028	pfs_create_file(root, "meminfo", &linprocfs_domeminfo,
1029	    NULL, NULL, PFS_RD);
1030#if 0
1031	pfs_create_file(root, "modules", &linprocfs_domodules,
1032	    NULL, NULL, PFS_RD);
1033#endif
1034	pfs_create_file(root, "mtab", &linprocfs_domtab,
1035	    NULL, NULL, PFS_RD);
1036	pfs_create_link(root, "self", &procfs_docurproc,
1037	    NULL, NULL, 0);
1038	pfs_create_file(root, "stat", &linprocfs_dostat,
1039	    NULL, NULL, PFS_RD);
1040	pfs_create_file(root, "uptime", &linprocfs_douptime,
1041	    NULL, NULL, PFS_RD);
1042	pfs_create_file(root, "version", &linprocfs_doversion,
1043	    NULL, NULL, PFS_RD);
1044
1045	/* /proc/net/... */
1046	dir = pfs_create_dir(root, "net", NULL, NULL, 0);
1047	pfs_create_file(dir, "dev", &linprocfs_donetdev,
1048	    NULL, NULL, PFS_RD);
1049
1050	/* /proc/<pid>/... */
1051	dir = pfs_create_dir(root, "pid", NULL, NULL, PFS_PROCDEP);
1052	pfs_create_file(dir, "cmdline", &linprocfs_doproccmdline,
1053	    NULL, NULL, PFS_RD);
1054	pfs_create_link(dir, "cwd", &linprocfs_doproccwd,
1055	    NULL, NULL, 0);
1056	pfs_create_file(dir, "environ", &linprocfs_doprocenviron,
1057	    NULL, NULL, PFS_RD);
1058	pfs_create_link(dir, "exe", &procfs_doprocfile,
1059	    NULL, &procfs_notsystem, 0);
1060	pfs_create_file(dir, "maps", &linprocfs_doprocmaps,
1061	    NULL, NULL, PFS_RD);
1062	pfs_create_file(dir, "mem", &procfs_doprocmem,
1063	    &procfs_attr, &procfs_candebug, PFS_RDWR|PFS_RAW);
1064	pfs_create_link(dir, "root", &linprocfs_doprocroot,
1065	    NULL, NULL, 0);
1066	pfs_create_file(dir, "stat", &linprocfs_doprocstat,
1067	    NULL, NULL, PFS_RD);
1068	pfs_create_file(dir, "statm", &linprocfs_doprocstatm,
1069	    NULL, NULL, PFS_RD);
1070	pfs_create_file(dir, "status", &linprocfs_doprocstatus,
1071	    NULL, NULL, PFS_RD);
1072
1073	return (0);
1074}
1075
1076/*
1077 * Destructor
1078 */
1079static int
1080linprocfs_uninit(PFS_INIT_ARGS)
1081{
1082
1083	/* nothing to do, pseudofs will GC */
1084	return (0);
1085}
1086
1087PSEUDOFS(linprocfs, 1);
1088MODULE_DEPEND(linprocfs, linux, 1, 1, 1);
1089MODULE_DEPEND(linprocfs, procfs, 1, 1, 1);
1090