linprocfs.c revision 143194
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 143194 2005-03-06 22:28:14Z sobomax $");
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 = i * PAGE_SIZE;
148	swapused = 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_NODEV,		"nodev");
399		ADD_OPTION(MNT_UNION,		"union");
400		ADD_OPTION(MNT_ASYNC,		"async");
401		ADD_OPTION(MNT_SUIDDIR,		"suiddir");
402		ADD_OPTION(MNT_NOSYMFOLLOW,	"nosymfollow");
403		ADD_OPTION(MNT_NOATIME,		"noatime");
404#undef ADD_OPTION
405		/* a real Linux mtab will also show NFS options */
406		sbuf_printf(sb, " 0 0\n");
407	}
408	mtx_unlock(&mountlist_mtx);
409	if (flep != NULL)
410		free(flep, M_TEMP);
411	return (error);
412}
413
414/*
415 * Filler function for proc/stat
416 */
417static int
418linprocfs_dostat(PFS_FILL_ARGS)
419{
420	int i;
421
422	sbuf_printf(sb, "cpu %ld %ld %ld %ld\n",
423	    T2J(cp_time[CP_USER]),
424	    T2J(cp_time[CP_NICE]),
425	    T2J(cp_time[CP_SYS] /*+ cp_time[CP_INTR]*/),
426	    T2J(cp_time[CP_IDLE]));
427	for (i = 0; i < mp_ncpus; ++i)
428		sbuf_printf(sb, "cpu%d %ld %ld %ld %ld\n", i,
429		    T2J(cp_time[CP_USER]) / mp_ncpus,
430		    T2J(cp_time[CP_NICE]) / mp_ncpus,
431		    T2J(cp_time[CP_SYS]) / mp_ncpus,
432		    T2J(cp_time[CP_IDLE]) / mp_ncpus);
433	sbuf_printf(sb,
434	    "disk 0 0 0 0\n"
435	    "page %u %u\n"
436	    "swap %u %u\n"
437	    "intr %u\n"
438	    "ctxt %u\n"
439	    "btime %lld\n",
440	    cnt.v_vnodepgsin,
441	    cnt.v_vnodepgsout,
442	    cnt.v_swappgsin,
443	    cnt.v_swappgsout,
444	    cnt.v_intr,
445	    cnt.v_swtch,
446	    (long long)boottime.tv_sec);
447	return (0);
448}
449
450/*
451 * Filler function for proc/uptime
452 */
453static int
454linprocfs_douptime(PFS_FILL_ARGS)
455{
456	struct timeval tv;
457
458	getmicrouptime(&tv);
459	sbuf_printf(sb, "%lld.%02ld %ld.%02ld\n",
460	    (long long)tv.tv_sec, tv.tv_usec / 10000,
461	    T2S(cp_time[CP_IDLE]), T2J(cp_time[CP_IDLE]) % 100);
462	return (0);
463}
464
465/*
466 * Filler function for proc/version
467 */
468static int
469linprocfs_doversion(PFS_FILL_ARGS)
470{
471	char osname[LINUX_MAX_UTSNAME];
472	char osrelease[LINUX_MAX_UTSNAME];
473
474	linux_get_osname(td, osname);
475	linux_get_osrelease(td, osrelease);
476
477	sbuf_printf(sb,
478	    "%s version %s (des@freebsd.org) (gcc version " __VERSION__ ")"
479	    " #4 Sun Dec 18 04:30:00 CET 1977\n", osname, osrelease);
480	return (0);
481}
482
483/*
484 * Filler function for proc/loadavg
485 */
486static int
487linprocfs_doloadavg(PFS_FILL_ARGS)
488{
489	sbuf_printf(sb,
490	    "%d.%02d %d.%02d %d.%02d %d/%d %d\n",
491	    (int)(averunnable.ldavg[0] / averunnable.fscale),
492	    (int)(averunnable.ldavg[0] * 100 / averunnable.fscale % 100),
493	    (int)(averunnable.ldavg[1] / averunnable.fscale),
494	    (int)(averunnable.ldavg[1] * 100 / averunnable.fscale % 100),
495	    (int)(averunnable.ldavg[2] / averunnable.fscale),
496	    (int)(averunnable.ldavg[2] * 100 / averunnable.fscale % 100),
497	    1,				/* number of running tasks */
498	    nprocs,			/* number of tasks */
499	    lastpid			/* the last pid */
500	);
501
502	return (0);
503}
504
505/*
506 * Filler function for proc/pid/stat
507 */
508static int
509linprocfs_doprocstat(PFS_FILL_ARGS)
510{
511	struct kinfo_proc kp;
512
513	PROC_LOCK(p);
514	fill_kinfo_proc(p, &kp);
515	sbuf_printf(sb, "%d", p->p_pid);
516#define PS_ADD(name, fmt, arg) sbuf_printf(sb, " " fmt, arg)
517	PS_ADD("comm",		"(%s)",	p->p_comm);
518	PS_ADD("statr",		"%c",	'0'); /* XXX */
519	PS_ADD("ppid",		"%d",	p->p_pptr ? p->p_pptr->p_pid : 0);
520	PS_ADD("pgrp",		"%d",	p->p_pgid);
521	PS_ADD("session",	"%d",	p->p_session->s_sid);
522	PROC_UNLOCK(p);
523	PS_ADD("tty",		"%d",	0); /* XXX */
524	PS_ADD("tpgid",		"%d",	0); /* XXX */
525	PS_ADD("flags",		"%u",	0); /* XXX */
526	PS_ADD("minflt",	"%u",	0); /* XXX */
527	PS_ADD("cminflt",	"%u",	0); /* XXX */
528	PS_ADD("majflt",	"%u",	0); /* XXX */
529	PS_ADD("cminflt",	"%u",	0); /* XXX */
530	PS_ADD("utime",		"%d",	0); /* XXX */
531	PS_ADD("stime",		"%d",	0); /* XXX */
532	PS_ADD("cutime",	"%d",	0); /* XXX */
533	PS_ADD("cstime",	"%d",	0); /* XXX */
534	PS_ADD("counter",	"%d",	0); /* XXX */
535	PS_ADD("priority",	"%d",	0); /* XXX */
536	PS_ADD("timeout",	"%u",	0); /* XXX */
537	PS_ADD("itrealvalue",	"%u",	0); /* XXX */
538	PS_ADD("starttime",	"%d",	0); /* XXX */
539	PS_ADD("vsize",		"%ju",	(uintmax_t)kp.ki_size);
540	PS_ADD("rss",		"%ju",	P2K((uintmax_t)kp.ki_rssize));
541	PS_ADD("rlim",		"%u",	0); /* XXX */
542	PS_ADD("startcode",	"%u",	(unsigned)0);
543	PS_ADD("endcode",	"%u",	0); /* XXX */
544	PS_ADD("startstack",	"%u",	0); /* XXX */
545	PS_ADD("esp",		"%u",	0); /* XXX */
546	PS_ADD("eip",		"%u",	0); /* XXX */
547	PS_ADD("signal",	"%d",	0); /* XXX */
548	PS_ADD("blocked",	"%d",	0); /* XXX */
549	PS_ADD("sigignore",	"%d",	0); /* XXX */
550	PS_ADD("sigcatch",	"%d",	0); /* XXX */
551	PS_ADD("wchan",		"%u",	0); /* XXX */
552	PS_ADD("nswap",		"%lu",	(long unsigned)0); /* XXX */
553	PS_ADD("cnswap",	"%lu",	(long unsigned)0); /* XXX */
554	PS_ADD("exitsignal",	"%d",	0); /* XXX */
555	PS_ADD("processor",	"%d",	0); /* XXX */
556#undef PS_ADD
557	sbuf_putc(sb, '\n');
558
559	return (0);
560}
561
562/*
563 * Filler function for proc/pid/statm
564 */
565static int
566linprocfs_doprocstatm(PFS_FILL_ARGS)
567{
568	struct kinfo_proc kp;
569	segsz_t lsize;
570
571	PROC_LOCK(p);
572	fill_kinfo_proc(p, &kp);
573	PROC_UNLOCK(p);
574
575	/*
576	 * See comments in linprocfs_doprocstatus() regarding the
577	 * computation of lsize.
578	 */
579	/* size resident share trs drs lrs dt */
580	sbuf_printf(sb, "%ju ", B2P((uintmax_t)kp.ki_size));
581	sbuf_printf(sb, "%ju ", (uintmax_t)kp.ki_rssize);
582	sbuf_printf(sb, "%ju ", (uintmax_t)0); /* XXX */
583	sbuf_printf(sb, "%ju ",	(uintmax_t)kp.ki_tsize);
584	sbuf_printf(sb, "%ju ", (uintmax_t)(kp.ki_dsize + kp.ki_ssize));
585	lsize = B2P(kp.ki_size) - kp.ki_dsize -
586	    kp.ki_ssize - kp.ki_tsize - 1;
587	sbuf_printf(sb, "%ju ", (uintmax_t)lsize);
588	sbuf_printf(sb, "%ju\n", (uintmax_t)0); /* XXX */
589
590	return (0);
591}
592
593/*
594 * Filler function for proc/pid/status
595 */
596static int
597linprocfs_doprocstatus(PFS_FILL_ARGS)
598{
599	struct kinfo_proc kp;
600	char *state;
601	segsz_t lsize;
602	struct thread *td2;
603	struct sigacts *ps;
604	int i;
605
606	PROC_LOCK(p);
607	td2 = FIRST_THREAD_IN_PROC(p); /* XXXKSE pretend only one thread */
608
609	if (P_SHOULDSTOP(p)) {
610		state = "T (stopped)";
611	} else {
612		mtx_lock_spin(&sched_lock);
613		switch(p->p_state) {
614		case PRS_NEW:
615			state = "I (idle)";
616			break;
617		case PRS_NORMAL:
618			if (p->p_flag & P_WEXIT) {
619				state = "X (exiting)";
620				break;
621			}
622			switch(td2->td_state) {
623			case TDS_INHIBITED:
624				state = "S (sleeping)";
625				break;
626			case TDS_RUNQ:
627			case TDS_RUNNING:
628				state = "R (running)";
629				break;
630			default:
631				state = "? (unknown)";
632				break;
633			}
634			break;
635		case PRS_ZOMBIE:
636			state = "Z (zombie)";
637			break;
638		default:
639			state = "? (unknown)";
640			break;
641		}
642		mtx_unlock_spin(&sched_lock);
643	}
644
645	fill_kinfo_proc(p, &kp);
646	sbuf_printf(sb, "Name:\t%s\n",		p->p_comm); /* XXX escape */
647	sbuf_printf(sb, "State:\t%s\n",		state);
648
649	/*
650	 * Credentials
651	 */
652	sbuf_printf(sb, "Pid:\t%d\n",		p->p_pid);
653	sbuf_printf(sb, "PPid:\t%d\n",		p->p_pptr ?
654						p->p_pptr->p_pid : 0);
655	sbuf_printf(sb, "Uid:\t%d %d %d %d\n",	p->p_ucred->cr_ruid,
656						p->p_ucred->cr_uid,
657						p->p_ucred->cr_svuid,
658						/* FreeBSD doesn't have fsuid */
659						p->p_ucred->cr_uid);
660	sbuf_printf(sb, "Gid:\t%d %d %d %d\n",	p->p_ucred->cr_rgid,
661						p->p_ucred->cr_gid,
662						p->p_ucred->cr_svgid,
663						/* FreeBSD doesn't have fsgid */
664						p->p_ucred->cr_gid);
665	sbuf_cat(sb, "Groups:\t");
666	for (i = 0; i < p->p_ucred->cr_ngroups; i++)
667		sbuf_printf(sb, "%d ",		p->p_ucred->cr_groups[i]);
668	PROC_UNLOCK(p);
669	sbuf_putc(sb, '\n');
670
671	/*
672	 * Memory
673	 *
674	 * While our approximation of VmLib may not be accurate (I
675	 * don't know of a simple way to verify it, and I'm not sure
676	 * it has much meaning anyway), I believe it's good enough.
677	 *
678	 * The same code that could (I think) accurately compute VmLib
679	 * could also compute VmLck, but I don't really care enough to
680	 * implement it. Submissions are welcome.
681	 */
682	sbuf_printf(sb, "VmSize:\t%8ju kB\n",	B2K((uintmax_t)kp.ki_size));
683	sbuf_printf(sb, "VmLck:\t%8u kB\n",	P2K(0)); /* XXX */
684	sbuf_printf(sb, "VmRss:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_rssize));
685	sbuf_printf(sb, "VmData:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_dsize));
686	sbuf_printf(sb, "VmStk:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_ssize));
687	sbuf_printf(sb, "VmExe:\t%8ju kB\n",	P2K((uintmax_t)kp.ki_tsize));
688	lsize = B2P(kp.ki_size) - kp.ki_dsize -
689	    kp.ki_ssize - kp.ki_tsize - 1;
690	sbuf_printf(sb, "VmLib:\t%8ju kB\n",	P2K((uintmax_t)lsize));
691
692	/*
693	 * Signal masks
694	 *
695	 * We support up to 128 signals, while Linux supports 32,
696	 * but we only define 32 (the same 32 as Linux, to boot), so
697	 * just show the lower 32 bits of each mask. XXX hack.
698	 *
699	 * NB: on certain platforms (Sparc at least) Linux actually
700	 * supports 64 signals, but this code is a long way from
701	 * running on anything but i386, so ignore that for now.
702	 */
703	PROC_LOCK(p);
704	sbuf_printf(sb, "SigPnd:\t%08x\n",	p->p_siglist.__bits[0]);
705	/*
706	 * I can't seem to find out where the signal mask is in
707	 * relation to struct proc, so SigBlk is left unimplemented.
708	 */
709	sbuf_printf(sb, "SigBlk:\t%08x\n",	0); /* XXX */
710	ps = p->p_sigacts;
711	mtx_lock(&ps->ps_mtx);
712	sbuf_printf(sb, "SigIgn:\t%08x\n",	ps->ps_sigignore.__bits[0]);
713	sbuf_printf(sb, "SigCgt:\t%08x\n",	ps->ps_sigcatch.__bits[0]);
714	mtx_unlock(&ps->ps_mtx);
715	PROC_UNLOCK(p);
716
717	/*
718	 * Linux also prints the capability masks, but we don't have
719	 * capabilities yet, and when we do get them they're likely to
720	 * be meaningless to Linux programs, so we lie. XXX
721	 */
722	sbuf_printf(sb, "CapInh:\t%016x\n",	0);
723	sbuf_printf(sb, "CapPrm:\t%016x\n",	0);
724	sbuf_printf(sb, "CapEff:\t%016x\n",	0);
725
726	return (0);
727}
728
729
730/*
731 * Filler function for proc/pid/cwd
732 */
733static int
734linprocfs_doproccwd(PFS_FILL_ARGS)
735{
736	char *fullpath = "unknown";
737	char *freepath = NULL;
738
739	vn_fullpath(td, p->p_fd->fd_cdir, &fullpath, &freepath);
740	sbuf_printf(sb, "%s", fullpath);
741	if (freepath)
742		free(freepath, M_TEMP);
743	return (0);
744}
745
746/*
747 * Filler function for proc/pid/root
748 */
749static int
750linprocfs_doprocroot(PFS_FILL_ARGS)
751{
752	struct vnode *rvp;
753	char *fullpath = "unknown";
754	char *freepath = NULL;
755
756	rvp = jailed(p->p_ucred) ? p->p_fd->fd_jdir : p->p_fd->fd_rdir;
757	vn_fullpath(td, rvp, &fullpath, &freepath);
758	sbuf_printf(sb, "%s", fullpath);
759	if (freepath)
760		free(freepath, M_TEMP);
761	return (0);
762}
763
764/*
765 * Filler function for proc/pid/cmdline
766 */
767static int
768linprocfs_doproccmdline(PFS_FILL_ARGS)
769{
770	struct ps_strings pstr;
771	char **ps_argvstr;
772	int error, i;
773
774	/*
775	 * If we are using the ps/cmdline caching, use that.  Otherwise
776	 * revert back to the old way which only implements full cmdline
777	 * for the currept process and just p->p_comm for all other
778	 * processes.
779	 * Note that if the argv is no longer available, we deliberately
780	 * don't fall back on p->p_comm or return an error: the authentic
781	 * Linux behaviour is to return zero-length in this case.
782	 */
783
784	PROC_LOCK(p);
785	if (p->p_args && p_cansee(td, p) == 0) {
786		sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length);
787		PROC_UNLOCK(p);
788	} else if (p != td->td_proc) {
789		PROC_UNLOCK(p);
790		sbuf_printf(sb, "%.*s", MAXCOMLEN, p->p_comm);
791	} else {
792		PROC_UNLOCK(p);
793		error = copyin((void *)p->p_sysent->sv_psstrings, &pstr,
794		    sizeof(pstr));
795		if (error)
796			return (error);
797		if (pstr.ps_nargvstr > ARG_MAX)
798			return (E2BIG);
799		ps_argvstr = malloc(pstr.ps_nargvstr * sizeof(char *),
800		    M_TEMP, M_WAITOK);
801		error = copyin((void *)pstr.ps_argvstr, ps_argvstr,
802		    pstr.ps_nargvstr * sizeof(char *));
803		if (error) {
804			free(ps_argvstr, M_TEMP);
805			return (error);
806		}
807		for (i = 0; i < pstr.ps_nargvstr; i++) {
808			sbuf_copyin(sb, ps_argvstr[i], 0);
809			sbuf_printf(sb, "%c", '\0');
810		}
811		free(ps_argvstr, M_TEMP);
812	}
813
814	return (0);
815}
816
817/*
818 * Filler function for proc/pid/environ
819 */
820static int
821linprocfs_doprocenviron(PFS_FILL_ARGS)
822{
823	sbuf_printf(sb, "doprocenviron\n%c", '\0');
824
825	return (0);
826}
827
828/*
829 * Filler function for proc/pid/maps
830 */
831static int
832linprocfs_doprocmaps(PFS_FILL_ARGS)
833{
834	char mebuffer[512];
835	vm_map_t map = &p->p_vmspace->vm_map;
836	vm_map_entry_t entry;
837	vm_object_t obj, tobj, lobj;
838	vm_ooffset_t off = 0;
839	char *name = "", *freename = NULL;
840	size_t len;
841	ino_t ino;
842	int ref_count, shadow_count, flags;
843	int error;
844	struct vnode *vp;
845	struct vattr vat;
846
847	PROC_LOCK(p);
848	error = p_candebug(td, p);
849	PROC_UNLOCK(p);
850	if (error)
851		return (error);
852
853	if (uio->uio_rw != UIO_READ)
854		return (EOPNOTSUPP);
855
856	if (uio->uio_offset != 0)
857		return (0);
858
859	error = 0;
860	if (map != &curthread->td_proc->p_vmspace->vm_map)
861		vm_map_lock_read(map);
862        for (entry = map->header.next;
863	    ((uio->uio_resid > 0) && (entry != &map->header));
864	    entry = entry->next) {
865		name = "";
866		freename = NULL;
867		if (entry->eflags & MAP_ENTRY_IS_SUB_MAP)
868			continue;
869		obj = entry->object.vm_object;
870		for (lobj = tobj = obj; tobj; tobj = tobj->backing_object)
871			lobj = tobj;
872		ino = 0;
873		if (lobj) {
874			vp = lobj->handle;
875			VM_OBJECT_LOCK(lobj);
876			off = IDX_TO_OFF(lobj->size);
877			if (lobj->type == OBJT_VNODE && lobj->handle) {
878				vn_fullpath(td, vp, &name, &freename);
879				VOP_GETATTR(vp, &vat, td->td_ucred, td);
880				ino = vat.va_fileid;
881			}
882			flags = obj->flags;
883			ref_count = obj->ref_count;
884			shadow_count = obj->shadow_count;
885			VM_OBJECT_UNLOCK(lobj);
886		} else {
887			flags = 0;
888			ref_count = 0;
889			shadow_count = 0;
890		}
891
892		/*
893	     	 * format:
894		 *  start, end, access, offset, major, minor, inode, name.
895		 */
896		snprintf(mebuffer, sizeof mebuffer,
897		    "%08lx-%08lx %s%s%s%s %08lx %02x:%02x %lu%s%s\n",
898		    (u_long)entry->start, (u_long)entry->end,
899		    (entry->protection & VM_PROT_READ)?"r":"-",
900		    (entry->protection & VM_PROT_WRITE)?"w":"-",
901		    (entry->protection & VM_PROT_EXECUTE)?"x":"-",
902		    "p",
903		    (u_long)off,
904		    0,
905		    0,
906		    (u_long)ino,
907		    *name ? "     " : "",
908		    name
909		    );
910		if (freename)
911			free(freename, M_TEMP);
912		len = strlen(mebuffer);
913		if (len > uio->uio_resid)
914			len = uio->uio_resid; /*
915					       * XXX We should probably return
916					       * EFBIG here, as in procfs.
917					       */
918		error = uiomove(mebuffer, len, uio);
919		if (error)
920			break;
921	}
922	if (map != &curthread->td_proc->p_vmspace->vm_map)
923		vm_map_unlock_read(map);
924
925	return (error);
926}
927
928/*
929 * Filler function for proc/net/dev
930 */
931static int
932linprocfs_donetdev(PFS_FILL_ARGS)
933{
934	char ifname[16]; /* XXX LINUX_IFNAMSIZ */
935	struct ifnet *ifp;
936
937	sbuf_printf(sb, "%6s|%58s|%s\n%6s|%58s|%58s\n",
938	    "Inter-", "   Receive", "  Transmit", " face",
939	    "bytes    packets errs drop fifo frame compressed",
940	    "bytes    packets errs drop fifo frame compressed");
941
942	IFNET_RLOCK();
943	TAILQ_FOREACH(ifp, &ifnet, if_link) {
944		linux_ifname(ifp, ifname, sizeof ifname);
945			sbuf_printf(sb, "%6.6s:", ifname);
946		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu ",
947		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
948		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
949		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
950	}
951	IFNET_RUNLOCK();
952
953	return (0);
954}
955
956#if 0
957extern struct cdevsw *cdevsw[];
958
959/*
960 * Filler function for proc/devices
961 */
962static int
963linprocfs_dodevices(PFS_FILL_ARGS)
964{
965	int i;
966
967	sbuf_printf(sb, "Character devices:\n");
968
969	for (i = 0; i < NUMCDEVSW; i++)
970		if (cdevsw[i] != NULL)
971			sbuf_printf(sb, "%3d %s\n", i, cdevsw[i]->d_name);
972
973	sbuf_printf(sb, "\nBlock devices:\n");
974
975	return (0);
976}
977#endif
978
979/*
980 * Filler function for proc/cmdline
981 */
982static int
983linprocfs_docmdline(PFS_FILL_ARGS)
984{
985	sbuf_printf(sb, "BOOT_IMAGE=%s", kernelname);
986	sbuf_printf(sb, " ro root=302\n");
987	return (0);
988}
989
990#if 0
991/*
992 * Filler function for proc/modules
993 */
994static int
995linprocfs_domodules(PFS_FILL_ARGS)
996{
997	struct linker_file *lf;
998
999	TAILQ_FOREACH(lf, &linker_files, link) {
1000		sbuf_printf(sb, "%-20s%8lu%4d\n", lf->filename,
1001		    (unsigned long)lf->size, lf->refs);
1002	}
1003	return (0);
1004}
1005#endif
1006
1007/*
1008 * Constructor
1009 */
1010static int
1011linprocfs_init(PFS_INIT_ARGS)
1012{
1013	struct pfs_node *root;
1014	struct pfs_node *dir;
1015
1016	root = pi->pi_root;
1017
1018	/* /proc/... */
1019	pfs_create_file(root, "cmdline", &linprocfs_docmdline,
1020	    NULL, NULL, PFS_RD);
1021	pfs_create_file(root, "cpuinfo", &linprocfs_docpuinfo,
1022	    NULL, NULL, PFS_RD);
1023#if 0
1024	pfs_create_file(root, "devices", &linprocfs_dodevices,
1025	    NULL, NULL, PFS_RD);
1026#endif
1027	pfs_create_file(root, "loadavg", &linprocfs_doloadavg,
1028	    NULL, NULL, PFS_RD);
1029	pfs_create_file(root, "meminfo", &linprocfs_domeminfo,
1030	    NULL, NULL, PFS_RD);
1031#if 0
1032	pfs_create_file(root, "modules", &linprocfs_domodules,
1033	    NULL, NULL, PFS_RD);
1034#endif
1035	pfs_create_file(root, "mtab", &linprocfs_domtab,
1036	    NULL, NULL, PFS_RD);
1037	pfs_create_link(root, "self", &procfs_docurproc,
1038	    NULL, NULL, 0);
1039	pfs_create_file(root, "stat", &linprocfs_dostat,
1040	    NULL, NULL, PFS_RD);
1041	pfs_create_file(root, "uptime", &linprocfs_douptime,
1042	    NULL, NULL, PFS_RD);
1043	pfs_create_file(root, "version", &linprocfs_doversion,
1044	    NULL, NULL, PFS_RD);
1045
1046	/* /proc/net/... */
1047	dir = pfs_create_dir(root, "net", NULL, NULL, 0);
1048	pfs_create_file(dir, "dev", &linprocfs_donetdev,
1049	    NULL, NULL, PFS_RD);
1050
1051	/* /proc/<pid>/... */
1052	dir = pfs_create_dir(root, "pid", NULL, NULL, PFS_PROCDEP);
1053	pfs_create_file(dir, "cmdline", &linprocfs_doproccmdline,
1054	    NULL, NULL, PFS_RD);
1055	pfs_create_link(dir, "cwd", &linprocfs_doproccwd,
1056	    NULL, NULL, 0);
1057	pfs_create_file(dir, "environ", &linprocfs_doprocenviron,
1058	    NULL, NULL, PFS_RD);
1059	pfs_create_link(dir, "exe", &procfs_doprocfile,
1060	    NULL, &procfs_notsystem, 0);
1061	pfs_create_file(dir, "maps", &linprocfs_doprocmaps,
1062	    NULL, NULL, PFS_RD);
1063	pfs_create_file(dir, "mem", &procfs_doprocmem,
1064	    &procfs_attr, &procfs_candebug, PFS_RDWR|PFS_RAW);
1065	pfs_create_link(dir, "root", &linprocfs_doprocroot,
1066	    NULL, NULL, 0);
1067	pfs_create_file(dir, "stat", &linprocfs_doprocstat,
1068	    NULL, NULL, PFS_RD);
1069	pfs_create_file(dir, "statm", &linprocfs_doprocstatm,
1070	    NULL, NULL, PFS_RD);
1071	pfs_create_file(dir, "status", &linprocfs_doprocstatus,
1072	    NULL, NULL, PFS_RD);
1073
1074	return (0);
1075}
1076
1077/*
1078 * Destructor
1079 */
1080static int
1081linprocfs_uninit(PFS_INIT_ARGS)
1082{
1083
1084	/* nothing to do, pseudofs will GC */
1085	return (0);
1086}
1087
1088PSEUDOFS(linprocfs, 1);
1089MODULE_DEPEND(linprocfs, linux, 1, 1, 1);
1090MODULE_DEPEND(linprocfs, procfs, 1, 1, 1);
1091