Index: stable/10/sys/compat/linprocfs/linprocfs.c =================================================================== --- stable/10/sys/compat/linprocfs/linprocfs.c (revision 263100) +++ stable/10/sys/compat/linprocfs/linprocfs.c (revision 263101) @@ -1,1456 +1,1478 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__i386__) || defined(__amd64__) #include #include #endif /* __i386__ || __amd64__ */ #ifdef COMPAT_FREEBSD32 #include #endif #include #include #include #include #include #include /* * 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); } + /* + * Filler function for proc/sys/kernel/random/uuid + */ +static int +linprocfs_douuid(PFS_FILL_ARGS) +{ + struct uuid uuid; + + kern_uuidgen(&uuid, 1); + sbuf_printf_uuid(sb, &uuid); + sbuf_printf(sb, "\n"); + 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//... */ 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); + + /* /proc/sys/kernel/random/... */ + dir = pfs_create_dir(dir, "random", NULL, NULL, NULL, 0); + pfs_create_file(dir, "uuid", &linprocfs_douuid, 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); Index: stable/10/usr.bin/makewhatis/makewhatis.1 =================================================================== --- stable/10/usr.bin/makewhatis/makewhatis.1 (revision 263100) +++ stable/10/usr.bin/makewhatis/makewhatis.1 (revision 263101) @@ -1,136 +1,135 @@ .\" Copyright (c) 2002 John Rochester .\" All rights reserved. .\" .\" 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. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. .\" .\" $FreeBSD$ .\" -.Dd December 3, 2005 -.Dt MAKEWHATIS 1 +.Dd December 8, 2013 +.Dt MAKEWHATIS 8 .Os .Sh NAME .Nm makewhatis -.Nd "create whatis database" +.Nd create whatis database .Sh SYNOPSIS .Nm .Op Fl a .Op Fl i Ar column .Op Fl n Ar name .Op Fl o Ar file .Op Fl v .Op Fl L .Op Ar directories ... .Sh DESCRIPTION The .Nm utility collects the names and short descriptions from all the unformatted man pages in the .Ar directories and puts them into a file used by the .Xr whatis 1 and .Xr apropos 1 commands. Directories may be separated by colons instead of spaces. If no .Ar directories are specified, the contents of the .Ev MANPATH environment variable will be used, or if that is not set, the default directory .Pa /usr/share/man will be processed. .Pp The options are as follows: .Bl -tag -width ".Fl i Ar column" .It Fl a Appends to the output file(s) instead of replacing them. The output will be sorted with duplicate lines removed, but may have obsolete entries. .It Fl i Ar column Indents the description by .Ar column characters. The default value is 24. .It Fl n Ar name Uses .Ar name instead of .Pa whatis . .It Fl o Ar file Outputs all lines to the .Ar file instead of .Pa */man/whatis . .It Fl v Makes .Nm more verbose about what it is doing. .It Fl L Process only localized subdirectories corresponding to the locale specified in the standard environment variables. .El .Sh ENVIRONMENT .Bl -tag -width ".Ev MANPATH" .It Ev LC_ALL , LC_CTYPE , LANG These variables control what subdirectories will be processed if the .Fl L option is used. .It Ev MACHINE If set, its value is used to override the current machine type when searching machine specific subdirectories. -.It Ev MACHINE_ARCH -If set, its value is used to override the current -architecture when searching architecture specific subdirectories. .It Ev MANPATH Determines the set of directories to be processed if none are given on the command line. .El .Sh FILES .Bl -tag -width ".Pa /usr/share/man" -compact .It Pa /usr/share/man Default directory to process if the .Ev MANPATH environment variable is not set. .It Pa */man/whatis The default output file. .El .Sh EXIT STATUS .Ex -std .Sh SEE ALSO .Xr apropos 1 , .Xr whatis 1 .Sh HISTORY The .Nm command appeared in .Fx 2.1 . .Sh AUTHORS .An -nosplit The .Nm program was originally written in Perl and was contributed by .An Wolfram Schneider . The current version of .Nm was rewritten in C by -.An John Rochester . +.An John Rochester +with additional contributions by +.An Franco Fichtner Aq Mt franco@lastsummer.de . Index: stable/10/usr.bin/makewhatis/makewhatis.c =================================================================== --- stable/10/usr.bin/makewhatis/makewhatis.c (revision 263100) +++ stable/10/usr.bin/makewhatis/makewhatis.c (revision 263101) @@ -1,1049 +1,1101 @@ /*- * Copyright (c) 2002 John Rochester + * Copyright (c) 2013 Franco Fichtner * All rights reserved. * * 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, * in this position and unchanged. * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $FreeBSD$ */ -#include -__FBSDID("$FreeBSD$"); - +#include #include -#include #include #include -#include +#include #include #include #include -#include #include #include #include #include #include #include #define DEFAULT_MANPATH "/usr/share/man" #define LINE_ALLOC 4096 static char blank[] = ""; /* - * Information collected about each man page in a section. + * Information collected about each man page alias. */ +struct page_alias { + RB_ENTRY(page_alias) entry; + char *filename; + char *name; + char *suffix; + int gzipped; +}; + +/* + * Information collected about each unique man page. + */ struct page_info { - char * filename; - char * name; - char * suffix; - int gzipped; - ino_t inode; + RB_HEAD(page_alias_tree, page_alias) head; + RB_ENTRY(page_info) entry; + ino_t inode; }; +static RB_HEAD(page_info_tree, page_info) page_head = RB_INITIALIZER(&page_head); + /* + * Sorts page info by inode number. + */ +static int +infosort(const struct page_info *a, const struct page_info *b) +{ + return (memcmp(&a->inode, &b->inode, sizeof(a->inode))); +} + +RB_PROTOTYPE(page_info_tree, page_info, entry, infosort); +RB_GENERATE(page_info_tree, page_info, entry, infosort); + +/* + * Sorts page alias first by suffix, then name. + */ +static int +aliassort(const struct page_alias *a, const struct page_alias *b) +{ + int ret = strcmp(a->suffix, b->suffix); + if (ret) { + return (ret); + } + + return (strcmp(a->name, b->name)); +} + +RB_PROTOTYPE(page_alias_tree, page_alias, entry, aliassort); +RB_GENERATE(page_alias_tree, page_alias, entry, aliassort); + +/* * An entry kept for each visited directory. */ struct visited_dir { dev_t device; ino_t inode; SLIST_ENTRY(visited_dir) next; }; /* * an expanding string */ struct sbuf { char * content; /* the start of the buffer */ char * end; /* just past the end of the content */ char * last; /* the last allocated character */ }; /* * Removes the last amount characters from the sbuf. */ #define sbuf_retract(sbuf, amount) \ ((sbuf)->end -= (amount)) /* * Returns the length of the sbuf content. */ #define sbuf_length(sbuf) \ ((sbuf)->end - (sbuf)->content) typedef char *edited_copy(char *from, char *to, int length); static int append; /* -a flag: append to existing whatis */ static int verbose; /* -v flag: be verbose with warnings */ static int indent = 24; /* -i option: description indentation */ static const char *whatis_name="whatis";/* -n option: the name */ static char *common_output; /* -o option: the single output file */ static char *locale; /* user's locale if -L is used */ static char *lang_locale; /* short form of locale */ -static const char *machine, *machine_arch; +static const char *machine; static int exit_code; /* exit code to use when finished */ static SLIST_HEAD(, visited_dir) visited_dirs = SLIST_HEAD_INITIALIZER(visited_dirs); /* * While the whatis line is being formed, it is stored in whatis_proto. * When finished, it is reformatted into whatis_final and then appended * to whatis_lines. */ static struct sbuf *whatis_proto; static struct sbuf *whatis_final; static StringList *whatis_lines; /* collected output lines */ static char tmp_file[MAXPATHLEN]; /* path of temporary file, if any */ /* A set of possible names for the NAME man page section */ static const char *name_section_titles[] = { "NAME", "Name", "NAMN", "BEZEICHNUNG", "\xcc\xbe\xbe\xce", "\xee\xe1\xfa\xf7\xe1\xee\xe9\xe5", NULL }; /* A subset of the mdoc(7) commands to ignore */ static char mdoc_commands[] = "ArDvErEvFlLiNmPa"; /* * Frees a struct page_info and its content. */ static void free_page_info(struct page_info *info) { - free(info->filename); - free(info->name); - free(info->suffix); + struct page_alias *alias; + + while ((alias = RB_ROOT(&info->head))) { + RB_REMOVE(page_alias_tree, &info->head, alias); + free(alias->filename); + free(alias->suffix); + free(alias->name); + free(alias); + } + free(info); } /* - * Allocates and fills in a new struct page_info given the - * name of the man section directory and the dirent of the file. - * If the file is not a man page, returns NULL. + * Allocates and fills in a new struct page_alias given the + * full file name of the man page and its dirent. + * If the file is not a man page, nothing is added. */ -static struct page_info * -new_page_info(char *dir, struct dirent *dirent) +static void +new_page_alias(struct page_info *info, char *filename, struct dirent *dirent) { - struct page_info *info; - int basename_length; + int gzipped, basename_length; + struct page_alias *alias; char *suffix; - struct stat st; - info = (struct page_info *) malloc(sizeof(struct page_info)); - if (info == NULL) - err(1, "malloc"); basename_length = strlen(dirent->d_name); suffix = &dirent->d_name[basename_length]; - asprintf(&info->filename, "%s/%s", dir, dirent->d_name); - if ((info->gzipped = basename_length >= 4 && strcmp(&dirent->d_name[basename_length - 3], ".gz") == 0)) { + + gzipped = basename_length >= 4 && + strcmp(&dirent->d_name[basename_length - 3], ".gz") == 0; + if (gzipped) { suffix -= 3; *suffix = '\0'; } + for (;;) { if (--suffix == dirent->d_name || !isalnum(*suffix)) { - if (*suffix == '.') + if (*suffix == '.') { break; - if (verbose) - warnx("%s: invalid man page name", info->filename); - free(info->filename); - free(info); - return NULL; + } + if (verbose) { + warnx("%s: invalid man page name", filename); + } + return; } } + *suffix++ = '\0'; - info->name = strdup(dirent->d_name); - info->suffix = strdup(suffix); - if (stat(info->filename, &st) < 0) { - warn("%s", info->filename); - free_page_info(info); - return NULL; + + alias = malloc(sizeof(*alias)); + if (alias == NULL) { + err(1, "malloc"); } - if (!S_ISREG(st.st_mode)) { - if (verbose && !S_ISDIR(st.st_mode)) - warnx("%s: not a regular file", info->filename); - free_page_info(info); - return NULL; - } - info->inode = st.st_ino; - return info; + + alias->name = strdup(dirent->d_name); /* XXX unsafe */ + alias->filename = strdup(filename); /* XXX unsafe */ + alias->suffix = strdup(suffix); /* XXX unsafe */ + alias->gzipped = gzipped; + + RB_INSERT(page_alias_tree, &info->head, alias); } /* * Reset an sbuf's length to 0. */ static void sbuf_clear(struct sbuf *sbuf) { sbuf->end = sbuf->content; } /* * Allocate a new sbuf. */ static struct sbuf * new_sbuf(void) { struct sbuf *sbuf = (struct sbuf *) malloc(sizeof(struct sbuf)); - sbuf->content = (char *) malloc(LINE_ALLOC); + sbuf->content = malloc(LINE_ALLOC); sbuf->last = sbuf->content + LINE_ALLOC - 1; sbuf_clear(sbuf); - return sbuf; + return(sbuf); } /* * Ensure that there is enough room in the sbuf for nchars more characters. */ static void sbuf_need(struct sbuf *sbuf, int nchars) { char *new_content; size_t size, cntsize; /* double the size of the allocation until the buffer is big enough */ while (sbuf->end + nchars > sbuf->last) { size = sbuf->last + 1 - sbuf->content; size *= 2; cntsize = sbuf->end - sbuf->content; - new_content = (char *)malloc(size); + new_content = malloc(size); memcpy(new_content, sbuf->content, cntsize); free(sbuf->content); sbuf->content = new_content; sbuf->end = new_content + cntsize; sbuf->last = new_content + size - 1; } } /* * Appends a string of a given length to the sbuf. */ static void sbuf_append(struct sbuf *sbuf, const char *text, int length) { if (length > 0) { sbuf_need(sbuf, length); memcpy(sbuf->end, text, length); sbuf->end += length; } } /* * Appends a null-terminated string to the sbuf. */ static void sbuf_append_str(struct sbuf *sbuf, char *text) { sbuf_append(sbuf, text, strlen(text)); } /* * Appends an edited null-terminated string to the sbuf. */ static void sbuf_append_edited(struct sbuf *sbuf, char *text, edited_copy copy) { int length = strlen(text); if (length > 0) { sbuf_need(sbuf, length); sbuf->end = copy(text, sbuf->end, length); } } /* * Strips any of a set of chars from the end of the sbuf. */ static void sbuf_strip(struct sbuf *sbuf, const char *set) { while (sbuf->end > sbuf->content && strchr(set, sbuf->end[-1]) != NULL) sbuf->end--; } /* * Returns the null-terminated string built by the sbuf. */ static char * sbuf_content(struct sbuf *sbuf) { *sbuf->end = '\0'; - return sbuf->content; + return(sbuf->content); } -/* - * Returns true if no man page exists in the directory with - * any of the names in the StringList. - */ -static int -no_page_exists(char *dir, StringList *names, char *suffix) -{ - char path[MAXPATHLEN]; - size_t i; - - for (i = 0; i < names->sl_cur; i++) { - snprintf(path, sizeof path, "%s/%s.%s.gz", dir, names->sl_str[i], suffix); - if (access(path, F_OK) < 0) { - path[strlen(path) - 3] = '\0'; - if (access(path, F_OK) < 0) - continue; - } - return 0; - } - return 1; -} - static void trap_signal(int sig __unused) { if (tmp_file[0] != '\0') unlink(tmp_file); exit(1); } /* * Attempts to open an output file. Returns NULL if unsuccessful. */ static FILE * open_output(char *name) { FILE *output; whatis_lines = sl_init(); if (append) { char line[LINE_ALLOC]; output = fopen(name, "r"); if (output == NULL) { warn("%s", name); exit_code = 1; - return NULL; + return(NULL); } while (fgets(line, sizeof line, output) != NULL) { line[strlen(line) - 1] = '\0'; sl_add(whatis_lines, strdup(line)); } } if (common_output == NULL) { snprintf(tmp_file, sizeof tmp_file, "%s.tmp", name); name = tmp_file; } output = fopen(name, "w"); if (output == NULL) { warn("%s", name); exit_code = 1; - return NULL; + return(NULL); } - return output; + return(output); } static int linesort(const void *a, const void *b) { - return strcmp((*(const char * const *)a), (*(const char * const *)b)); + return(strcmp((*(const char * const *)a), (*(const char * const *)b))); } /* * Writes the unique sorted lines to the output file. */ static void finish_output(FILE *output, char *name) { size_t i; char *prev = NULL; - qsort(whatis_lines->sl_str, whatis_lines->sl_cur, sizeof(char *), linesort); + qsort(whatis_lines->sl_str, whatis_lines->sl_cur, sizeof(char *), + linesort); for (i = 0; i < whatis_lines->sl_cur; i++) { char *line = whatis_lines->sl_str[i]; if (i > 0 && strcmp(line, prev) == 0) continue; prev = line; fputs(line, output); putc('\n', output); } fclose(output); sl_free(whatis_lines, 1); if (common_output == NULL) { rename(tmp_file, name); unlink(tmp_file); } } static FILE * open_whatis(char *mandir) { char filename[MAXPATHLEN]; snprintf(filename, sizeof filename, "%s/%s", mandir, whatis_name); - return open_output(filename); + return(open_output(filename)); } static void finish_whatis(FILE *output, char *mandir) { char filename[MAXPATHLEN]; snprintf(filename, sizeof filename, "%s/%s", mandir, whatis_name); finish_output(output, filename); } /* * Tests to see if the given directory has already been visited. */ static int already_visited(char *dir) { struct stat st; struct visited_dir *visit; if (stat(dir, &st) < 0) { warn("%s", dir); exit_code = 1; - return 1; + return(1); } SLIST_FOREACH(visit, &visited_dirs, next) { if (visit->inode == st.st_ino && visit->device == st.st_dev) { warnx("already visited %s", dir); - return 1; + return(1); } } visit = (struct visited_dir *) malloc(sizeof(struct visited_dir)); visit->device = st.st_dev; visit->inode = st.st_ino; SLIST_INSERT_HEAD(&visited_dirs, visit, next); - return 0; + return(0); } /* * Removes trailing spaces from a string, returning a pointer to just * beyond the new last character. */ static char * trim_rhs(char *str) { char *rhs = &str[strlen(str)]; while (--rhs > str && isspace(*rhs)) ; *++rhs = '\0'; - return rhs; + return(rhs); } /* * Returns a pointer to the next non-space character in the string. */ static char * skip_spaces(char *s) { while (*s != '\0' && isspace(*s)) s++; - return s; + return(s); } /* * Returns whether the string contains only digits. */ static int only_digits(char *line) { if (!isdigit(*line++)) - return 0; + return(0); while (isdigit(*line)) line++; - return *line == '\0'; + return(*line == '\0'); } /* * Returns whether the line is of one of the forms: * .Sh NAME * .Sh "NAME" * etc. * assuming that section_start is ".Sh". */ static int name_section_line(char *line, const char *section_start) { char *rhs; const char **title; if (strncmp(line, section_start, 3) != 0) - return 0; + return(0); line = skip_spaces(line + 3); rhs = trim_rhs(line); if (*line == '"') { line++; if (*--rhs == '"') *rhs = '\0'; } for (title = name_section_titles; *title != NULL; title++) if (strcmp(*title, line) == 0) - return 1; - return 0; + return(1); + return(0); } /* * Copies characters while removing the most common nroff/troff * markup: * \(em, \(mi, \s[+-N], \& * \fF, \f(fo, \f[font] * \*s, \*(st, \*[stringvar] */ static char * de_nroff_copy(char *from, char *to, int fromlen) { char *from_end = &from[fromlen]; while (from < from_end) { switch (*from) { case '\\': switch (*++from) { case '(': if (strncmp(&from[1], "em", 2) == 0 || - strncmp(&from[1], "mi", 2) == 0) { + strncmp(&from[1], "mi", 2) == 0) { from += 3; continue; } break; case 's': if (*++from == '-') from++; while (isdigit(*from)) from++; continue; case 'f': case '*': if (*++from == '(') from += 3; else if (*from == '[') { - while (*++from != ']' && from < from_end); + while (*++from != ']' && from < from_end) + ; from++; } else from++; continue; case '&': from++; continue; } break; } *to++ = *from++; } - return to; + return(to); } /* * Appends a string with the nroff formatting removed. */ static void add_nroff(char *text) { sbuf_append_edited(whatis_proto, text, de_nroff_copy); } /* * Appends "name(suffix), " to whatis_final. */ static void add_whatis_name(char *name, char *suffix) { if (*name != '\0') { sbuf_append_str(whatis_final, name); sbuf_append(whatis_final, "(", 1); sbuf_append_str(whatis_final, suffix); sbuf_append(whatis_final, "), ", 3); } } /* * Processes an old-style man(7) line. This ignores commands with only * a single number argument. */ static void process_man_line(char *line) { if (*line == '.') { while (isalpha(*++line)) ; line = skip_spaces(line); if (only_digits(line)) return; } else line = skip_spaces(line); if (*line != '\0') { add_nroff(line); sbuf_append(whatis_proto, " ", 1); } } +struct mdoc_text { + const char *mdoc; + const char *text; +}; + +static int +process_mdoc_macro(char *line) +{ + static const struct mdoc_text list[] = { + { ".At", "AT&T UNIX" }, + { ".Bsx", "BSD/OS" }, + { ".Bx", "BSD" }, + { ".Dx", "DragonFly" }, + { ".Fx", "FreeBSD" }, + { ".Nx", "NetBSD" }, + { ".Ox", "OpenBSD" }, + { ".Ux", "UNIX" }, + }; + unsigned int i; + + for (i = 0; i < sizeof(list) / sizeof(list[0]); ++i) { + if (!strcmp(line, list[i].mdoc)) { + sbuf_append(whatis_proto, list[i].text, + strlen(list[i].text)); + sbuf_append(whatis_proto, " ", 1); + return (1); + } + } + + return (0); +} + /* * Processes a new-style mdoc(7) line. */ static void process_mdoc_line(char *line) { int xref; int arg = 0; char *line_end = &line[strlen(line)]; int orig_length = sbuf_length(whatis_proto); char *next; if (*line == '\0') return; if (line[0] != '.' || !isupper(line[1]) || !islower(line[2])) { add_nroff(skip_spaces(line)); sbuf_append(whatis_proto, " ", 1); return; } + if (process_mdoc_macro(line)) { + return; + } xref = strncmp(line, ".Xr", 3) == 0; line += 3; while ((line = skip_spaces(line)) < line_end) { if (*line == '"') { next = ++line; for (;;) { next = strchr(next, '"'); if (next == NULL) break; memmove(next, next + 1, strlen(next)); line_end--; if (*next != '"') break; next++; } } else next = strpbrk(line, " \t"); if (next != NULL) *next++ = '\0'; else next = line_end; if (isupper(*line) && islower(line[1]) && line[2] == '\0') { if (strcmp(line, "Ns") == 0) { arg = 0; line = next; continue; } if (strstr(mdoc_commands, line) != NULL) { line = next; continue; } } if (arg > 0 && strchr(",.:;?!)]", *line) == 0) { if (xref) { sbuf_append(whatis_proto, "(", 1); add_nroff(line); sbuf_append(whatis_proto, ")", 1); xref = 0; line = blank; } else sbuf_append(whatis_proto, " ", 1); } add_nroff(line); arg++; line = next; } if (sbuf_length(whatis_proto) > orig_length) sbuf_append(whatis_proto, " ", 1); } -/* - * Collects a list of comma-separated names from the text. - */ -static void -collect_names(StringList *names, char *text) -{ - char *arg; - - for (;;) { - arg = text; - text = strchr(text, ','); - if (text != NULL) - *text++ = '\0'; - sl_add(names, arg); - if (text == NULL) - return; - if (*text == ' ') - text++; - } -} - enum { STATE_UNKNOWN, STATE_MANSTYLE, STATE_MDOCNAME, STATE_MDOCDESC }; /* * Processes a man page source into a single whatis line and adds it * to whatis_lines. */ static void -process_page(struct page_info *page, char *section_dir) +process_page(struct page_info *info) { - gzFile in; - char buffer[4096]; - char *line; - StringList *names; - char *descr; int state = STATE_UNKNOWN; - size_t i; + struct page_alias *alias; + char *line, *descr; + char buffer[4096]; + gzFile in; + /* + * Only read the page once for each inode. It's + * safe to assume that page->list is set. + */ + alias = RB_MIN(page_alias_tree, &info->head); + + if (verbose) { + fprintf(stderr, "\treading %s\n", alias->filename); + } + sbuf_clear(whatis_proto); - if ((in = gzopen(page->filename, "r")) == NULL) { - warn("%s", page->filename); + if ((in = gzopen(alias->filename, "r")) == NULL) { + warn("%s", alias->filename); exit_code = 1; return; } - while (gzgets(in, buffer, sizeof buffer) != NULL) { + while (gzgets(in, buffer, sizeof(buffer)) != NULL) { line = buffer; - if (strncmp(line, ".\\\"", 3) == 0) /* ignore comments */ + if (strncmp(line, ".\\\"", 3) == 0) /* ignore comments */ continue; switch (state) { /* * haven't reached the NAME section yet. */ case STATE_UNKNOWN: if (name_section_line(line, ".SH")) state = STATE_MANSTYLE; else if (name_section_line(line, ".Sh")) state = STATE_MDOCNAME; continue; /* * Inside an old-style .SH NAME section. */ case STATE_MANSTYLE: if (strncmp(line, ".SH", 3) == 0) break; if (strncmp(line, ".SS", 3) == 0) break; trim_rhs(line); if (strcmp(line, ".") == 0) continue; if (strncmp(line, ".IX", 3) == 0) { line += 3; line = skip_spaces(line); } process_man_line(line); continue; /* * Inside a new-style .Sh NAME section (the .Nm part). */ case STATE_MDOCNAME: trim_rhs(line); if (strncmp(line, ".Nm", 3) == 0) { process_mdoc_line(line); continue; } else { if (strcmp(line, ".") == 0) continue; sbuf_append(whatis_proto, "- ", 2); state = STATE_MDOCDESC; } /* fall through */ /* * Inside a new-style .Sh NAME section (after the .Nm-s). */ case STATE_MDOCDESC: if (strncmp(line, ".Sh", 3) == 0) break; trim_rhs(line); if (strcmp(line, ".") == 0) continue; process_mdoc_line(line); continue; } break; } gzclose(in); sbuf_strip(whatis_proto, " \t.-"); line = sbuf_content(whatis_proto); /* * line now contains the appropriate data, but without * the proper indentation or the section appended to each name. */ descr = strstr(line, " - "); if (descr == NULL) { descr = strchr(line, ' '); if (descr == NULL) { if (verbose) - fprintf(stderr, " ignoring junk description \"%s\"\n", line); + fprintf(stderr, + "\tignoring junk description \"%s\"\n", + line); return; } *descr++ = '\0'; } else { *descr = '\0'; descr += 3; } - names = sl_init(); - collect_names(names, line); sbuf_clear(whatis_final); - if (!sl_find(names, page->name) && no_page_exists(section_dir, names, page->suffix)) { + RB_FOREACH(alias, page_alias_tree, &info->head) { /* - * Add the page name since that's the only thing that - * man(1) will find. + * This won't append names stored in `line'. + * The reason for that is that we cannot be sure + * which section they belong to unless we have + * a real alias (via MLINKS) in this list. */ - add_whatis_name(page->name, page->suffix); + add_whatis_name(alias->name, alias->suffix); } - for (i = 0; i < names->sl_cur; i++) - add_whatis_name(names->sl_str[i], page->suffix); - sl_free(names, 0); sbuf_retract(whatis_final, 2); /* remove last ", " */ while (sbuf_length(whatis_final) < indent) sbuf_append(whatis_final, " ", 1); sbuf_append(whatis_final, " - ", 3); sbuf_append_str(whatis_final, skip_spaces(descr)); sl_add(whatis_lines, strdup(sbuf_content(whatis_final))); } /* - * Sorts pages first by inode number, then by name. - */ -static int -pagesort(const void *a, const void *b) -{ - const struct page_info *p1 = *(struct page_info * const *) a; - const struct page_info *p2 = *(struct page_info * const *) b; - if (p1->inode == p2->inode) - return strcmp(p1->name, p2->name); - return p1->inode - p2->inode; -} - -/* * Processes a single man section. */ static void process_section(char *section_dir) { struct dirent **entries; + struct page_info *info; int nentries; - struct page_info **pages; - int npages = 0; int i; - ino_t prev_inode = 0; - if (verbose) + if (verbose) { fprintf(stderr, " %s\n", section_dir); + } /* * scan the man section directory for pages */ nentries = scandir(section_dir, &entries, NULL, alphasort); if (nentries < 0) { warn("%s", section_dir); exit_code = 1; return; } + /* * collect information about man pages */ - pages = (struct page_info **) calloc(nentries, sizeof(struct page_info *)); for (i = 0; i < nentries; i++) { - struct page_info *info = new_page_info(section_dir, entries[i]); - if (info != NULL) - pages[npages++] = info; + struct page_info ref; + char *filename; + struct stat st; + + if (asprintf(&filename, "%s/%s", section_dir, + entries[i]->d_name) < 0) { + err(1, "malloc"); + } + + if (stat(filename, &st) < 0) { + warn("%s", filename); + goto process_section_next; + } + + if (!S_ISREG(st.st_mode)) { + if (verbose && !S_ISDIR(st.st_mode)) + warnx("%s: not a regular file", filename); + goto process_section_next; + } + + ref.inode = st.st_ino; + + info = RB_FIND(page_info_tree, &page_head, &ref); + if (info == NULL) { + info = malloc(sizeof(*info)); + if (info == NULL) { + err(1, "malloc"); + } + + bzero(info, sizeof(*info)); + info->inode = st.st_ino; + RB_INIT(&info->head); + + RB_INSERT(page_info_tree, &page_head, info); + } + + new_page_alias(info, filename, entries[i]); + +process_section_next: + free(entries[i]); + free(filename); } free(entries); - qsort(pages, npages, sizeof(struct page_info *), pagesort); - /* - * process each unique page - */ - for (i = 0; i < npages; i++) { - struct page_info *page = pages[i]; - if (page->inode != prev_inode) { - prev_inode = page->inode; - if (verbose) - fprintf(stderr, " reading %s\n", page->filename); - process_page(page, section_dir); - } else if (verbose) - fprintf(stderr, " skipping %s, duplicate\n", page->filename); - free_page_info(page); - } - free(pages); } /* * Returns whether the directory entry is a man page section. */ static int select_sections(const struct dirent *entry) { const char *p = &entry->d_name[3]; if (strncmp(entry->d_name, "man", 3) != 0) - return 0; + return(0); while (*p != '\0') { if (!isalnum(*p++)) - return 0; + return(0); } - return 1; + return(1); } /* * Processes a single top-level man directory by finding all the * sub-directories named man* and processing each one in turn. */ static void process_mandir(char *dir_name) { struct dirent **entries; + struct page_info *info; int nsections; FILE *fp = NULL; int i; struct stat st; if (already_visited(dir_name)) return; if (verbose) fprintf(stderr, "man directory %s\n", dir_name); nsections = scandir(dir_name, &entries, select_sections, alphasort); if (nsections < 0) { warn("%s", dir_name); exit_code = 1; return; } if (common_output == NULL && (fp = open_whatis(dir_name)) == NULL) return; for (i = 0; i < nsections; i++) { char section_dir[MAXPATHLEN]; - snprintf(section_dir, sizeof section_dir, "%s/%s", dir_name, entries[i]->d_name); + snprintf(section_dir, sizeof section_dir, "%s/%s", dir_name, + entries[i]->d_name); process_section(section_dir); snprintf(section_dir, sizeof section_dir, "%s/%s/%s", dir_name, - entries[i]->d_name, machine); + entries[i]->d_name, machine); if (stat(section_dir, &st) == 0 && S_ISDIR(st.st_mode)) process_section(section_dir); - if (strcmp(machine_arch, machine) != 0) { - snprintf(section_dir, sizeof section_dir, "%s/%s/%s", - dir_name, entries[i]->d_name, machine_arch); - if (stat(section_dir, &st) == 0 && S_ISDIR(st.st_mode)) - process_section(section_dir); - } free(entries[i]); } free(entries); + + /* + * process and free all pages + */ + while ((info = RB_ROOT(&page_head))) { + RB_REMOVE(page_info_tree, &page_head, info); + process_page(info); + free_page_info(info); + } + if (common_output == NULL) finish_whatis(fp, dir_name); } /* * Processes one argument, which may be a colon-separated list of * directories. */ static void process_argument(const char *arg) { char *dir; char *mandir; char *parg; parg = strdup(arg); if (parg == NULL) err(1, "out of memory"); while ((dir = strsep(&parg, ":")) != NULL) { if (locale != NULL) { asprintf(&mandir, "%s/%s", dir, locale); process_mandir(mandir); free(mandir); if (lang_locale != NULL) { asprintf(&mandir, "%s/%s", dir, lang_locale); process_mandir(mandir); free(mandir); } } else { process_mandir(dir); } } free(parg); } int main(int argc, char **argv) { int opt; FILE *fp = NULL; while ((opt = getopt(argc, argv, "ai:n:o:vL")) != -1) { switch (opt) { case 'a': append++; break; case 'i': indent = atoi(optarg); break; case 'n': whatis_name = optarg; break; case 'o': common_output = optarg; break; case 'v': verbose++; break; case 'L': locale = getenv("LC_ALL"); if (locale == NULL) locale = getenv("LC_CTYPE"); if (locale == NULL) locale = getenv("LANG"); if (locale != NULL) { char *sep = strchr(locale, '_'); if (sep != NULL && isupper(sep[1]) && isupper(sep[2])) { - asprintf(&lang_locale, "%.*s%s", (int)(ptrdiff_t)(sep - locale), locale, &sep[3]); + asprintf(&lang_locale, "%.*s%s", + (int)(sep - locale), + locale, &sep[3]); } } break; default: fprintf(stderr, "usage: %s [-a] [-i indent] [-n name] [-o output_file] [-v] [-L] [directories...]\n", argv[0]); exit(1); } } signal(SIGINT, trap_signal); signal(SIGHUP, trap_signal); signal(SIGQUIT, trap_signal); signal(SIGTERM, trap_signal); SLIST_INIT(&visited_dirs); whatis_proto = new_sbuf(); whatis_final = new_sbuf(); - if ((machine = getenv("MACHINE")) == NULL) { - static struct utsname utsname; - - if (uname(&utsname) == -1) - err(1, "uname"); - machine = utsname.machine; - } - - if ((machine_arch = getenv("MACHINE_ARCH")) == NULL) - machine_arch = MACHINE_ARCH; + if ((machine = getenv("MACHINE")) == NULL) + machine = MACHINE; if (common_output != NULL && (fp = open_output(common_output)) == NULL) err(1, "%s", common_output); if (optind == argc) { const char *manpath = getenv("MANPATH"); if (manpath == NULL) manpath = DEFAULT_MANPATH; process_argument(manpath); } else { while (optind < argc) process_argument(argv[optind++]); } if (common_output != NULL) finish_output(fp, common_output); exit(exit_code); } Index: stable/10 =================================================================== --- stable/10 (revision 263100) +++ stable/10 (revision 263101) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r262539