diff options
Diffstat (limited to 'sys/compat/linprocfs/linprocfs.c')
-rw-r--r-- | sys/compat/linprocfs/linprocfs.c | 1456 |
1 files changed, 1456 insertions, 0 deletions
diff --git a/sys/compat/linprocfs/linprocfs.c b/sys/compat/linprocfs/linprocfs.c new file mode 100644 index 0000000..28b683a --- /dev/null +++ b/sys/compat/linprocfs/linprocfs.c @@ -0,0 +1,1456 @@ +/*- + * Copyright (c) 2000 Dag-Erling Coïdan Smørgrav + * Copyright (c) 1999 Pierre Beyssac + * Copyright (c) 1993 Jan-Simon Pendry + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)procfs_status.c 8.4 (Berkeley) 6/15/94 + */ + +#include "opt_compat.h" + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/blist.h> +#include <sys/conf.h> +#include <sys/exec.h> +#include <sys/fcntl.h> +#include <sys/filedesc.h> +#include <sys/jail.h> +#include <sys/kernel.h> +#include <sys/linker.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mount.h> +#include <sys/msg.h> +#include <sys/mutex.h> +#include <sys/namei.h> +#include <sys/proc.h> +#include <sys/ptrace.h> +#include <sys/resourcevar.h> +#include <sys/sbuf.h> +#include <sys/sem.h> +#include <sys/smp.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/tty.h> +#include <sys/user.h> +#include <sys/vmmeter.h> +#include <sys/vnode.h> +#include <sys/bus.h> + +#include <net/if.h> +#include <net/vnet.h> + +#include <vm/vm.h> +#include <vm/vm_extern.h> +#include <vm/pmap.h> +#include <vm/vm_map.h> +#include <vm/vm_param.h> +#include <vm/vm_object.h> +#include <vm/swap_pager.h> + +#include <machine/clock.h> + +#include <geom/geom.h> +#include <geom/geom_int.h> + +#if defined(__i386__) || defined(__amd64__) +#include <machine/cputypes.h> +#include <machine/md_var.h> +#endif /* __i386__ || __amd64__ */ + +#ifdef COMPAT_FREEBSD32 +#include <compat/freebsd32/freebsd32_util.h> +#endif + +#include <compat/linux/linux_ioctl.h> +#include <compat/linux/linux_mib.h> +#include <compat/linux/linux_misc.h> +#include <compat/linux/linux_util.h> +#include <fs/pseudofs/pseudofs.h> +#include <fs/procfs/procfs.h> + +/* + * Various conversion macros + */ +#define T2J(x) ((long)(((x) * 100ULL) / (stathz ? stathz : hz))) /* ticks to jiffies */ +#define T2CS(x) ((unsigned long)(((x) * 100ULL) / (stathz ? stathz : hz))) /* ticks to centiseconds */ +#define T2S(x) ((x) / (stathz ? stathz : hz)) /* ticks to seconds */ +#define B2K(x) ((x) >> 10) /* bytes to kbytes */ +#define B2P(x) ((x) >> PAGE_SHIFT) /* bytes to pages */ +#define P2B(x) ((x) << PAGE_SHIFT) /* pages to bytes */ +#define P2K(x) ((x) << (PAGE_SHIFT - 10)) /* pages to kbytes */ +#define TV2J(x) ((x)->tv_sec * 100UL + (x)->tv_usec / 10000) + +/** + * @brief Mapping of ki_stat in struct kinfo_proc to the linux state + * + * The linux procfs state field displays one of the characters RSDZTW to + * denote running, sleeping in an interruptible wait, waiting in an + * uninterruptible disk sleep, a zombie process, process is being traced + * or stopped, or process is paging respectively. + * + * Our struct kinfo_proc contains the variable ki_stat which contains a + * value out of SIDL, SRUN, SSLEEP, SSTOP, SZOMB, SWAIT and SLOCK. + * + * This character array is used with ki_stati-1 as an index and tries to + * map our states to suitable linux states. + */ +static char linux_state[] = "RRSTZDD"; + +/* + * Filler function for proc/meminfo + */ +static int +linprocfs_domeminfo(PFS_FILL_ARGS) +{ + unsigned long memtotal; /* total memory in bytes */ + unsigned long memused; /* used memory in bytes */ + unsigned long memfree; /* free memory in bytes */ + unsigned long memshared; /* shared memory ??? */ + unsigned long buffers, cached; /* buffer / cache memory ??? */ + unsigned long long swaptotal; /* total swap space in bytes */ + unsigned long long swapused; /* used swap space in bytes */ + unsigned long long swapfree; /* free swap space in bytes */ + vm_object_t object; + int i, j; + + memtotal = physmem * PAGE_SIZE; + /* + * The correct thing here would be: + * + memfree = cnt.v_free_count * PAGE_SIZE; + memused = memtotal - memfree; + * + * but it might mislead linux binaries into thinking there + * is very little memory left, so we cheat and tell them that + * all memory that isn't wired down is free. + */ + memused = cnt.v_wire_count * PAGE_SIZE; + memfree = memtotal - memused; + swap_pager_status(&i, &j); + swaptotal = (unsigned long long)i * PAGE_SIZE; + swapused = (unsigned long long)j * PAGE_SIZE; + swapfree = swaptotal - swapused; + memshared = 0; + mtx_lock(&vm_object_list_mtx); + TAILQ_FOREACH(object, &vm_object_list, object_list) + if (object->shadow_count > 1) + memshared += object->resident_page_count; + mtx_unlock(&vm_object_list_mtx); + memshared *= PAGE_SIZE; + /* + * We'd love to be able to write: + * + buffers = bufspace; + * + * but bufspace is internal to vfs_bio.c and we don't feel + * like unstaticizing it just for linprocfs's sake. + */ + buffers = 0; + cached = cnt.v_cache_count * PAGE_SIZE; + + sbuf_printf(sb, + " total: used: free: shared: buffers: cached:\n" + "Mem: %lu %lu %lu %lu %lu %lu\n" + "Swap: %llu %llu %llu\n" + "MemTotal: %9lu kB\n" + "MemFree: %9lu kB\n" + "MemShared:%9lu kB\n" + "Buffers: %9lu kB\n" + "Cached: %9lu kB\n" + "SwapTotal:%9llu kB\n" + "SwapFree: %9llu kB\n", + memtotal, memused, memfree, memshared, buffers, cached, + swaptotal, swapused, swapfree, + B2K(memtotal), B2K(memfree), + B2K(memshared), B2K(buffers), B2K(cached), + B2K(swaptotal), B2K(swapfree)); + + return (0); +} + +#if defined(__i386__) || defined(__amd64__) +/* + * Filler function for proc/cpuinfo (i386 & amd64 version) + */ +static int +linprocfs_docpuinfo(PFS_FILL_ARGS) +{ + int hw_model[2]; + char model[128]; + uint64_t freq; + size_t size; + int class, fqmhz, fqkhz; + int i; + + /* + * We default the flags to include all non-conflicting flags, + * and the Intel versions of conflicting flags. + */ + static char *flags[] = { + "fpu", "vme", "de", "pse", "tsc", + "msr", "pae", "mce", "cx8", "apic", + "sep", "sep", "mtrr", "pge", "mca", + "cmov", "pat", "pse36", "pn", "b19", + "b20", "b21", "mmxext", "mmx", "fxsr", + "xmm", "sse2", "b27", "b28", "b29", + "3dnowext", "3dnow" + }; + + switch (cpu_class) { +#ifdef __i386__ + case CPUCLASS_286: + class = 2; + break; + case CPUCLASS_386: + class = 3; + break; + case CPUCLASS_486: + class = 4; + break; + case CPUCLASS_586: + class = 5; + break; + case CPUCLASS_686: + class = 6; + break; + default: + class = 0; + break; +#else /* __amd64__ */ + default: + class = 15; + break; +#endif + } + + hw_model[0] = CTL_HW; + hw_model[1] = HW_MODEL; + model[0] = '\0'; + size = sizeof(model); + if (kernel_sysctl(td, hw_model, 2, &model, &size, 0, 0, 0, 0) != 0) + strcpy(model, "unknown"); + for (i = 0; i < mp_ncpus; ++i) { + sbuf_printf(sb, + "processor\t: %d\n" + "vendor_id\t: %.20s\n" + "cpu family\t: %u\n" + "model\t\t: %u\n" + "model name\t: %s\n" + "stepping\t: %u\n\n", + i, cpu_vendor, CPUID_TO_FAMILY(cpu_id), + CPUID_TO_MODEL(cpu_id), model, cpu_id & CPUID_STEPPING); + /* XXX per-cpu vendor / class / model / id? */ + } + + sbuf_cat(sb, "flags\t\t:"); + +#ifdef __i386__ + switch (cpu_vendor_id) { + case CPU_VENDOR_AMD: + if (class < 6) + flags[16] = "fcmov"; + break; + case CPU_VENDOR_CYRIX: + flags[24] = "cxmmx"; + break; + } +#endif + + for (i = 0; i < 32; i++) + if (cpu_feature & (1 << i)) + sbuf_printf(sb, " %s", flags[i]); + sbuf_cat(sb, "\n"); + freq = atomic_load_acq_64(&tsc_freq); + if (freq != 0) { + fqmhz = (freq + 4999) / 1000000; + fqkhz = ((freq + 4999) / 10000) % 100; + sbuf_printf(sb, + "cpu MHz\t\t: %d.%02d\n" + "bogomips\t: %d.%02d\n", + fqmhz, fqkhz, fqmhz, fqkhz); + } + + return (0); +} +#endif /* __i386__ || __amd64__ */ + +/* + * Filler function for proc/mtab + * + * This file doesn't exist in Linux' procfs, but is included here so + * users can symlink /compat/linux/etc/mtab to /proc/mtab + */ +static int +linprocfs_domtab(PFS_FILL_ARGS) +{ + struct nameidata nd; + struct mount *mp; + const char *lep; + char *dlep, *flep, *mntto, *mntfrom, *fstype; + size_t lep_len; + int error; + + /* resolve symlinks etc. in the emulation tree prefix */ + NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, linux_emul_path, td); + flep = NULL; + error = namei(&nd); + lep = linux_emul_path; + if (error == 0) { + if (vn_fullpath(td, nd.ni_vp, &dlep, &flep) == 0) + lep = dlep; + vrele(nd.ni_vp); + } + lep_len = strlen(lep); + + mtx_lock(&mountlist_mtx); + error = 0; + TAILQ_FOREACH(mp, &mountlist, mnt_list) { + /* determine device name */ + mntfrom = mp->mnt_stat.f_mntfromname; + + /* determine mount point */ + mntto = mp->mnt_stat.f_mntonname; + if (strncmp(mntto, lep, lep_len) == 0 && + mntto[lep_len] == '/') + mntto += lep_len; + + /* determine fs type */ + fstype = mp->mnt_stat.f_fstypename; + if (strcmp(fstype, pn->pn_info->pi_name) == 0) + mntfrom = fstype = "proc"; + else if (strcmp(fstype, "procfs") == 0) + continue; + + if (strcmp(fstype, "linsysfs") == 0) { + sbuf_printf(sb, "/sys %s sysfs %s", mntto, + mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw"); + } else { + /* For Linux msdosfs is called vfat */ + if (strcmp(fstype, "msdosfs") == 0) + fstype = "vfat"; + sbuf_printf(sb, "%s %s %s %s", mntfrom, mntto, fstype, + mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw"); + } +#define ADD_OPTION(opt, name) \ + if (mp->mnt_stat.f_flags & (opt)) sbuf_printf(sb, "," name); + ADD_OPTION(MNT_SYNCHRONOUS, "sync"); + ADD_OPTION(MNT_NOEXEC, "noexec"); + ADD_OPTION(MNT_NOSUID, "nosuid"); + ADD_OPTION(MNT_UNION, "union"); + ADD_OPTION(MNT_ASYNC, "async"); + ADD_OPTION(MNT_SUIDDIR, "suiddir"); + ADD_OPTION(MNT_NOSYMFOLLOW, "nosymfollow"); + ADD_OPTION(MNT_NOATIME, "noatime"); +#undef ADD_OPTION + /* a real Linux mtab will also show NFS options */ + sbuf_printf(sb, " 0 0\n"); + } + mtx_unlock(&mountlist_mtx); + free(flep, M_TEMP); + return (error); +} + +/* + * Filler function for proc/partitions + * + */ +static int +linprocfs_dopartitions(PFS_FILL_ARGS) +{ + struct g_class *cp; + struct g_geom *gp; + struct g_provider *pp; + struct nameidata nd; + const char *lep; + char *dlep, *flep; + size_t lep_len; + int error; + int major, minor; + + /* resolve symlinks etc. in the emulation tree prefix */ + NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, linux_emul_path, td); + flep = NULL; + error = namei(&nd); + lep = linux_emul_path; + if (error == 0) { + if (vn_fullpath(td, nd.ni_vp, &dlep, &flep) == 0) + lep = dlep; + vrele(nd.ni_vp); + } + lep_len = strlen(lep); + + g_topology_lock(); + error = 0; + sbuf_printf(sb, "major minor #blocks name rio rmerge rsect " + "ruse wio wmerge wsect wuse running use aveq\n"); + + LIST_FOREACH(cp, &g_classes, class) { + if (strcmp(cp->name, "DISK") == 0 || + strcmp(cp->name, "PART") == 0) + LIST_FOREACH(gp, &cp->geom, geom) { + LIST_FOREACH(pp, &gp->provider, provider) { + if (linux_driver_get_major_minor( + pp->name, &major, &minor) != 0) { + major = 0; + minor = 0; + } + sbuf_printf(sb, "%d %d %lld %s " + "%d %d %d %d %d " + "%d %d %d %d %d %d\n", + major, minor, + (long long)pp->mediasize, pp->name, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0); + } + } + } + g_topology_unlock(); + + free(flep, M_TEMP); + return (error); +} + + +/* + * Filler function for proc/stat + */ +static int +linprocfs_dostat(PFS_FILL_ARGS) +{ + struct pcpu *pcpu; + long cp_time[CPUSTATES]; + long *cp; + int i; + + read_cpu_time(cp_time); + sbuf_printf(sb, "cpu %ld %ld %ld %ld\n", + T2J(cp_time[CP_USER]), + T2J(cp_time[CP_NICE]), + T2J(cp_time[CP_SYS] /*+ cp_time[CP_INTR]*/), + T2J(cp_time[CP_IDLE])); + CPU_FOREACH(i) { + pcpu = pcpu_find(i); + cp = pcpu->pc_cp_time; + sbuf_printf(sb, "cpu%d %ld %ld %ld %ld\n", i, + T2J(cp[CP_USER]), + T2J(cp[CP_NICE]), + T2J(cp[CP_SYS] /*+ cp[CP_INTR]*/), + T2J(cp[CP_IDLE])); + } + sbuf_printf(sb, + "disk 0 0 0 0\n" + "page %u %u\n" + "swap %u %u\n" + "intr %u\n" + "ctxt %u\n" + "btime %lld\n", + cnt.v_vnodepgsin, + cnt.v_vnodepgsout, + cnt.v_swappgsin, + cnt.v_swappgsout, + cnt.v_intr, + cnt.v_swtch, + (long long)boottime.tv_sec); + return (0); +} + +static int +linprocfs_doswaps(PFS_FILL_ARGS) +{ + struct xswdev xsw; + uintmax_t total, used; + int n; + char devname[SPECNAMELEN + 1]; + + sbuf_printf(sb, "Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n"); + mtx_lock(&Giant); + for (n = 0; ; n++) { + if (swap_dev_info(n, &xsw, devname, sizeof(devname)) != 0) + break; + total = (uintmax_t)xsw.xsw_nblks * PAGE_SIZE / 1024; + used = (uintmax_t)xsw.xsw_used * PAGE_SIZE / 1024; + + /* + * The space and not tab after the device name is on + * purpose. Linux does so. + */ + sbuf_printf(sb, "/dev/%-34s unknown\t\t%jd\t%jd\t-1\n", + devname, total, used); + } + mtx_unlock(&Giant); + return (0); +} + +/* + * Filler function for proc/uptime + */ +static int +linprocfs_douptime(PFS_FILL_ARGS) +{ + long cp_time[CPUSTATES]; + struct timeval tv; + + getmicrouptime(&tv); + read_cpu_time(cp_time); + sbuf_printf(sb, "%lld.%02ld %ld.%02lu\n", + (long long)tv.tv_sec, tv.tv_usec / 10000, + T2S(cp_time[CP_IDLE] / mp_ncpus), + T2CS(cp_time[CP_IDLE] / mp_ncpus) % 100); + return (0); +} + +/* + * Get OS build date + */ +static void +linprocfs_osbuild(struct thread *td, struct sbuf *sb) +{ +#if 0 + char osbuild[256]; + char *cp1, *cp2; + + strncpy(osbuild, version, 256); + osbuild[255] = '\0'; + cp1 = strstr(osbuild, "\n"); + cp2 = strstr(osbuild, ":"); + if (cp1 && cp2) { + *cp1 = *cp2 = '\0'; + cp1 = strstr(osbuild, "#"); + } else + cp1 = NULL; + if (cp1) + sbuf_printf(sb, "%s%s", cp1, cp2 + 1); + else +#endif + sbuf_cat(sb, "#4 Sun Dec 18 04:30:00 CET 1977"); +} + +/* + * Get OS builder + */ +static void +linprocfs_osbuilder(struct thread *td, struct sbuf *sb) +{ +#if 0 + char builder[256]; + char *cp; + + cp = strstr(version, "\n "); + if (cp) { + strncpy(builder, cp + 5, 256); + builder[255] = '\0'; + cp = strstr(builder, ":"); + if (cp) + *cp = '\0'; + } + if (cp) + sbuf_cat(sb, builder); + else +#endif + sbuf_cat(sb, "des@freebsd.org"); +} + +/* + * Filler function for proc/version + */ +static int +linprocfs_doversion(PFS_FILL_ARGS) +{ + char osname[LINUX_MAX_UTSNAME]; + char osrelease[LINUX_MAX_UTSNAME]; + + linux_get_osname(td, osname); + linux_get_osrelease(td, osrelease); + sbuf_printf(sb, "%s version %s (", osname, osrelease); + linprocfs_osbuilder(td, sb); + sbuf_cat(sb, ") (gcc version " __VERSION__ ") "); + linprocfs_osbuild(td, sb); + sbuf_cat(sb, "\n"); + + return (0); +} + +/* + * Filler function for proc/loadavg + */ +static int +linprocfs_doloadavg(PFS_FILL_ARGS) +{ + + sbuf_printf(sb, + "%d.%02d %d.%02d %d.%02d %d/%d %d\n", + (int)(averunnable.ldavg[0] / averunnable.fscale), + (int)(averunnable.ldavg[0] * 100 / averunnable.fscale % 100), + (int)(averunnable.ldavg[1] / averunnable.fscale), + (int)(averunnable.ldavg[1] * 100 / averunnable.fscale % 100), + (int)(averunnable.ldavg[2] / averunnable.fscale), + (int)(averunnable.ldavg[2] * 100 / averunnable.fscale % 100), + 1, /* number of running tasks */ + nprocs, /* number of tasks */ + lastpid /* the last pid */ + ); + return (0); +} + +/* + * Filler function for proc/pid/stat + */ +static int +linprocfs_doprocstat(PFS_FILL_ARGS) +{ + struct kinfo_proc kp; + char state; + static int ratelimit = 0; + vm_offset_t startcode, startdata; + + PROC_LOCK(p); + fill_kinfo_proc(p, &kp); + if (p->p_vmspace) { + startcode = (vm_offset_t)p->p_vmspace->vm_taddr; + startdata = (vm_offset_t)p->p_vmspace->vm_daddr; + } else { + startcode = 0; + startdata = 0; + }; + sbuf_printf(sb, "%d", p->p_pid); +#define PS_ADD(name, fmt, arg) sbuf_printf(sb, " " fmt, arg) + PS_ADD("comm", "(%s)", p->p_comm); + if (kp.ki_stat > sizeof(linux_state)) { + state = 'R'; + + if (ratelimit == 0) { + printf("linprocfs: don't know how to handle unknown FreeBSD state %d/%zd, mapping to R\n", + kp.ki_stat, sizeof(linux_state)); + ++ratelimit; + } + } else + state = linux_state[kp.ki_stat - 1]; + PS_ADD("state", "%c", state); + PS_ADD("ppid", "%d", p->p_pptr ? p->p_pptr->p_pid : 0); + PS_ADD("pgrp", "%d", p->p_pgid); + PS_ADD("session", "%d", p->p_session->s_sid); + PROC_UNLOCK(p); + PS_ADD("tty", "%d", kp.ki_tdev); + PS_ADD("tpgid", "%d", kp.ki_tpgid); + PS_ADD("flags", "%u", 0); /* XXX */ + PS_ADD("minflt", "%lu", kp.ki_rusage.ru_minflt); + PS_ADD("cminflt", "%lu", kp.ki_rusage_ch.ru_minflt); + PS_ADD("majflt", "%lu", kp.ki_rusage.ru_majflt); + PS_ADD("cmajflt", "%lu", kp.ki_rusage_ch.ru_majflt); + PS_ADD("utime", "%ld", TV2J(&kp.ki_rusage.ru_utime)); + PS_ADD("stime", "%ld", TV2J(&kp.ki_rusage.ru_stime)); + PS_ADD("cutime", "%ld", TV2J(&kp.ki_rusage_ch.ru_utime)); + PS_ADD("cstime", "%ld", TV2J(&kp.ki_rusage_ch.ru_stime)); + PS_ADD("priority", "%d", kp.ki_pri.pri_user); + PS_ADD("nice", "%d", kp.ki_nice); /* 19 (nicest) to -19 */ + PS_ADD("0", "%d", 0); /* removed field */ + PS_ADD("itrealvalue", "%d", 0); /* XXX */ + PS_ADD("starttime", "%lu", TV2J(&kp.ki_start) - TV2J(&boottime)); + PS_ADD("vsize", "%ju", P2K((uintmax_t)kp.ki_size)); + PS_ADD("rss", "%ju", (uintmax_t)kp.ki_rssize); + PS_ADD("rlim", "%lu", kp.ki_rusage.ru_maxrss); + PS_ADD("startcode", "%ju", (uintmax_t)startcode); + PS_ADD("endcode", "%ju", (uintmax_t)startdata); + PS_ADD("startstack", "%u", 0); /* XXX */ + PS_ADD("kstkesp", "%u", 0); /* XXX */ + PS_ADD("kstkeip", "%u", 0); /* XXX */ + PS_ADD("signal", "%u", 0); /* XXX */ + PS_ADD("blocked", "%u", 0); /* XXX */ + PS_ADD("sigignore", "%u", 0); /* XXX */ + PS_ADD("sigcatch", "%u", 0); /* XXX */ + PS_ADD("wchan", "%u", 0); /* XXX */ + PS_ADD("nswap", "%lu", kp.ki_rusage.ru_nswap); + PS_ADD("cnswap", "%lu", kp.ki_rusage_ch.ru_nswap); + PS_ADD("exitsignal", "%d", 0); /* XXX */ + PS_ADD("processor", "%u", kp.ki_lastcpu); + PS_ADD("rt_priority", "%u", 0); /* XXX */ /* >= 2.5.19 */ + PS_ADD("policy", "%u", kp.ki_pri.pri_class); /* >= 2.5.19 */ +#undef PS_ADD + sbuf_putc(sb, '\n'); + + return (0); +} + +/* + * Filler function for proc/pid/statm + */ +static int +linprocfs_doprocstatm(PFS_FILL_ARGS) +{ + struct kinfo_proc kp; + segsz_t lsize; + + PROC_LOCK(p); + fill_kinfo_proc(p, &kp); + PROC_UNLOCK(p); + + /* + * See comments in linprocfs_doprocstatus() regarding the + * computation of lsize. + */ + /* size resident share trs drs lrs dt */ + sbuf_printf(sb, "%ju ", B2P((uintmax_t)kp.ki_size)); + sbuf_printf(sb, "%ju ", (uintmax_t)kp.ki_rssize); + sbuf_printf(sb, "%ju ", (uintmax_t)0); /* XXX */ + sbuf_printf(sb, "%ju ", (uintmax_t)kp.ki_tsize); + sbuf_printf(sb, "%ju ", (uintmax_t)(kp.ki_dsize + kp.ki_ssize)); + lsize = B2P(kp.ki_size) - kp.ki_dsize - + kp.ki_ssize - kp.ki_tsize - 1; + sbuf_printf(sb, "%ju ", (uintmax_t)lsize); + sbuf_printf(sb, "%ju\n", (uintmax_t)0); /* XXX */ + + return (0); +} + +/* + * Filler function for proc/pid/status + */ +static int +linprocfs_doprocstatus(PFS_FILL_ARGS) +{ + struct kinfo_proc kp; + char *state; + segsz_t lsize; + struct thread *td2; + struct sigacts *ps; + int i; + + PROC_LOCK(p); + td2 = FIRST_THREAD_IN_PROC(p); /* XXXKSE pretend only one thread */ + + if (P_SHOULDSTOP(p)) { + state = "T (stopped)"; + } else { + switch(p->p_state) { + case PRS_NEW: + state = "I (idle)"; + break; + case PRS_NORMAL: + if (p->p_flag & P_WEXIT) { + state = "X (exiting)"; + break; + } + switch(td2->td_state) { + case TDS_INHIBITED: + state = "S (sleeping)"; + break; + case TDS_RUNQ: + case TDS_RUNNING: + state = "R (running)"; + break; + default: + state = "? (unknown)"; + break; + } + break; + case PRS_ZOMBIE: + state = "Z (zombie)"; + break; + default: + state = "? (unknown)"; + break; + } + } + + fill_kinfo_proc(p, &kp); + sbuf_printf(sb, "Name:\t%s\n", p->p_comm); /* XXX escape */ + sbuf_printf(sb, "State:\t%s\n", state); + + /* + * Credentials + */ + sbuf_printf(sb, "Pid:\t%d\n", p->p_pid); + sbuf_printf(sb, "PPid:\t%d\n", p->p_pptr ? + p->p_pptr->p_pid : 0); + sbuf_printf(sb, "Uid:\t%d %d %d %d\n", p->p_ucred->cr_ruid, + p->p_ucred->cr_uid, + p->p_ucred->cr_svuid, + /* FreeBSD doesn't have fsuid */ + p->p_ucred->cr_uid); + sbuf_printf(sb, "Gid:\t%d %d %d %d\n", p->p_ucred->cr_rgid, + p->p_ucred->cr_gid, + p->p_ucred->cr_svgid, + /* FreeBSD doesn't have fsgid */ + p->p_ucred->cr_gid); + sbuf_cat(sb, "Groups:\t"); + for (i = 0; i < p->p_ucred->cr_ngroups; i++) + sbuf_printf(sb, "%d ", p->p_ucred->cr_groups[i]); + PROC_UNLOCK(p); + sbuf_putc(sb, '\n'); + + /* + * Memory + * + * While our approximation of VmLib may not be accurate (I + * don't know of a simple way to verify it, and I'm not sure + * it has much meaning anyway), I believe it's good enough. + * + * The same code that could (I think) accurately compute VmLib + * could also compute VmLck, but I don't really care enough to + * implement it. Submissions are welcome. + */ + sbuf_printf(sb, "VmSize:\t%8ju kB\n", B2K((uintmax_t)kp.ki_size)); + sbuf_printf(sb, "VmLck:\t%8u kB\n", P2K(0)); /* XXX */ + sbuf_printf(sb, "VmRSS:\t%8ju kB\n", P2K((uintmax_t)kp.ki_rssize)); + sbuf_printf(sb, "VmData:\t%8ju kB\n", P2K((uintmax_t)kp.ki_dsize)); + sbuf_printf(sb, "VmStk:\t%8ju kB\n", P2K((uintmax_t)kp.ki_ssize)); + sbuf_printf(sb, "VmExe:\t%8ju kB\n", P2K((uintmax_t)kp.ki_tsize)); + lsize = B2P(kp.ki_size) - kp.ki_dsize - + kp.ki_ssize - kp.ki_tsize - 1; + sbuf_printf(sb, "VmLib:\t%8ju kB\n", P2K((uintmax_t)lsize)); + + /* + * Signal masks + * + * We support up to 128 signals, while Linux supports 32, + * but we only define 32 (the same 32 as Linux, to boot), so + * just show the lower 32 bits of each mask. XXX hack. + * + * NB: on certain platforms (Sparc at least) Linux actually + * supports 64 signals, but this code is a long way from + * running on anything but i386, so ignore that for now. + */ + PROC_LOCK(p); + sbuf_printf(sb, "SigPnd:\t%08x\n", p->p_siglist.__bits[0]); + /* + * I can't seem to find out where the signal mask is in + * relation to struct proc, so SigBlk is left unimplemented. + */ + sbuf_printf(sb, "SigBlk:\t%08x\n", 0); /* XXX */ + ps = p->p_sigacts; + mtx_lock(&ps->ps_mtx); + sbuf_printf(sb, "SigIgn:\t%08x\n", ps->ps_sigignore.__bits[0]); + sbuf_printf(sb, "SigCgt:\t%08x\n", ps->ps_sigcatch.__bits[0]); + mtx_unlock(&ps->ps_mtx); + PROC_UNLOCK(p); + + /* + * Linux also prints the capability masks, but we don't have + * capabilities yet, and when we do get them they're likely to + * be meaningless to Linux programs, so we lie. XXX + */ + sbuf_printf(sb, "CapInh:\t%016x\n", 0); + sbuf_printf(sb, "CapPrm:\t%016x\n", 0); + sbuf_printf(sb, "CapEff:\t%016x\n", 0); + + return (0); +} + + +/* + * Filler function for proc/pid/cwd + */ +static int +linprocfs_doproccwd(PFS_FILL_ARGS) +{ + char *fullpath = "unknown"; + char *freepath = NULL; + + vn_fullpath(td, p->p_fd->fd_cdir, &fullpath, &freepath); + sbuf_printf(sb, "%s", fullpath); + if (freepath) + free(freepath, M_TEMP); + return (0); +} + +/* + * Filler function for proc/pid/root + */ +static int +linprocfs_doprocroot(PFS_FILL_ARGS) +{ + struct vnode *rvp; + char *fullpath = "unknown"; + char *freepath = NULL; + + rvp = jailed(p->p_ucred) ? p->p_fd->fd_jdir : p->p_fd->fd_rdir; + vn_fullpath(td, rvp, &fullpath, &freepath); + sbuf_printf(sb, "%s", fullpath); + if (freepath) + free(freepath, M_TEMP); + return (0); +} + +/* + * Filler function for proc/pid/cmdline + */ +static int +linprocfs_doproccmdline(PFS_FILL_ARGS) +{ + int ret; + + PROC_LOCK(p); + if ((ret = p_cansee(td, p)) != 0) { + PROC_UNLOCK(p); + return (ret); + } + + /* + * Mimic linux behavior and pass only processes with usermode + * address space as valid. Return zero silently otherwize. + */ + if (p->p_vmspace == &vmspace0) { + PROC_UNLOCK(p); + return (0); + } + if (p->p_args != NULL) { + sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length); + PROC_UNLOCK(p); + return (0); + } + + if ((p->p_flag & P_SYSTEM) != 0) { + PROC_UNLOCK(p); + return (0); + } + + PROC_UNLOCK(p); + + ret = proc_getargv(td, p, sb); + return (ret); +} + +/* + * Filler function for proc/pid/environ + */ +static int +linprocfs_doprocenviron(PFS_FILL_ARGS) +{ + int ret; + + PROC_LOCK(p); + if ((ret = p_candebug(td, p)) != 0) { + PROC_UNLOCK(p); + return (ret); + } + + /* + * Mimic linux behavior and pass only processes with usermode + * address space as valid. Return zero silently otherwize. + */ + if (p->p_vmspace == &vmspace0) { + PROC_UNLOCK(p); + return (0); + } + + if ((p->p_flag & P_SYSTEM) != 0) { + PROC_UNLOCK(p); + return (0); + } + + PROC_UNLOCK(p); + + ret = proc_getenvv(td, p, sb); + return (ret); +} + +/* + * Filler function for proc/pid/maps + */ +static int +linprocfs_doprocmaps(PFS_FILL_ARGS) +{ + struct vmspace *vm; + vm_map_t map; + vm_map_entry_t entry, tmp_entry; + vm_object_t obj, tobj, lobj; + vm_offset_t e_start, e_end; + vm_ooffset_t off = 0; + vm_prot_t e_prot; + unsigned int last_timestamp; + char *name = "", *freename = NULL; + ino_t ino; + int ref_count, shadow_count, flags; + int error; + struct vnode *vp; + struct vattr vat; + + PROC_LOCK(p); + error = p_candebug(td, p); + PROC_UNLOCK(p); + if (error) + return (error); + + if (uio->uio_rw != UIO_READ) + return (EOPNOTSUPP); + + error = 0; + vm = vmspace_acquire_ref(p); + if (vm == NULL) + return (ESRCH); + map = &vm->vm_map; + vm_map_lock_read(map); + for (entry = map->header.next; entry != &map->header; + entry = entry->next) { + name = ""; + freename = NULL; + if (entry->eflags & MAP_ENTRY_IS_SUB_MAP) + continue; + e_prot = entry->protection; + e_start = entry->start; + e_end = entry->end; + obj = entry->object.vm_object; + for (lobj = tobj = obj; tobj; tobj = tobj->backing_object) { + VM_OBJECT_RLOCK(tobj); + if (lobj != obj) + VM_OBJECT_RUNLOCK(lobj); + lobj = tobj; + } + last_timestamp = map->timestamp; + vm_map_unlock_read(map); + ino = 0; + if (lobj) { + off = IDX_TO_OFF(lobj->size); + if (lobj->type == OBJT_VNODE) { + vp = lobj->handle; + if (vp) + vref(vp); + } + else + vp = NULL; + if (lobj != obj) + VM_OBJECT_RUNLOCK(lobj); + flags = obj->flags; + ref_count = obj->ref_count; + shadow_count = obj->shadow_count; + VM_OBJECT_RUNLOCK(obj); + if (vp) { + vn_fullpath(td, vp, &name, &freename); + vn_lock(vp, LK_SHARED | LK_RETRY); + VOP_GETATTR(vp, &vat, td->td_ucred); + ino = vat.va_fileid; + vput(vp); + } + } else { + flags = 0; + ref_count = 0; + shadow_count = 0; + } + + /* + * format: + * start, end, access, offset, major, minor, inode, name. + */ + error = sbuf_printf(sb, + "%08lx-%08lx %s%s%s%s %08lx %02x:%02x %lu%s%s\n", + (u_long)e_start, (u_long)e_end, + (e_prot & VM_PROT_READ)?"r":"-", + (e_prot & VM_PROT_WRITE)?"w":"-", + (e_prot & VM_PROT_EXECUTE)?"x":"-", + "p", + (u_long)off, + 0, + 0, + (u_long)ino, + *name ? " " : "", + name + ); + if (freename) + free(freename, M_TEMP); + vm_map_lock_read(map); + if (error == -1) { + error = 0; + break; + } + if (last_timestamp != map->timestamp) { + /* + * Look again for the entry because the map was + * modified while it was unlocked. Specifically, + * the entry may have been clipped, merged, or deleted. + */ + vm_map_lookup_entry(map, e_end - 1, &tmp_entry); + entry = tmp_entry; + } + } + vm_map_unlock_read(map); + vmspace_free(vm); + + return (error); +} + +/* + * Filler function for proc/net/dev + */ +static int +linprocfs_donetdev(PFS_FILL_ARGS) +{ + char ifname[16]; /* XXX LINUX_IFNAMSIZ */ + struct ifnet *ifp; + + sbuf_printf(sb, "%6s|%58s|%s\n" + "%6s|%58s|%58s\n", + "Inter-", " Receive", " Transmit", + " face", + "bytes packets errs drop fifo frame compressed multicast", + "bytes packets errs drop fifo colls carrier compressed"); + + CURVNET_SET(TD_TO_VNET(curthread)); + IFNET_RLOCK(); + TAILQ_FOREACH(ifp, &V_ifnet, if_link) { + linux_ifname(ifp, ifname, sizeof ifname); + sbuf_printf(sb, "%6.6s: ", ifname); + sbuf_printf(sb, "%7lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu ", + ifp->if_ibytes, /* rx_bytes */ + ifp->if_ipackets, /* rx_packets */ + ifp->if_ierrors, /* rx_errors */ + ifp->if_iqdrops, /* rx_dropped + + * rx_missed_errors */ + 0UL, /* rx_fifo_errors */ + 0UL, /* rx_length_errors + + * rx_over_errors + + * rx_crc_errors + + * rx_frame_errors */ + 0UL, /* rx_compressed */ + ifp->if_imcasts); /* multicast, XXX-BZ rx only? */ + sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n", + ifp->if_obytes, /* tx_bytes */ + ifp->if_opackets, /* tx_packets */ + ifp->if_oerrors, /* tx_errors */ + 0UL, /* tx_dropped */ + 0UL, /* tx_fifo_errors */ + ifp->if_collisions, /* collisions */ + 0UL, /* tx_carrier_errors + + * tx_aborted_errors + + * tx_window_errors + + * tx_heartbeat_errors */ + 0UL); /* tx_compressed */ + } + IFNET_RUNLOCK(); + CURVNET_RESTORE(); + + return (0); +} + +/* + * Filler function for proc/sys/kernel/osrelease + */ +static int +linprocfs_doosrelease(PFS_FILL_ARGS) +{ + char osrelease[LINUX_MAX_UTSNAME]; + + linux_get_osrelease(td, osrelease); + sbuf_printf(sb, "%s\n", osrelease); + + return (0); +} + +/* + * Filler function for proc/sys/kernel/ostype + */ +static int +linprocfs_doostype(PFS_FILL_ARGS) +{ + char osname[LINUX_MAX_UTSNAME]; + + linux_get_osname(td, osname); + sbuf_printf(sb, "%s\n", osname); + + return (0); +} + +/* + * Filler function for proc/sys/kernel/version + */ +static int +linprocfs_doosbuild(PFS_FILL_ARGS) +{ + + linprocfs_osbuild(td, sb); + sbuf_cat(sb, "\n"); + return (0); +} + +/* + * Filler function for proc/sys/kernel/msgmni + */ +static int +linprocfs_domsgmni(PFS_FILL_ARGS) +{ + + sbuf_printf(sb, "%d\n", msginfo.msgmni); + return (0); +} + +/* + * Filler function for proc/sys/kernel/pid_max + */ +static int +linprocfs_dopid_max(PFS_FILL_ARGS) +{ + + sbuf_printf(sb, "%i\n", PID_MAX); + return (0); +} + +/* + * Filler function for proc/sys/kernel/sem + */ +static int +linprocfs_dosem(PFS_FILL_ARGS) +{ + + sbuf_printf(sb, "%d %d %d %d\n", seminfo.semmsl, seminfo.semmns, + seminfo.semopm, seminfo.semmni); + return (0); +} + +/* + * Filler function for proc/scsi/device_info + */ +static int +linprocfs_doscsidevinfo(PFS_FILL_ARGS) +{ + + return (0); +} + +/* + * Filler function for proc/scsi/scsi + */ +static int +linprocfs_doscsiscsi(PFS_FILL_ARGS) +{ + + return (0); +} + +extern struct cdevsw *cdevsw[]; + +/* + * Filler function for proc/devices + */ +static int +linprocfs_dodevices(PFS_FILL_ARGS) +{ + char *char_devices; + sbuf_printf(sb, "Character devices:\n"); + + char_devices = linux_get_char_devices(); + sbuf_printf(sb, "%s", char_devices); + linux_free_get_char_devices(char_devices); + + sbuf_printf(sb, "\nBlock devices:\n"); + + return (0); +} + +/* + * Filler function for proc/cmdline + */ +static int +linprocfs_docmdline(PFS_FILL_ARGS) +{ + + sbuf_printf(sb, "BOOT_IMAGE=%s", kernelname); + sbuf_printf(sb, " ro root=302\n"); + return (0); +} + +/* + * Filler function for proc/filesystems + */ +static int +linprocfs_dofilesystems(PFS_FILL_ARGS) +{ + struct vfsconf *vfsp; + + mtx_lock(&Giant); + TAILQ_FOREACH(vfsp, &vfsconf, vfc_list) { + if (vfsp->vfc_flags & VFCF_SYNTHETIC) + sbuf_printf(sb, "nodev"); + sbuf_printf(sb, "\t%s\n", vfsp->vfc_name); + } + mtx_unlock(&Giant); + return(0); +} + +#if 0 +/* + * Filler function for proc/modules + */ +static int +linprocfs_domodules(PFS_FILL_ARGS) +{ + struct linker_file *lf; + + TAILQ_FOREACH(lf, &linker_files, link) { + sbuf_printf(sb, "%-20s%8lu%4d\n", lf->filename, + (unsigned long)lf->size, lf->refs); + } + return (0); +} +#endif + +/* + * Filler function for proc/pid/fd + */ +static int +linprocfs_dofdescfs(PFS_FILL_ARGS) +{ + + if (p == curproc) + sbuf_printf(sb, "/dev/fd"); + else + sbuf_printf(sb, "unknown"); + return (0); +} + +/* + * Constructor + */ +static int +linprocfs_init(PFS_INIT_ARGS) +{ + struct pfs_node *root; + struct pfs_node *dir; + + root = pi->pi_root; + + /* /proc/... */ + pfs_create_file(root, "cmdline", &linprocfs_docmdline, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(root, "cpuinfo", &linprocfs_docpuinfo, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(root, "devices", &linprocfs_dodevices, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(root, "filesystems", &linprocfs_dofilesystems, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(root, "loadavg", &linprocfs_doloadavg, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(root, "meminfo", &linprocfs_domeminfo, + NULL, NULL, NULL, PFS_RD); +#if 0 + pfs_create_file(root, "modules", &linprocfs_domodules, + NULL, NULL, NULL, PFS_RD); +#endif + pfs_create_file(root, "mounts", &linprocfs_domtab, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(root, "mtab", &linprocfs_domtab, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(root, "partitions", &linprocfs_dopartitions, + NULL, NULL, NULL, PFS_RD); + pfs_create_link(root, "self", &procfs_docurproc, + NULL, NULL, NULL, 0); + pfs_create_file(root, "stat", &linprocfs_dostat, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(root, "swaps", &linprocfs_doswaps, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(root, "uptime", &linprocfs_douptime, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(root, "version", &linprocfs_doversion, + NULL, NULL, NULL, PFS_RD); + + /* /proc/net/... */ + dir = pfs_create_dir(root, "net", NULL, NULL, NULL, 0); + pfs_create_file(dir, "dev", &linprocfs_donetdev, + NULL, NULL, NULL, PFS_RD); + + /* /proc/<pid>/... */ + dir = pfs_create_dir(root, "pid", NULL, NULL, NULL, PFS_PROCDEP); + pfs_create_file(dir, "cmdline", &linprocfs_doproccmdline, + NULL, NULL, NULL, PFS_RD); + pfs_create_link(dir, "cwd", &linprocfs_doproccwd, + NULL, NULL, NULL, 0); + pfs_create_file(dir, "environ", &linprocfs_doprocenviron, + NULL, NULL, NULL, PFS_RD); + pfs_create_link(dir, "exe", &procfs_doprocfile, + NULL, &procfs_notsystem, NULL, 0); + pfs_create_file(dir, "maps", &linprocfs_doprocmaps, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(dir, "mem", &procfs_doprocmem, + &procfs_attr, &procfs_candebug, NULL, PFS_RDWR|PFS_RAW); + pfs_create_link(dir, "root", &linprocfs_doprocroot, + NULL, NULL, NULL, 0); + pfs_create_file(dir, "stat", &linprocfs_doprocstat, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(dir, "statm", &linprocfs_doprocstatm, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(dir, "status", &linprocfs_doprocstatus, + NULL, NULL, NULL, PFS_RD); + pfs_create_link(dir, "fd", &linprocfs_dofdescfs, + NULL, NULL, NULL, 0); + + /* /proc/scsi/... */ + dir = pfs_create_dir(root, "scsi", NULL, NULL, NULL, 0); + pfs_create_file(dir, "device_info", &linprocfs_doscsidevinfo, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(dir, "scsi", &linprocfs_doscsiscsi, + NULL, NULL, NULL, PFS_RD); + + /* /proc/sys/... */ + dir = pfs_create_dir(root, "sys", NULL, NULL, NULL, 0); + /* /proc/sys/kernel/... */ + dir = pfs_create_dir(dir, "kernel", NULL, NULL, NULL, 0); + pfs_create_file(dir, "osrelease", &linprocfs_doosrelease, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(dir, "ostype", &linprocfs_doostype, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(dir, "version", &linprocfs_doosbuild, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(dir, "msgmni", &linprocfs_domsgmni, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(dir, "pid_max", &linprocfs_dopid_max, + NULL, NULL, NULL, PFS_RD); + pfs_create_file(dir, "sem", &linprocfs_dosem, + NULL, NULL, NULL, PFS_RD); + + return (0); +} + +/* + * Destructor + */ +static int +linprocfs_uninit(PFS_INIT_ARGS) +{ + + /* nothing to do, pseudofs will GC */ + return (0); +} + +PSEUDOFS(linprocfs, 1, 0); +MODULE_DEPEND(linprocfs, linux, 1, 1, 1); +MODULE_DEPEND(linprocfs, procfs, 1, 1, 1); +MODULE_DEPEND(linprocfs, sysvmsg, 1, 1, 1); +MODULE_DEPEND(linprocfs, sysvsem, 1, 1, 1); |