xref: /freebsd/sys/compat/linprocfs/linprocfs.c (revision f9218d3d4fd34f082473b3a021c6d4d109fb47cf)
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  * $FreeBSD$
42  */
43 
44 #include <sys/param.h>
45 #include <sys/queue.h>
46 #include <sys/blist.h>
47 #include <sys/conf.h>
48 #include <sys/exec.h>
49 #include <sys/jail.h>
50 #include <sys/kernel.h>
51 #include <sys/linker.h>
52 #include <sys/lock.h>
53 #include <sys/malloc.h>
54 #include <sys/mount.h>
55 #include <sys/mutex.h>
56 #include <sys/namei.h>
57 #include <sys/proc.h>
58 #include <sys/resourcevar.h>
59 #include <sys/sbuf.h>
60 #include <sys/socket.h>
61 #include <sys/sysctl.h>
62 #include <sys/systm.h>
63 #include <sys/tty.h>
64 #include <sys/user.h>
65 #include <sys/vmmeter.h>
66 #include <sys/vnode.h>
67 
68 #include <net/if.h>
69 
70 #include <vm/vm.h>
71 #include <vm/pmap.h>
72 #include <vm/vm_map.h>
73 #include <vm/vm_param.h>
74 #include <vm/vm_object.h>
75 #include <vm/swap_pager.h>
76 
77 #include <machine/clock.h>
78 
79 #ifdef __alpha__
80 #include <machine/alpha_cpu.h>
81 #include <machine/cpuconf.h>
82 #include <machine/rpb.h>
83 extern int ncpus;
84 #endif /* __alpha__ */
85 
86 #ifdef __i386__
87 #include <machine/cputypes.h>
88 #include <machine/md_var.h>
89 #endif /* __i386__ */
90 
91 #include <machine/../linux/linux.h>
92 #include <compat/linux/linux_ioctl.h>
93 #include <compat/linux/linux_mib.h>
94 #include <compat/linux/linux_util.h>
95 #include <fs/pseudofs/pseudofs.h>
96 #include <fs/procfs/procfs.h>
97 
98 /*
99  * Various conversion macros
100  */
101 #define T2J(x) (((x) * 100UL) / (stathz ? stathz : hz))	/* ticks to jiffies */
102 #define T2S(x) ((x) / (stathz ? stathz : hz))		/* ticks to seconds */
103 #define B2K(x) ((x) >> 10)				/* bytes to kbytes */
104 #define B2P(x) ((x) >> PAGE_SHIFT)			/* bytes to pages */
105 #define P2B(x) ((x) << PAGE_SHIFT)			/* pages to bytes */
106 #define P2K(x) ((x) << (PAGE_SHIFT - 10))		/* pages to kbytes */
107 
108 /*
109  * Filler function for proc/meminfo
110  */
111 static int
112 linprocfs_domeminfo(PFS_FILL_ARGS)
113 {
114 	unsigned long memtotal;		/* total memory in bytes */
115 	unsigned long memused;		/* used memory in bytes */
116 	unsigned long memfree;		/* free memory in bytes */
117 	unsigned long memshared;	/* shared memory ??? */
118 	unsigned long buffers, cached;	/* buffer / cache memory ??? */
119 	u_quad_t swaptotal;		/* total swap space in bytes */
120 	u_quad_t swapused;		/* used swap space in bytes */
121 	u_quad_t swapfree;		/* free swap space in bytes */
122 	vm_object_t object;
123 
124 	memtotal = physmem * PAGE_SIZE;
125 	/*
126 	 * The correct thing here would be:
127 	 *
128 	memfree = cnt.v_free_count * PAGE_SIZE;
129 	memused = memtotal - memfree;
130 	 *
131 	 * but it might mislead linux binaries into thinking there
132 	 * is very little memory left, so we cheat and tell them that
133 	 * all memory that isn't wired down is free.
134 	 */
135 	memused = cnt.v_wire_count * PAGE_SIZE;
136 	memfree = memtotal - memused;
137 	if (swapblist == NULL) {
138 		swaptotal = 0;
139 		swapfree = 0;
140 	} else {
141 		swaptotal = (u_quad_t)swapblist->bl_blocks * 1024; /* XXX why 1024? */
142 		swapfree = (u_quad_t)swapblist->bl_root->u.bmu_avail * PAGE_SIZE;
143 	}
144 	swapused = swaptotal - swapfree;
145 	memshared = 0;
146 	TAILQ_FOREACH(object, &vm_object_list, object_list)
147 		if (object->shadow_count > 1)
148 			memshared += object->resident_page_count;
149 	memshared *= PAGE_SIZE;
150 	/*
151 	 * We'd love to be able to write:
152 	 *
153 	buffers = bufspace;
154 	 *
155 	 * but bufspace is internal to vfs_bio.c and we don't feel
156 	 * like unstaticizing it just for linprocfs's sake.
157 	 */
158 	buffers = 0;
159 	cached = cnt.v_cache_count * PAGE_SIZE;
160 
161 	sbuf_printf(sb,
162 	    "	     total:    used:	free:  shared: buffers:	 cached:\n"
163 	    "Mem:  %lu %lu %lu %lu %lu %lu\n"
164 	    "Swap: %llu %llu %llu\n"
165 	    "MemTotal: %9lu kB\n"
166 	    "MemFree:  %9lu kB\n"
167 	    "MemShared:%9lu kB\n"
168 	    "Buffers:  %9lu kB\n"
169 	    "Cached:   %9lu kB\n"
170 	    "SwapTotal:%9llu kB\n"
171 	    "SwapFree: %9llu kB\n",
172 	    memtotal, memused, memfree, memshared, buffers, cached,
173 	    swaptotal, swapused, swapfree,
174 	    B2K(memtotal), B2K(memfree),
175 	    B2K(memshared), B2K(buffers), B2K(cached),
176 	    B2K(swaptotal), B2K(swapfree));
177 
178 	return (0);
179 }
180 
181 #ifdef __alpha__
182 /*
183  * Filler function for proc/cpuinfo (Alpha version)
184  */
185 static int
186 linprocfs_docpuinfo(PFS_FILL_ARGS)
187 {
188 	u_int64_t type, major;
189 	struct pcs *pcsp;
190 	const char *model, *sysname;
191 
192 	static const char *cpuname[] = {
193 		"EV3", "EV4", "Simulate", "LCA4", "EV5", "EV45", "EV56",
194 		"EV6", "PCA56", "PCA57", "EV67", "EV68CB", "EV68AL"
195 	};
196 
197 	pcsp = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id);
198 	type = pcsp->pcs_proc_type;
199 	major = (type & PCS_PROC_MAJOR) >> PCS_PROC_MAJORSHIFT;
200 	if (major < sizeof(cpuname)/sizeof(char *)) {
201 		model = cpuname[major - 1];
202 	} else {
203 		model = "unknown";
204 	}
205 
206 	sysname = alpha_dsr_sysname();
207 
208 	sbuf_printf(sb,
209 	    "cpu\t\t\t: Alpha\n"
210 	    "cpu model\t\t: %s\n"
211 	    "cpu variation\t\t: %ld\n"
212 	    "cpu revision\t\t: %ld\n"
213 	    "cpu serial number\t: %s\n"
214 	    "system type\t\t: %s\n"
215 	    "system variation\t: %s\n"
216 	    "system revision\t\t: %ld\n"
217 	    "system serial number\t: %s\n"
218 	    "cycle frequency [Hz]\t: %lu\n"
219 	    "timer frequency [Hz]\t: %lu\n"
220 	    "page size [bytes]\t: %ld\n"
221 	    "phys. address bits\t: %ld\n"
222 	    "max. addr. space #\t: %ld\n"
223 	    "BogoMIPS\t\t: %lu.%02lu\n"
224 	    "kernel unaligned acc\t: %ld (pc=%lx,va=%lx)\n"
225 	    "user unaligned acc\t: %ld (pc=%lx,va=%lx)\n"
226 	    "platform string\t\t: %s\n"
227 	    "cpus detected\t\t: %d\n"
228 	    ,
229 	    model,
230 	    pcsp->pcs_proc_var,
231 	    *(int *)hwrpb->rpb_revision,
232 	    " ",
233 	    " ",
234 	    "0",
235 	    0,
236 	    " ",
237 	    hwrpb->rpb_cc_freq,
238 	    hz,
239 	    hwrpb->rpb_page_size,
240 	    hwrpb->rpb_phys_addr_size,
241 	    hwrpb->rpb_max_asn,
242 	    0, 0,
243 	    0, 0, 0,
244 	    0, 0, 0,
245 	    sysname,
246 	    ncpus);
247 	return (0);
248 }
249 #endif /* __alpha__ */
250 
251 #ifdef __i386__
252 /*
253  * Filler function for proc/cpuinfo (i386 version)
254  */
255 static int
256 linprocfs_docpuinfo(PFS_FILL_ARGS)
257 {
258 	int class, i, fqmhz, fqkhz;
259 
260 	/*
261 	 * We default the flags to include all non-conflicting flags,
262 	 * and the Intel versions of conflicting flags.
263 	 */
264 	static char *flags[] = {
265 		"fpu",	    "vme",     "de",	   "pse",      "tsc",
266 		"msr",	    "pae",     "mce",	   "cx8",      "apic",
267 		"sep",	    "sep",     "mtrr",	   "pge",      "mca",
268 		"cmov",	    "pat",     "pse36",	   "pn",       "b19",
269 		"b20",	    "b21",     "mmxext",   "mmx",      "fxsr",
270 		"xmm",	    "b26",     "b27",	   "b28",      "b29",
271 		"3dnowext", "3dnow"
272 	};
273 
274 	switch (cpu_class) {
275 	case CPUCLASS_286:
276 		class = 2;
277 		break;
278 	case CPUCLASS_386:
279 		class = 3;
280 		break;
281 	case CPUCLASS_486:
282 		class = 4;
283 		break;
284 	case CPUCLASS_586:
285 		class = 5;
286 		break;
287 	case CPUCLASS_686:
288 		class = 6;
289 		break;
290 	default:
291 		class = 0;
292 		break;
293 	}
294 
295 	sbuf_printf(sb,
296 	    "processor\t: %d\n"
297 	    "vendor_id\t: %.20s\n"
298 	    "cpu family\t: %d\n"
299 	    "model\t\t: %d\n"
300 	    "stepping\t: %d\n",
301 	    0, cpu_vendor, class, cpu, cpu_id & 0xf);
302 
303 	sbuf_cat(sb,
304 	    "flags\t\t:");
305 
306 	if (!strcmp(cpu_vendor, "AuthenticAMD") && (class < 6)) {
307 		flags[16] = "fcmov";
308 	} else if (!strcmp(cpu_vendor, "CyrixInstead")) {
309 		flags[24] = "cxmmx";
310 	}
311 
312 	for (i = 0; i < 32; i++)
313 		if (cpu_feature & (1 << i))
314 			sbuf_printf(sb, " %s", flags[i]);
315 	sbuf_cat(sb, "\n");
316 	if (class >= 5) {
317 		fqmhz = (tsc_freq + 4999) / 1000000;
318 		fqkhz = ((tsc_freq + 4999) / 10000) % 100;
319 		sbuf_printf(sb,
320 		    "cpu MHz\t\t: %d.%02d\n"
321 		    "bogomips\t: %d.%02d\n",
322 		    fqmhz, fqkhz, fqmhz, fqkhz);
323 	}
324 
325 	return (0);
326 }
327 #endif /* __i386__ */
328 
329 /*
330  * Filler function for proc/mtab
331  *
332  * This file doesn't exist in Linux' procfs, but is included here so
333  * users can symlink /compat/linux/etc/mtab to /proc/mtab
334  */
335 static int
336 linprocfs_domtab(PFS_FILL_ARGS)
337 {
338 	struct nameidata nd;
339 	struct mount *mp;
340 	const char *lep;
341 	char *dlep, *flep, *mntto, *mntfrom, *fstype;
342 	size_t lep_len;
343 	int error;
344 
345 	/* resolve symlinks etc. in the emulation tree prefix */
346 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, linux_emul_path, td);
347 	flep = NULL;
348 	if (namei(&nd) != 0 || vn_fullpath(td, nd.ni_vp, &dlep, &flep) == -1)
349 		lep = linux_emul_path;
350 	else
351 		lep = dlep;
352 	lep_len = strlen(lep);
353 
354 	mtx_lock(&mountlist_mtx);
355 	error = 0;
356 	TAILQ_FOREACH(mp, &mountlist, mnt_list) {
357 		error = VFS_STATFS(mp, &mp->mnt_stat, td);
358 		if (error)
359 			break;
360 
361 		/* determine device name */
362 		mntfrom = mp->mnt_stat.f_mntfromname;
363 
364 		/* determine mount point */
365 		mntto = mp->mnt_stat.f_mntonname;
366 		if (strncmp(mntto, lep, lep_len) == 0 &&
367 		    mntto[lep_len] == '/')
368 			mntto += lep_len;
369 
370 		/* determine fs type */
371 		fstype = mp->mnt_stat.f_fstypename;
372 		if (strcmp(fstype, pn->pn_info->pi_name) == 0)
373 			mntfrom = fstype = "proc";
374 		else if (strcmp(fstype, "procfs") == 0)
375 			continue;
376 
377 		sbuf_printf(sb, "%s %s %s %s", mntfrom, mntto, fstype,
378 		    mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw");
379 #define ADD_OPTION(opt, name) \
380 	if (mp->mnt_stat.f_flags & (opt)) sbuf_printf(sb, "," name);
381 		ADD_OPTION(MNT_SYNCHRONOUS,	"sync");
382 		ADD_OPTION(MNT_NOEXEC,		"noexec");
383 		ADD_OPTION(MNT_NOSUID,		"nosuid");
384 		ADD_OPTION(MNT_NODEV,		"nodev");
385 		ADD_OPTION(MNT_UNION,		"union");
386 		ADD_OPTION(MNT_ASYNC,		"async");
387 		ADD_OPTION(MNT_SUIDDIR,		"suiddir");
388 		ADD_OPTION(MNT_NOSYMFOLLOW,	"nosymfollow");
389 		ADD_OPTION(MNT_NOATIME,		"noatime");
390 #undef ADD_OPTION
391 		/* a real Linux mtab will also show NFS options */
392 		sbuf_printf(sb, " 0 0\n");
393 	}
394 	mtx_unlock(&mountlist_mtx);
395 	if (flep != NULL)
396 		free(flep, M_TEMP);
397 	return (error);
398 }
399 
400 /*
401  * Filler function for proc/stat
402  */
403 static int
404 linprocfs_dostat(PFS_FILL_ARGS)
405 {
406 	sbuf_printf(sb,
407 	    "cpu %ld %ld %ld %ld\n"
408 	    "disk 0 0 0 0\n"
409 	    "page %u %u\n"
410 	    "swap %u %u\n"
411 	    "intr %u\n"
412 	    "ctxt %u\n"
413 	    "btime %lld\n",
414 	    T2J(cp_time[CP_USER]),
415 	    T2J(cp_time[CP_NICE]),
416 	    T2J(cp_time[CP_SYS] /*+ cp_time[CP_INTR]*/),
417 	    T2J(cp_time[CP_IDLE]),
418 	    cnt.v_vnodepgsin,
419 	    cnt.v_vnodepgsout,
420 	    cnt.v_swappgsin,
421 	    cnt.v_swappgsout,
422 	    cnt.v_intr,
423 	    cnt.v_swtch,
424 	    (quad_t)boottime.tv_sec);
425 	return (0);
426 }
427 
428 /*
429  * Filler function for proc/uptime
430  */
431 static int
432 linprocfs_douptime(PFS_FILL_ARGS)
433 {
434 	struct timeval tv;
435 
436 	getmicrouptime(&tv);
437 	sbuf_printf(sb, "%lld.%02ld %ld.%02ld\n",
438 	    (quad_t)tv.tv_sec, tv.tv_usec / 10000,
439 	    T2S(cp_time[CP_IDLE]), T2J(cp_time[CP_IDLE]) % 100);
440 	return (0);
441 }
442 
443 /*
444  * Filler function for proc/version
445  */
446 static int
447 linprocfs_doversion(PFS_FILL_ARGS)
448 {
449 	char osname[LINUX_MAX_UTSNAME];
450 	char osrelease[LINUX_MAX_UTSNAME];
451 
452 	linux_get_osname(td->td_proc, osname);
453 	linux_get_osrelease(td->td_proc, osrelease);
454 
455 	sbuf_printf(sb,
456 	    "%s version %s (des@freebsd.org) (gcc version " __VERSION__ ")"
457 	    " #4 Sun Dec 18 04:30:00 CET 1977\n", osname, osrelease);
458 	return (0);
459 }
460 
461 /*
462  * Filler function for proc/loadavg
463  */
464 static int
465 linprocfs_doloadavg(PFS_FILL_ARGS)
466 {
467 	sbuf_printf(sb,
468 	    "%d.%02d %d.%02d %d.%02d %d/%d %d\n",
469 	    (int)(averunnable.ldavg[0] / averunnable.fscale),
470 	    (int)(averunnable.ldavg[0] * 100 / averunnable.fscale % 100),
471 	    (int)(averunnable.ldavg[1] / averunnable.fscale),
472 	    (int)(averunnable.ldavg[1] * 100 / averunnable.fscale % 100),
473 	    (int)(averunnable.ldavg[2] / averunnable.fscale),
474 	    (int)(averunnable.ldavg[2] * 100 / averunnable.fscale % 100),
475 	    1,				/* number of running tasks */
476 	    nprocs,			/* number of tasks */
477 	    lastpid			/* the last pid */
478 	);
479 
480 	return (0);
481 }
482 
483 /*
484  * Filler function for proc/pid/stat
485  */
486 static int
487 linprocfs_doprocstat(PFS_FILL_ARGS)
488 {
489 	struct kinfo_proc kp;
490 
491 	PROC_LOCK(p);
492 	fill_kinfo_proc(p, &kp);
493 	sbuf_printf(sb, "%d", p->p_pid);
494 #define PS_ADD(name, fmt, arg) sbuf_printf(sb, " " fmt, arg)
495 	PS_ADD("comm",		"(%s)",	p->p_comm);
496 	PS_ADD("statr",		"%c",	'0'); /* XXX */
497 	PS_ADD("ppid",		"%d",	p->p_pptr ? p->p_pptr->p_pid : 0);
498 	PS_ADD("pgrp",		"%d",	p->p_pgid);
499 	PS_ADD("session",	"%d",	p->p_session->s_sid);
500 	PROC_UNLOCK(p);
501 	PS_ADD("tty",		"%d",	0); /* XXX */
502 	PS_ADD("tpgid",		"%d",	0); /* XXX */
503 	PS_ADD("flags",		"%u",	0); /* XXX */
504 	PS_ADD("minflt",	"%u",	0); /* XXX */
505 	PS_ADD("cminflt",	"%u",	0); /* XXX */
506 	PS_ADD("majflt",	"%u",	0); /* XXX */
507 	PS_ADD("cminflt",	"%u",	0); /* XXX */
508 	PS_ADD("utime",		"%d",	0); /* XXX */
509 	PS_ADD("stime",		"%d",	0); /* XXX */
510 	PS_ADD("cutime",	"%d",	0); /* XXX */
511 	PS_ADD("cstime",	"%d",	0); /* XXX */
512 	PS_ADD("counter",	"%d",	0); /* XXX */
513 	PS_ADD("priority",	"%d",	0); /* XXX */
514 	PS_ADD("timeout",	"%u",	0); /* XXX */
515 	PS_ADD("itrealvalue",	"%u",	0); /* XXX */
516 	PS_ADD("starttime",	"%d",	0); /* XXX */
517 	PS_ADD("vsize",		"%u",	kp.ki_size);
518 	PS_ADD("rss",		"%u",	P2K(kp.ki_rssize));
519 	PS_ADD("rlim",		"%u",	0); /* XXX */
520 	PS_ADD("startcode",	"%u",	(unsigned)0);
521 	PS_ADD("endcode",	"%u",	0); /* XXX */
522 	PS_ADD("startstack",	"%u",	0); /* XXX */
523 	PS_ADD("esp",		"%u",	0); /* XXX */
524 	PS_ADD("eip",		"%u",	0); /* XXX */
525 	PS_ADD("signal",	"%d",	0); /* XXX */
526 	PS_ADD("blocked",	"%d",	0); /* XXX */
527 	PS_ADD("sigignore",	"%d",	0); /* XXX */
528 	PS_ADD("sigcatch",	"%d",	0); /* XXX */
529 	PS_ADD("wchan",		"%u",	0); /* XXX */
530 	PS_ADD("nswap",		"%lu",	(long unsigned)0); /* XXX */
531 	PS_ADD("cnswap",	"%lu",	(long unsigned)0); /* XXX */
532 	PS_ADD("exitsignal",	"%d",	0); /* XXX */
533 	PS_ADD("processor",	"%d",	0); /* XXX */
534 #undef PS_ADD
535 	sbuf_putc(sb, '\n');
536 
537 	return (0);
538 }
539 
540 /*
541  * Filler function for proc/pid/status
542  */
543 static int
544 linprocfs_doprocstatus(PFS_FILL_ARGS)
545 {
546 	struct kinfo_proc kp;
547 	char *state;
548 	segsz_t lsize;
549 	struct thread *td2;
550 	int i;
551 
552 	mtx_lock_spin(&sched_lock);
553 	td2 = FIRST_THREAD_IN_PROC(p); /* XXXKSE pretend only one thread */
554 
555 	if (P_SHOULDSTOP(p)) {
556 		state = "T (stopped)";
557 	} else {
558 		switch(p->p_state) {
559 		case PRS_NEW:
560 			state = "I (idle)";
561 			break;
562 		case PRS_NORMAL:
563 			if (p->p_flag & P_WEXIT) {
564 				state = "X (exiting)";
565 				break;
566 			}
567 			switch(td2->td_state) {
568 			case TDS_INHIBITED:
569 				state = "S (sleeping)";
570 				break;
571 			case TDS_RUNQ:
572 			case TDS_RUNNING:
573 				state = "R (running)";
574 				break;
575 			default:
576 				state = "? (unknown)";
577 				break;
578 			}
579 			break;
580 		case PRS_ZOMBIE:
581 			state = "Z (zombie)";
582 			break;
583 		default:
584 			state = "? (unknown)";
585 			break;
586 		}
587 	}
588 	mtx_unlock_spin(&sched_lock);
589 
590 	PROC_LOCK(p);
591 	fill_kinfo_proc(p, &kp);
592 	sbuf_printf(sb, "Name:\t%s\n",		p->p_comm); /* XXX escape */
593 	sbuf_printf(sb, "State:\t%s\n",		state);
594 
595 	/*
596 	 * Credentials
597 	 */
598 	sbuf_printf(sb, "Pid:\t%d\n",		p->p_pid);
599 	sbuf_printf(sb, "PPid:\t%d\n",		p->p_pptr ?
600 						p->p_pptr->p_pid : 0);
601 	sbuf_printf(sb, "Uid:\t%d %d %d %d\n",	p->p_ucred->cr_ruid,
602 						p->p_ucred->cr_uid,
603 						p->p_ucred->cr_svuid,
604 						/* FreeBSD doesn't have fsuid */
605 						p->p_ucred->cr_uid);
606 	sbuf_printf(sb, "Gid:\t%d %d %d %d\n",	p->p_ucred->cr_rgid,
607 						p->p_ucred->cr_gid,
608 						p->p_ucred->cr_svgid,
609 						/* FreeBSD doesn't have fsgid */
610 						p->p_ucred->cr_gid);
611 	sbuf_cat(sb, "Groups:\t");
612 	for (i = 0; i < p->p_ucred->cr_ngroups; i++)
613 		sbuf_printf(sb, "%d ",		p->p_ucred->cr_groups[i]);
614 	PROC_UNLOCK(p);
615 	sbuf_putc(sb, '\n');
616 
617 	/*
618 	 * Memory
619 	 *
620 	 * While our approximation of VmLib may not be accurate (I
621 	 * don't know of a simple way to verify it, and I'm not sure
622 	 * it has much meaning anyway), I believe it's good enough.
623 	 *
624 	 * The same code that could (I think) accurately compute VmLib
625 	 * could also compute VmLck, but I don't really care enough to
626 	 * implement it. Submissions are welcome.
627 	 */
628 	sbuf_printf(sb, "VmSize:\t%8u kB\n",	B2K(kp.ki_size));
629 	sbuf_printf(sb, "VmLck:\t%8u kB\n",	P2K(0)); /* XXX */
630 	sbuf_printf(sb, "VmRss:\t%8u kB\n",	P2K(kp.ki_rssize));
631 	sbuf_printf(sb, "VmData:\t%8u kB\n",	P2K(kp.ki_dsize));
632 	sbuf_printf(sb, "VmStk:\t%8u kB\n",	P2K(kp.ki_ssize));
633 	sbuf_printf(sb, "VmExe:\t%8u kB\n",	P2K(kp.ki_tsize));
634 	lsize = B2P(kp.ki_size) - kp.ki_dsize -
635 	    kp.ki_ssize - kp.ki_tsize - 1;
636 	sbuf_printf(sb, "VmLib:\t%8u kB\n",	P2K(lsize));
637 
638 	/*
639 	 * Signal masks
640 	 *
641 	 * We support up to 128 signals, while Linux supports 32,
642 	 * but we only define 32 (the same 32 as Linux, to boot), so
643 	 * just show the lower 32 bits of each mask. XXX hack.
644 	 *
645 	 * NB: on certain platforms (Sparc at least) Linux actually
646 	 * supports 64 signals, but this code is a long way from
647 	 * running on anything but i386, so ignore that for now.
648 	 */
649 	PROC_LOCK(p);
650 	sbuf_printf(sb, "SigPnd:\t%08x\n",	p->p_siglist.__bits[0]);
651 	/*
652 	 * I can't seem to find out where the signal mask is in
653 	 * relation to struct proc, so SigBlk is left unimplemented.
654 	 */
655 	sbuf_printf(sb, "SigBlk:\t%08x\n",	0); /* XXX */
656 	sbuf_printf(sb, "SigIgn:\t%08x\n",	p->p_sigignore.__bits[0]);
657 	sbuf_printf(sb, "SigCgt:\t%08x\n",	p->p_sigcatch.__bits[0]);
658 	PROC_UNLOCK(p);
659 
660 	/*
661 	 * Linux also prints the capability masks, but we don't have
662 	 * capabilities yet, and when we do get them they're likely to
663 	 * be meaningless to Linux programs, so we lie. XXX
664 	 */
665 	sbuf_printf(sb, "CapInh:\t%016x\n",	0);
666 	sbuf_printf(sb, "CapPrm:\t%016x\n",	0);
667 	sbuf_printf(sb, "CapEff:\t%016x\n",	0);
668 
669 	return (0);
670 }
671 
672 /*
673  * Filler function for proc/pid/cmdline
674  */
675 static int
676 linprocfs_doproccmdline(PFS_FILL_ARGS)
677 {
678 	struct ps_strings pstr;
679 	int error, i;
680 
681 	/*
682 	 * If we are using the ps/cmdline caching, use that.  Otherwise
683 	 * revert back to the old way which only implements full cmdline
684 	 * for the currept process and just p->p_comm for all other
685 	 * processes.
686 	 * Note that if the argv is no longer available, we deliberately
687 	 * don't fall back on p->p_comm or return an error: the authentic
688 	 * Linux behaviour is to return zero-length in this case.
689 	 */
690 
691 	PROC_LOCK(p);
692 	if (p->p_args && (ps_argsopen || !p_cansee(td, p))) {
693 		sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length);
694 		PROC_UNLOCK(p);
695 	} else if (p != td->td_proc) {
696 		PROC_UNLOCK(p);
697 		sbuf_printf(sb, "%.*s", MAXCOMLEN, p->p_comm);
698 	} else {
699 		PROC_UNLOCK(p);
700 		error = copyin((void *)p->p_sysent->sv_psstrings, &pstr,
701 		    sizeof(pstr));
702 		if (error)
703 			return (error);
704 		for (i = 0; i < pstr.ps_nargvstr; i++) {
705 			sbuf_copyin(sb, pstr.ps_argvstr[i], 0);
706 			sbuf_printf(sb, "%c", '\0');
707 		}
708 	}
709 
710 	return (0);
711 }
712 
713 /*
714  * Filler function for proc/net/dev
715  */
716 static int
717 linprocfs_donetdev(PFS_FILL_ARGS)
718 {
719 	char ifname[16]; /* XXX LINUX_IFNAMSIZ */
720 	struct ifnet *ifp;
721 
722 	sbuf_printf(sb, "%6s|%58s|%s\n%6s|%58s|%58s\n",
723 	    "Inter-", "   Receive", "  Transmit", " face",
724 	    "bytes    packets errs drop fifo frame compressed",
725 	    "bytes    packets errs drop fifo frame compressed");
726 
727 	IFNET_RLOCK();
728 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
729 		linux_ifname(ifp, ifname, sizeof ifname);
730 			sbuf_printf(sb, "%6.6s:", ifname);
731 		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu ",
732 		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
733 		sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
734 		    0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
735 	}
736 	IFNET_RUNLOCK();
737 
738 	return (0);
739 }
740 
741 #if 0
742 extern struct cdevsw *cdevsw[];
743 
744 /*
745  * Filler function for proc/devices
746  */
747 static int
748 linprocfs_dodevices(PFS_FILL_ARGS)
749 {
750 	int i;
751 
752 	sbuf_printf(sb, "Character devices:\n");
753 
754 	for (i = 0; i < NUMCDEVSW; i++)
755 		if (cdevsw[i] != NULL)
756 			sbuf_printf(sb, "%3d %s\n", i, cdevsw[i]->d_name);
757 
758 	sbuf_printf(sb, "\nBlock devices:\n");
759 
760 	return (0);
761 }
762 #endif
763 
764 /*
765  * Filler function for proc/cmdline
766  */
767 static int
768 linprocfs_docmdline(PFS_FILL_ARGS)
769 {
770 	sbuf_printf(sb, "BOOT_IMAGE=%s", kernelname);
771 	sbuf_printf(sb, " ro root=302\n");
772 	return (0);
773 }
774 
775 #if 0
776 /*
777  * Filler function for proc/modules
778  */
779 static int
780 linprocfs_domodules(PFS_FILL_ARGS)
781 {
782 	struct linker_file *lf;
783 
784 	TAILQ_FOREACH(lf, &linker_files, link) {
785 		sbuf_printf(sb, "%-20s%8lu%4d\n", lf->filename,
786 		    (unsigned long)lf->size, lf->refs);
787 	}
788 	return (0);
789 }
790 #endif
791 
792 /*
793  * Constructor
794  */
795 static int
796 linprocfs_init(PFS_INIT_ARGS)
797 {
798 	struct pfs_node *root;
799 	struct pfs_node *dir;
800 
801 	root = pi->pi_root;
802 
803 #define PFS_CREATE_FILE(name) \
804 	pfs_create_file(root, #name, &linprocfs_do##name, NULL, NULL, PFS_RD)
805 	PFS_CREATE_FILE(cmdline);
806 	PFS_CREATE_FILE(cpuinfo);
807 #if 0
808 	PFS_CREATE_FILE(devices);
809 #endif
810 	PFS_CREATE_FILE(loadavg);
811 	PFS_CREATE_FILE(meminfo);
812 #if 0
813 	PFS_CREATE_FILE(modules);
814 #endif
815 	PFS_CREATE_FILE(mtab);
816 	PFS_CREATE_FILE(stat);
817 	PFS_CREATE_FILE(uptime);
818 	PFS_CREATE_FILE(version);
819 #undef PFS_CREATE_FILE
820 	pfs_create_link(root, "self", &procfs_docurproc,
821 	    NULL, NULL, 0);
822 
823 	dir = pfs_create_dir(root, "net", NULL, NULL, 0);
824 	pfs_create_file(dir, "dev", &linprocfs_donetdev,
825 	    NULL, NULL, PFS_RD);
826 
827 	dir = pfs_create_dir(root, "pid", NULL, NULL, PFS_PROCDEP);
828 	pfs_create_file(dir, "cmdline", &linprocfs_doproccmdline,
829 	    NULL, NULL, PFS_RD);
830 	pfs_create_link(dir, "exe", &procfs_doprocfile,
831 	    NULL, &procfs_notsystem, 0);
832 	pfs_create_file(dir, "mem", &procfs_doprocmem,
833 	    &procfs_attr, &procfs_candebug, PFS_RDWR|PFS_RAW);
834 	pfs_create_file(dir, "stat", &linprocfs_doprocstat,
835 	    NULL, NULL, PFS_RD);
836 	pfs_create_file(dir, "status", &linprocfs_doprocstatus,
837 	    NULL, NULL, PFS_RD);
838 
839 	return (0);
840 }
841 
842 /*
843  * Destructor
844  */
845 static int
846 linprocfs_uninit(PFS_INIT_ARGS)
847 {
848 
849 	/* nothing to do, pseudofs will GC */
850 	return (0);
851 }
852 
853 PSEUDOFS(linprocfs, 1);
854 MODULE_DEPEND(linprocfs, linux, 1, 1, 1);
855 MODULE_DEPEND(linprocfs, procfs, 1, 1, 1);
856