summaryrefslogtreecommitdiffstats
path: root/bin/ps
diff options
context:
space:
mode:
Diffstat (limited to 'bin/ps')
-rw-r--r--bin/ps/Makefile16
-rw-r--r--bin/ps/Makefile.depend24
-rw-r--r--bin/ps/extern.h90
-rw-r--r--bin/ps/fmt.c137
-rw-r--r--bin/ps/keyword.c370
-rw-r--r--bin/ps/nlist.c67
-rw-r--r--bin/ps/print.c834
-rw-r--r--bin/ps/ps.1780
-rw-r--r--bin/ps/ps.c1438
-rw-r--r--bin/ps/ps.h88
10 files changed, 3844 insertions, 0 deletions
diff --git a/bin/ps/Makefile b/bin/ps/Makefile
new file mode 100644
index 0000000..79e9fc6
--- /dev/null
+++ b/bin/ps/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+# @(#)Makefile 8.1 (Berkeley) 6/2/93
+
+PROG= ps
+SRCS= fmt.c keyword.c nlist.c print.c ps.c
+
+#
+# To support "lazy" ps for non root/wheel users
+# add -DLAZY_PS to the cflags. This helps
+# keep ps from being an unnecessary load
+# on large systems.
+#
+CFLAGS+=-DLAZY_PS
+LIBADD= m kvm jail xo
+
+.include <bsd.prog.mk>
diff --git a/bin/ps/Makefile.depend b/bin/ps/Makefile.depend
new file mode 100644
index 0000000..1576eff
--- /dev/null
+++ b/bin/ps/Makefile.depend
@@ -0,0 +1,24 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ gnu/lib/libgcc \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libelf \
+ lib/libjail \
+ lib/libkvm \
+ lib/libutil \
+ lib/libxo \
+ lib/msun \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/bin/ps/extern.h b/bin/ps/extern.h
new file mode 100644
index 0000000..eb9b2cf
--- /dev/null
+++ b/bin/ps/extern.h
@@ -0,0 +1,90 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. 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.
+ * 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.
+ *
+ * @(#)extern.h 8.3 (Berkeley) 4/2/94
+ * $FreeBSD$
+ */
+
+struct kinfo;
+struct nlist;
+struct var;
+struct varent;
+
+extern fixpt_t ccpu;
+extern int cflag, eval, fscale, nlistread, rawcpu;
+extern unsigned long mempages;
+extern time_t now;
+extern int showthreads, sumrusage, termwidth;
+extern STAILQ_HEAD(velisthead, varent) varlist;
+
+__BEGIN_DECLS
+char *arguments(KINFO *, VARENT *);
+char *command(KINFO *, VARENT *);
+char *cputime(KINFO *, VARENT *);
+int donlist(void);
+char *elapsed(KINFO *, VARENT *);
+char *elapseds(KINFO *, VARENT *);
+char *emulname(KINFO *, VARENT *);
+VARENT *find_varentry(VAR *);
+const char *fmt_argv(char **, char *, char *, size_t);
+double getpcpu(const KINFO *);
+char *kvar(KINFO *, VARENT *);
+char *label(KINFO *, VARENT *);
+char *loginclass(KINFO *, VARENT *);
+char *logname(KINFO *, VARENT *);
+char *longtname(KINFO *, VARENT *);
+char *lstarted(KINFO *, VARENT *);
+char *maxrss(KINFO *, VARENT *);
+char *lockname(KINFO *, VARENT *);
+char *mwchan(KINFO *, VARENT *);
+char *nwchan(KINFO *, VARENT *);
+char *pagein(KINFO *, VARENT *);
+void parsefmt(const char *, int);
+char *pcpu(KINFO *, VARENT *);
+char *pmem(KINFO *, VARENT *);
+char *pri(KINFO *, VARENT *);
+void printheader(void);
+char *priorityr(KINFO *, VARENT *);
+char *egroupname(KINFO *, VARENT *);
+char *rgroupname(KINFO *, VARENT *);
+char *runame(KINFO *, VARENT *);
+char *rvar(KINFO *, VARENT *);
+void showkey(void);
+char *started(KINFO *, VARENT *);
+char *state(KINFO *, VARENT *);
+char *systime(KINFO *, VARENT *);
+char *tdev(KINFO *, VARENT *);
+char *tdnam(KINFO *, VARENT *);
+char *tname(KINFO *, VARENT *);
+char *ucomm(KINFO *, VARENT *);
+char *uname(KINFO *, VARENT *);
+char *upr(KINFO *, VARENT *);
+char *usertime(KINFO *, VARENT *);
+char *vsize(KINFO *, VARENT *);
+char *wchan(KINFO *, VARENT *);
+__END_DECLS
diff --git a/bin/ps/fmt.c b/bin/ps/fmt.c
new file mode 100644
index 0000000..4db6b8d
--- /dev/null
+++ b/bin/ps/fmt.c
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. 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.
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)fmt.c 8.4 (Berkeley) 4/15/94";
+#endif
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <vis.h>
+
+#include "ps.h"
+
+static char *cmdpart(char *);
+static char *shquote(char **);
+
+static char *
+shquote(char **argv)
+{
+ long arg_max;
+ static size_t buf_size;
+ size_t len;
+ char **p, *dst, *src;
+ static char *buf = NULL;
+
+ if (buf == NULL) {
+ if ((arg_max = sysconf(_SC_ARG_MAX)) == -1)
+ errx(1, "sysconf _SC_ARG_MAX failed");
+ if (arg_max >= LONG_MAX / 4 || arg_max >= (long)(SIZE_MAX / 4))
+ errx(1, "sysconf _SC_ARG_MAX preposterously large");
+ buf_size = 4 * arg_max + 1;
+ if ((buf = malloc(buf_size)) == NULL)
+ errx(1, "malloc failed");
+ }
+
+ if (*argv == NULL) {
+ buf[0] = '\0';
+ return (buf);
+ }
+ dst = buf;
+ for (p = argv; (src = *p++) != NULL; ) {
+ if (*src == '\0')
+ continue;
+ len = (buf_size - 1 - (dst - buf)) / 4;
+ strvisx(dst, src, strlen(src) < len ? strlen(src) : len,
+ VIS_NL | VIS_CSTYLE);
+ while (*dst != '\0')
+ dst++;
+ if ((buf_size - 1 - (dst - buf)) / 4 > 0)
+ *dst++ = ' ';
+ }
+ /* Chop off trailing space */
+ if (dst != buf && dst[-1] == ' ')
+ dst--;
+ *dst = '\0';
+ return (buf);
+}
+
+static char *
+cmdpart(char *arg0)
+{
+ char *cp;
+
+ return ((cp = strrchr(arg0, '/')) != NULL ? cp + 1 : arg0);
+}
+
+const char *
+fmt_argv(char **argv, char *cmd, char *thread, size_t maxlen)
+{
+ size_t len;
+ char *ap, *cp;
+
+ if (argv == NULL || argv[0] == NULL) {
+ if (cmd == NULL)
+ return ("");
+ ap = NULL;
+ len = maxlen + 3;
+ } else {
+ ap = shquote(argv);
+ len = strlen(ap) + maxlen + 4;
+ }
+ cp = malloc(len);
+ if (cp == NULL)
+ errx(1, "malloc failed");
+ if (ap == NULL) {
+ if (thread != NULL) {
+ asprintf(&ap, "%s/%s", cmd, thread);
+ sprintf(cp, "[%.*s]", (int)maxlen, ap);
+ free(ap);
+ } else
+ sprintf(cp, "[%.*s]", (int)maxlen, cmd);
+ } else if (strncmp(cmdpart(argv[0]), cmd, maxlen) != 0)
+ sprintf(cp, "%s (%.*s)", ap, (int)maxlen, cmd);
+ else
+ strcpy(cp, ap);
+ return (cp);
+}
diff --git a/bin/ps/keyword.c b/bin/ps/keyword.c
new file mode 100644
index 0000000..b802c37
--- /dev/null
+++ b/bin/ps/keyword.c
@@ -0,0 +1,370 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. 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.
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)keyword.c 8.5 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+#include <err.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libxo/xo.h>
+
+#include "ps.h"
+
+static VAR *findvar(char *, int, char **header);
+static int vcmp(const void *, const void *);
+
+/* Compute offset in common structures. */
+#define KOFF(x) offsetof(struct kinfo_proc, x)
+#define ROFF(x) offsetof(struct rusage, x)
+
+#define LWPFMT "d"
+#define NLWPFMT "d"
+#define UIDFMT "u"
+#define PIDFMT "d"
+
+/* PLEASE KEEP THE TABLE BELOW SORTED ALPHABETICALLY!!! */
+static VAR var[] = {
+ {"%cpu", "%CPU", NULL, "percent-cpu", 0, pcpu, 0, CHAR, NULL, 0},
+ {"%mem", "%MEM", NULL, "percent-memory", 0, pmem, 0, CHAR, NULL, 0},
+ {"acflag", "ACFLG", NULL, "accounting-flag", 0, kvar, KOFF(ki_acflag),
+ USHORT, "x", 0},
+ {"acflg", "", "acflag", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"args", "COMMAND", NULL, "arguments", COMM|LJUST|USER, arguments, 0,
+ CHAR, NULL, 0},
+ {"blocked", "", "sigmask", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"caught", "", "sigcatch", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"class", "CLASS", NULL, "login-class", LJUST, loginclass, 0, CHAR,
+ NULL, 0},
+ {"comm", "COMMAND", NULL, "command", LJUST, ucomm, 0, CHAR, NULL, 0},
+ {"command", "COMMAND", NULL, "command", COMM|LJUST|USER, command, 0,
+ CHAR, NULL, 0},
+ {"cow", "COW", NULL, "copy-on-write-faults", 0, kvar, KOFF(ki_cow),
+ UINT, "u", 0},
+ {"cpu", "CPU", NULL, "cpu-usage", 0, kvar, KOFF(ki_estcpu), UINT, "d",
+ 0},
+ {"cputime", "", "time", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"dsiz", "DSIZ", NULL, "data-size", 0, kvar, KOFF(ki_dsize), PGTOK,
+ "ld", 0},
+ {"egid", "", "gid", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"egroup", "", "group", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"emul", "EMUL", NULL, "emulation-envirnment", LJUST, emulname, 0,
+ CHAR, NULL, 0},
+ {"etime", "ELAPSED", NULL, "elapsed-time", USER, elapsed, 0, CHAR,
+ NULL, 0},
+ {"etimes", "ELAPSED", NULL, "elapsed-times", USER, elapseds, 0, CHAR,
+ NULL, 0},
+ {"euid", "", "uid", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"f", "F", NULL, "flags", 0, kvar, KOFF(ki_flag), INT, "x", 0},
+ {"f2", "F2", NULL, "flags2", 0, kvar, KOFF(ki_flag2), INT, "08x", 0},
+ {"fib", "FIB", NULL, "fib", 0, kvar, KOFF(ki_fibnum), INT, "d", 0},
+ {"flags", "", "f", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"flags2", "", "f2", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"gid", "GID", NULL, "gid", 0, kvar, KOFF(ki_groups), UINT, UIDFMT, 0},
+ {"group", "GROUP", NULL, "group", LJUST, egroupname, 0, CHAR, NULL, 0},
+ {"ignored", "", "sigignore", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"inblk", "INBLK", NULL, "read-blocks", USER, rvar, ROFF(ru_inblock),
+ LONG, "ld", 0},
+ {"inblock", "", "inblk", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"jid", "JID", NULL, "jail-id", 0, kvar, KOFF(ki_jid), INT, "d", 0},
+ {"jobc", "JOBC", NULL, "job-control-count", 0, kvar, KOFF(ki_jobc),
+ SHORT, "d", 0},
+ {"ktrace", "KTRACE", NULL, "ktrace", 0, kvar, KOFF(ki_traceflag), INT,
+ "x", 0},
+ {"label", "LABEL", NULL, "label", LJUST, label, 0, CHAR, NULL, 0},
+ {"lim", "LIM", NULL, "memory-limit", 0, maxrss, 0, CHAR, NULL, 0},
+ {"lockname", "LOCK", NULL, "lock-name", LJUST, lockname, 0, CHAR, NULL,
+ 0},
+ {"login", "LOGIN", NULL, "login-name", LJUST, logname, 0, CHAR, NULL,
+ 0},
+ {"logname", "", "login", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"lstart", "STARTED", NULL, "start-time", LJUST|USER, lstarted, 0,
+ CHAR, NULL, 0},
+ {"lwp", "LWP", NULL, "process-thread-id", 0, kvar, KOFF(ki_tid), UINT,
+ LWPFMT, 0},
+ {"majflt", "MAJFLT", NULL, "major-faults", USER, rvar, ROFF(ru_majflt),
+ LONG, "ld", 0},
+ {"minflt", "MINFLT", NULL, "minor-faults", USER, rvar, ROFF(ru_minflt),
+ LONG, "ld", 0},
+ {"msgrcv", "MSGRCV", NULL, "received-messages", USER, rvar,
+ ROFF(ru_msgrcv), LONG, "ld", 0},
+ {"msgsnd", "MSGSND", NULL, "sent-messages", USER, rvar,
+ ROFF(ru_msgsnd), LONG, "ld", 0},
+ {"mwchan", "MWCHAN", NULL, "wait-channel", LJUST, mwchan, 0, CHAR,
+ NULL, 0},
+ {"ni", "", "nice", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"nice", "NI", NULL, "nice", 0, kvar, KOFF(ki_nice), CHAR, "d", 0},
+ {"nivcsw", "NIVCSW", NULL, "involuntary-context-switches", USER, rvar,
+ ROFF(ru_nivcsw), LONG, "ld", 0},
+ {"nlwp", "NLWP", NULL, "threads", 0, kvar, KOFF(ki_numthreads), UINT,
+ NLWPFMT, 0},
+ {"nsignals", "", "nsigs", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"nsigs", "NSIGS", NULL, "signals-taken", USER, rvar,
+ ROFF(ru_nsignals), LONG, "ld", 0},
+ {"nswap", "NSWAP", NULL, "swaps", USER, rvar, ROFF(ru_nswap), LONG,
+ "ld", 0},
+ {"nvcsw", "NVCSW", NULL, "voluntary-context-switches", USER, rvar,
+ ROFF(ru_nvcsw), LONG, "ld", 0},
+ {"nwchan", "NWCHAN", NULL, "wait-channel-address", LJUST, nwchan, 0,
+ CHAR, NULL, 0},
+ {"oublk", "OUBLK", NULL, "written-blocks", USER, rvar,
+ ROFF(ru_oublock), LONG, "ld", 0},
+ {"oublock", "", "oublk", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"paddr", "PADDR", NULL, "process-address", 0, kvar, KOFF(ki_paddr),
+ KPTR, "lx", 0},
+ {"pagein", "PAGEIN", NULL, "pageins", USER, pagein, 0, CHAR, NULL, 0},
+ {"pcpu", "", "%cpu", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"pending", "", "sig", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"pgid", "PGID", NULL, "process-group", 0, kvar, KOFF(ki_pgid), UINT,
+ PIDFMT, 0},
+ {"pid", "PID", NULL, "pid", 0, kvar, KOFF(ki_pid), UINT, PIDFMT, 0},
+ {"pmem", "", "%mem", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"ppid", "PPID", NULL, "ppid", 0, kvar, KOFF(ki_ppid), UINT, PIDFMT, 0},
+ {"pri", "PRI", NULL, "priority", 0, pri, 0, CHAR, NULL, 0},
+ {"re", "RE", NULL, "residency-time", INF127, kvar, KOFF(ki_swtime),
+ UINT, "d", 0},
+ {"rgid", "RGID", NULL, "real-gid", 0, kvar, KOFF(ki_rgid), UINT,
+ UIDFMT, 0},
+ {"rgroup", "RGROUP", NULL, "real-group", LJUST, rgroupname, 0, CHAR,
+ NULL, 0},
+ {"rss", "RSS", NULL, "rss", 0, kvar, KOFF(ki_rssize), PGTOK, "ld", 0},
+ {"rtprio", "RTPRIO", NULL, "realtime-priority", 0, priorityr,
+ KOFF(ki_pri), CHAR, NULL, 0},
+ {"ruid", "RUID", NULL, "real-uid", 0, kvar, KOFF(ki_ruid), UINT,
+ UIDFMT, 0},
+ {"ruser", "RUSER", NULL, "real-user", LJUST, runame, 0, CHAR, NULL, 0},
+ {"sid", "SID", NULL, "sid", 0, kvar, KOFF(ki_sid), UINT, PIDFMT, 0},
+ {"sig", "PENDING", NULL, "signals-pending", 0, kvar, KOFF(ki_siglist),
+ INT, "x", 0},
+ {"sigcatch", "CAUGHT", NULL, "signals-caught", 0, kvar,
+ KOFF(ki_sigcatch), UINT, "x", 0},
+ {"sigignore", "IGNORED", NULL, "signals-ignored", 0, kvar,
+ KOFF(ki_sigignore), UINT, "x", 0},
+ {"sigmask", "BLOCKED", NULL, "signal-mask", 0, kvar, KOFF(ki_sigmask),
+ UINT, "x", 0},
+ {"sl", "SL", NULL, "sleep-time", INF127, kvar, KOFF(ki_slptime), UINT,
+ "d", 0},
+ {"ssiz", "SSIZ", NULL, "stack-size", 0, kvar, KOFF(ki_ssize), PGTOK,
+ "ld", 0},
+ {"start", "STARTED", NULL, "start-time", LJUST|USER, started, 0, CHAR,
+ NULL, 0},
+ {"stat", "", "state", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"state", "STAT", NULL, "state", LJUST, state, 0, CHAR, NULL, 0},
+ {"svgid", "SVGID", NULL, "saved-gid", 0, kvar, KOFF(ki_svgid), UINT,
+ UIDFMT, 0},
+ {"svuid", "SVUID", NULL, "saved-uid", 0, kvar, KOFF(ki_svuid), UINT,
+ UIDFMT, 0},
+ {"systime", "SYSTIME", NULL, "system-time", USER, systime, 0, CHAR,
+ NULL, 0},
+ {"tdaddr", "TDADDR", NULL, "thread-address", 0, kvar, KOFF(ki_tdaddr),
+ KPTR, "lx", 0},
+ {"tdev", "TDEV", NULL, "terminal-device", 0, tdev, 0, CHAR, NULL, 0},
+ {"tdnam", "TDNAM", NULL, "terminal-device-name", LJUST, tdnam, 0, CHAR,
+ NULL, 0},
+ {"time", "TIME", NULL, "cpu-time", USER, cputime, 0, CHAR, NULL, 0},
+ {"tpgid", "TPGID", NULL, "terminal-process-gid", 0, kvar,
+ KOFF(ki_tpgid), UINT, PIDFMT, 0},
+ {"tracer", "TRACER", NULL, "tracer", 0, kvar, KOFF(ki_tracer), UINT,
+ PIDFMT, 0},
+ {"tsid", "TSID", NULL, "terminal-sid", 0, kvar, KOFF(ki_tsid), UINT,
+ PIDFMT, 0},
+ {"tsiz", "TSIZ", NULL, "text-size", 0, kvar, KOFF(ki_tsize), PGTOK,
+ "ld", 0},
+ {"tt", "TT ", NULL, "terminal-name", 0, tname, 0, CHAR, NULL, 0},
+ {"tty", "TTY", NULL, "tty", LJUST, longtname, 0, CHAR, NULL, 0},
+ {"ucomm", "UCOMM", NULL, "accounting-name", LJUST, ucomm, 0, CHAR,
+ NULL, 0},
+ {"uid", "UID", NULL, "uid", 0, kvar, KOFF(ki_uid), UINT, UIDFMT, 0},
+ {"upr", "UPR", NULL, "user-priority", 0, upr, 0, CHAR, NULL, 0},
+ {"uprocp", "UPROCP", NULL, "process-address", 0, kvar, KOFF(ki_paddr),
+ KPTR, "lx", 0},
+ {"user", "USER", NULL, "user", LJUST, uname, 0, CHAR, NULL, 0},
+ {"usertime", "USERTIME", NULL, "user-time", USER, usertime, 0, CHAR,
+ NULL, 0},
+ {"usrpri", "", "upr", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"vsize", "", "vsz", NULL, 0, NULL, 0, CHAR, NULL, 0},
+ {"vsz", "VSZ", NULL, "virtual-size", 0, vsize, 0, CHAR, NULL, 0},
+ {"wchan", "WCHAN", NULL, "wait-channel", LJUST, wchan, 0, CHAR, NULL,
+ 0},
+ {"xstat", "XSTAT", NULL, "exit-status", 0, kvar, KOFF(ki_xstat),
+ USHORT, "x", 0},
+ {"", NULL, NULL, NULL, 0, NULL, 0, CHAR, NULL, 0},
+};
+
+void
+showkey(void)
+{
+ VAR *v;
+ int i;
+ const char *p, *sep;
+
+ i = 0;
+ sep = "";
+ xo_open_list("key");
+ for (v = var; *(p = v->name); ++v) {
+ int len = strlen(p);
+ if (termwidth && (i += len + 1) > termwidth) {
+ i = len;
+ sep = "\n";
+ }
+ xo_emit("{P:/%s}{l:key/%s}", sep, p);
+ sep = " ";
+ }
+ xo_emit("\n");
+ xo_close_list("key");
+ xo_finish();
+}
+
+void
+parsefmt(const char *p, int user)
+{
+ char *tempstr, *tempstr1;
+
+#define FMTSEP " \t,\n"
+ tempstr1 = tempstr = strdup(p);
+ while (tempstr && *tempstr) {
+ char *cp, *hp;
+ VAR *v;
+ struct varent *vent;
+
+ /*
+ * If an item contains an equals sign, it specifies a column
+ * header, may contain embedded separator characters and
+ * is always the last item.
+ */
+ if (tempstr[strcspn(tempstr, "="FMTSEP)] != '=')
+ while ((cp = strsep(&tempstr, FMTSEP)) != NULL &&
+ *cp == '\0')
+ /* void */;
+ else {
+ cp = tempstr;
+ tempstr = NULL;
+ }
+ if (cp == NULL || !(v = findvar(cp, user, &hp)))
+ continue;
+ if (!user) {
+ /*
+ * If the user is NOT adding this field manually,
+ * get on with our lives if this VAR is already
+ * represented in the list.
+ */
+ vent = find_varentry(v);
+ if (vent != NULL)
+ continue;
+ }
+ if ((vent = malloc(sizeof(struct varent))) == NULL)
+ errx(1, "malloc failed");
+ vent->header = v->header;
+ if (hp) {
+ hp = strdup(hp);
+ if (hp)
+ vent->header = hp;
+ }
+ vent->var = malloc(sizeof(*vent->var));
+ if (vent->var == NULL)
+ errx(1, "malloc failed");
+ memcpy(vent->var, v, sizeof(*vent->var));
+ STAILQ_INSERT_TAIL(&varlist, vent, next_ve);
+ }
+ free(tempstr1);
+ if (STAILQ_EMPTY(&varlist)) {
+ warnx("no valid keywords; valid keywords:");
+ showkey();
+ exit(1);
+ }
+}
+
+static VAR *
+findvar(char *p, int user, char **header)
+{
+ size_t rflen;
+ VAR *v, key;
+ char *hp, *realfmt;
+
+ hp = strchr(p, '=');
+ if (hp)
+ *hp++ = '\0';
+
+ key.name = p;
+ v = bsearch(&key, var, sizeof(var)/sizeof(VAR) - 1, sizeof(VAR), vcmp);
+
+ if (v && v->alias) {
+ /*
+ * If the user specified an alternate-header for this
+ * (aliased) format-name, then we need to copy that
+ * alternate-header when making the recursive call to
+ * process the alias.
+ */
+ if (hp == NULL)
+ parsefmt(v->alias, user);
+ else {
+ /*
+ * XXX - This processing will not be correct for
+ * any alias which expands into a list of format
+ * keywords. Presently there are no aliases
+ * which do that.
+ */
+ rflen = strlen(v->alias) + strlen(hp) + 2;
+ realfmt = malloc(rflen);
+ if (realfmt == NULL)
+ errx(1, "malloc failed");
+ snprintf(realfmt, rflen, "%s=%s", v->alias, hp);
+ parsefmt(realfmt, user);
+ free(realfmt);
+ }
+ return ((VAR *)NULL);
+ }
+ if (!v) {
+ warnx("%s: keyword not found", p);
+ eval = 1;
+ }
+ if (header)
+ *header = hp;
+ return (v);
+}
+
+static int
+vcmp(const void *a, const void *b)
+{
+ return (strcmp(((const VAR *)a)->name, ((const VAR *)b)->name));
+}
diff --git a/bin/ps/nlist.c b/bin/ps/nlist.c
new file mode 100644
index 0000000..f5e600b
--- /dev/null
+++ b/bin/ps/nlist.c
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. 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.
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)nlist.c 8.4 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <stddef.h>
+
+#include "ps.h"
+
+fixpt_t ccpu; /* kernel _ccpu variable */
+int nlistread; /* if nlist already read. */
+unsigned long mempages; /* number of pages of phys. memory */
+int fscale; /* kernel _fscale variable */
+
+int
+donlist(void)
+{
+ size_t oldlen;
+
+ oldlen = sizeof(ccpu);
+ if (sysctlbyname("kern.ccpu", &ccpu, &oldlen, NULL, 0) == -1)
+ return (1);
+ oldlen = sizeof(fscale);
+ if (sysctlbyname("kern.fscale", &fscale, &oldlen, NULL, 0) == -1)
+ return (1);
+ oldlen = sizeof(mempages);
+ if (sysctlbyname("hw.availpages", &mempages, &oldlen, NULL, 0) == -1)
+ return (1);
+ nlistread = 1;
+ return (0);
+}
diff --git a/bin/ps/print.c b/bin/ps/print.c
new file mode 100644
index 0000000..91daa44
--- /dev/null
+++ b/bin/ps/print.c
@@ -0,0 +1,834 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. 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.
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)print.c 8.6 (Berkeley) 4/16/94";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+
+#include <sys/mac.h>
+#include <sys/user.h>
+#include <sys/sysctl.h>
+#include <sys/vmmeter.h>
+
+#include <err.h>
+#include <grp.h>
+#include <langinfo.h>
+#include <locale.h>
+#include <math.h>
+#include <nlist.h>
+#include <pwd.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <vis.h>
+#include <libxo/xo.h>
+
+#include "ps.h"
+
+#define COMMAND_WIDTH 16
+#define ARGUMENTS_WIDTH 16
+
+#define ps_pgtok(a) (((a) * getpagesize()) / 1024)
+
+void
+printheader(void)
+{
+ VAR *v;
+ struct varent *vent;
+
+ STAILQ_FOREACH(vent, &varlist, next_ve)
+ if (*vent->header != '\0')
+ break;
+ if (!vent)
+ return;
+
+ STAILQ_FOREACH(vent, &varlist, next_ve) {
+ v = vent->var;
+ if (v->flag & LJUST) {
+ if (STAILQ_NEXT(vent, next_ve) == NULL) /* last one */
+ xo_emit("{T:/%s}", vent->header);
+ else
+ xo_emit("{T:/%-*s}", v->width, vent->header);
+ } else
+ xo_emit("{T:/%*s}", v->width, vent->header);
+ if (STAILQ_NEXT(vent, next_ve) != NULL)
+ xo_emit("{P: }");
+ }
+ xo_emit("\n");
+}
+
+char *
+arguments(KINFO *k, VARENT *ve)
+{
+ char *vis_args;
+
+ if ((vis_args = malloc(strlen(k->ki_args) * 4 + 1)) == NULL)
+ xo_errx(1, "malloc failed");
+ strvis(vis_args, k->ki_args, VIS_TAB | VIS_NL | VIS_NOSLASH);
+
+ if (STAILQ_NEXT(ve, next_ve) != NULL && strlen(vis_args) > ARGUMENTS_WIDTH)
+ vis_args[ARGUMENTS_WIDTH] = '\0';
+
+ return (vis_args);
+}
+
+char *
+command(KINFO *k, VARENT *ve)
+{
+ char *vis_args, *vis_env, *str;
+
+ if (cflag) {
+ /* If it is the last field, then don't pad */
+ if (STAILQ_NEXT(ve, next_ve) == NULL) {
+ asprintf(&str, "%s%s%s%s",
+ k->ki_d.prefix ? k->ki_d.prefix : "",
+ k->ki_p->ki_comm,
+ (showthreads && k->ki_p->ki_numthreads > 1) ? "/" : "",
+ (showthreads && k->ki_p->ki_numthreads > 1) ? k->ki_p->ki_tdname : "");
+ } else
+ str = strdup(k->ki_p->ki_comm);
+
+ return (str);
+ }
+ if ((vis_args = malloc(strlen(k->ki_args) * 4 + 1)) == NULL)
+ xo_errx(1, "malloc failed");
+ strvis(vis_args, k->ki_args, VIS_TAB | VIS_NL | VIS_NOSLASH);
+
+ if (STAILQ_NEXT(ve, next_ve) == NULL) {
+ /* last field */
+
+ if (k->ki_env) {
+ if ((vis_env = malloc(strlen(k->ki_env) * 4 + 1))
+ == NULL)
+ xo_errx(1, "malloc failed");
+ strvis(vis_env, k->ki_env,
+ VIS_TAB | VIS_NL | VIS_NOSLASH);
+ } else
+ vis_env = NULL;
+
+ asprintf(&str, "%s%s%s%s",
+ k->ki_d.prefix ? k->ki_d.prefix : "",
+ vis_env ? vis_env : "",
+ vis_env ? " " : "",
+ vis_args);
+
+ if (vis_env != NULL)
+ free(vis_env);
+ free(vis_args);
+ } else {
+ /* ki_d.prefix & ki_env aren't shown for interim fields */
+ str = vis_args;
+
+ if (strlen(str) > COMMAND_WIDTH)
+ str[COMMAND_WIDTH] = '\0';
+ }
+
+ return (str);
+}
+
+char *
+ucomm(KINFO *k, VARENT *ve)
+{
+ char *str;
+
+ if (STAILQ_NEXT(ve, next_ve) == NULL) { /* last field, don't pad */
+ asprintf(&str, "%s%s%s%s",
+ k->ki_d.prefix ? k->ki_d.prefix : "",
+ k->ki_p->ki_comm,
+ (showthreads && k->ki_p->ki_numthreads > 1) ? "/" : "",
+ (showthreads && k->ki_p->ki_numthreads > 1) ? k->ki_p->ki_tdname : "");
+ } else {
+ if (showthreads && k->ki_p->ki_numthreads > 1)
+ asprintf(&str, "%s/%s", k->ki_p->ki_comm, k->ki_p->ki_tdname);
+ else
+ str = strdup(k->ki_p->ki_comm);
+ }
+ return (str);
+}
+
+char *
+tdnam(KINFO *k, VARENT *ve __unused)
+{
+ char *str;
+
+ if (showthreads && k->ki_p->ki_numthreads > 1)
+ str = strdup(k->ki_p->ki_tdname);
+ else
+ str = strdup(" ");
+
+ return (str);
+}
+
+char *
+logname(KINFO *k, VARENT *ve __unused)
+{
+
+ if (*k->ki_p->ki_login == '\0')
+ return (NULL);
+ return (strdup(k->ki_p->ki_login));
+}
+
+char *
+state(KINFO *k, VARENT *ve __unused)
+{
+ int flag, tdflags;
+ char *cp, *buf;
+
+ buf = malloc(16);
+ if (buf == NULL)
+ xo_errx(1, "malloc failed");
+
+ flag = k->ki_p->ki_flag;
+ tdflags = k->ki_p->ki_tdflags; /* XXXKSE */
+ cp = buf;
+
+ switch (k->ki_p->ki_stat) {
+
+ case SSTOP:
+ *cp = 'T';
+ break;
+
+ case SSLEEP:
+ if (tdflags & TDF_SINTR) /* interruptable (long) */
+ *cp = k->ki_p->ki_slptime >= MAXSLP ? 'I' : 'S';
+ else
+ *cp = 'D';
+ break;
+
+ case SRUN:
+ case SIDL:
+ *cp = 'R';
+ break;
+
+ case SWAIT:
+ *cp = 'W';
+ break;
+
+ case SLOCK:
+ *cp = 'L';
+ break;
+
+ case SZOMB:
+ *cp = 'Z';
+ break;
+
+ default:
+ *cp = '?';
+ }
+ cp++;
+ if (!(flag & P_INMEM))
+ *cp++ = 'W';
+ if (k->ki_p->ki_nice < NZERO)
+ *cp++ = '<';
+ else if (k->ki_p->ki_nice > NZERO)
+ *cp++ = 'N';
+ if (flag & P_TRACED)
+ *cp++ = 'X';
+ if (flag & P_WEXIT && k->ki_p->ki_stat != SZOMB)
+ *cp++ = 'E';
+ if (flag & P_PPWAIT)
+ *cp++ = 'V';
+ if ((flag & P_SYSTEM) || k->ki_p->ki_lock > 0)
+ *cp++ = 'L';
+ if (k->ki_p->ki_kiflag & KI_SLEADER)
+ *cp++ = 's';
+ if ((flag & P_CONTROLT) && k->ki_p->ki_pgid == k->ki_p->ki_tpgid)
+ *cp++ = '+';
+ if (flag & P_JAILED)
+ *cp++ = 'J';
+ *cp = '\0';
+ return (buf);
+}
+
+#define scalepri(x) ((x) - PZERO)
+
+char *
+pri(KINFO *k, VARENT *ve __unused)
+{
+ char *str;
+
+ asprintf(&str, "%d", scalepri(k->ki_p->ki_pri.pri_level));
+ return (str);
+}
+
+char *
+upr(KINFO *k, VARENT *ve __unused)
+{
+ char *str;
+
+ asprintf(&str, "%d", scalepri(k->ki_p->ki_pri.pri_user));
+ return (str);
+}
+#undef scalepri
+
+char *
+uname(KINFO *k, VARENT *ve __unused)
+{
+
+ return (strdup(user_from_uid(k->ki_p->ki_uid, 0)));
+}
+
+char *
+egroupname(KINFO *k, VARENT *ve __unused)
+{
+
+ return (strdup(group_from_gid(k->ki_p->ki_groups[0], 0)));
+}
+
+char *
+rgroupname(KINFO *k, VARENT *ve __unused)
+{
+
+ return (strdup(group_from_gid(k->ki_p->ki_rgid, 0)));
+}
+
+char *
+runame(KINFO *k, VARENT *ve __unused)
+{
+
+ return (strdup(user_from_uid(k->ki_p->ki_ruid, 0)));
+}
+
+char *
+tdev(KINFO *k, VARENT *ve __unused)
+{
+ dev_t dev;
+ char *str;
+
+ dev = k->ki_p->ki_tdev;
+ if (dev == NODEV)
+ str = strdup("-");
+ else
+ asprintf(&str, "%#jx", (uintmax_t)dev);
+
+ return (str);
+}
+
+char *
+tname(KINFO *k, VARENT *ve __unused)
+{
+ dev_t dev;
+ char *ttname, *str;
+
+ dev = k->ki_p->ki_tdev;
+ if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL)
+ str = strdup("- ");
+ else {
+ if (strncmp(ttname, "tty", 3) == 0 ||
+ strncmp(ttname, "cua", 3) == 0)
+ ttname += 3;
+ if (strncmp(ttname, "pts/", 4) == 0)
+ ttname += 4;
+ asprintf(&str, "%s%c", ttname,
+ k->ki_p->ki_kiflag & KI_CTTY ? ' ' : '-');
+ }
+
+ return (str);
+}
+
+char *
+longtname(KINFO *k, VARENT *ve __unused)
+{
+ dev_t dev;
+ const char *ttname;
+
+ dev = k->ki_p->ki_tdev;
+ if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL)
+ ttname = "-";
+
+ return (strdup(ttname));
+}
+
+char *
+started(KINFO *k, VARENT *ve __unused)
+{
+ time_t then;
+ struct tm *tp;
+ size_t buflen = 100;
+ char *buf;
+
+ if (!k->ki_valid)
+ return (NULL);
+
+ buf = malloc(buflen);
+ if (buf == NULL)
+ xo_errx(1, "malloc failed");
+
+ then = k->ki_p->ki_start.tv_sec;
+ tp = localtime(&then);
+ if (now - k->ki_p->ki_start.tv_sec < 24 * 3600) {
+ (void)strftime(buf, buflen, "%H:%M ", tp);
+ } else if (now - k->ki_p->ki_start.tv_sec < 7 * 86400) {
+ (void)strftime(buf, buflen, "%a%H ", tp);
+ } else
+ (void)strftime(buf, buflen, "%e%b%y", tp);
+ return (buf);
+}
+
+char *
+lstarted(KINFO *k, VARENT *ve __unused)
+{
+ time_t then;
+ char *buf;
+ size_t buflen = 100;
+
+ if (!k->ki_valid)
+ return (NULL);
+
+ buf = malloc(buflen);
+ if (buf == NULL)
+ xo_errx(1, "malloc failed");
+
+ then = k->ki_p->ki_start.tv_sec;
+ (void)strftime(buf, buflen, "%c", localtime(&then));
+ return (buf);
+}
+
+char *
+lockname(KINFO *k, VARENT *ve __unused)
+{
+ char *str;
+
+ if (k->ki_p->ki_kiflag & KI_LOCKBLOCK) {
+ if (k->ki_p->ki_lockname[0] != 0)
+ str = strdup(k->ki_p->ki_lockname);
+ else
+ str = strdup("???");
+ } else
+ str = NULL;
+
+ return (str);
+}
+
+char *
+wchan(KINFO *k, VARENT *ve __unused)
+{
+ char *str;
+
+ if (k->ki_p->ki_wchan) {
+ if (k->ki_p->ki_wmesg[0] != 0)
+ str = strdup(k->ki_p->ki_wmesg);
+ else
+ asprintf(&str, "%lx", (long)k->ki_p->ki_wchan);
+ } else
+ str = NULL;
+
+ return (str);
+}
+
+char *
+nwchan(KINFO *k, VARENT *ve __unused)
+{
+ char *str;
+
+ if (k->ki_p->ki_wchan)
+ asprintf(&str, "%0lx", (long)k->ki_p->ki_wchan);
+ else
+ str = NULL;
+
+ return (str);
+}
+
+char *
+mwchan(KINFO *k, VARENT *ve __unused)
+{
+ char *str;
+
+ if (k->ki_p->ki_wchan) {
+ if (k->ki_p->ki_wmesg[0] != 0)
+ str = strdup(k->ki_p->ki_wmesg);
+ else
+ asprintf(&str, "%lx", (long)k->ki_p->ki_wchan);
+ } else if (k->ki_p->ki_kiflag & KI_LOCKBLOCK) {
+ if (k->ki_p->ki_lockname[0]) {
+ str = strdup(k->ki_p->ki_lockname);
+ } else
+ str = strdup("???");
+ } else
+ str = NULL;
+
+ return (str);
+}
+
+char *
+vsize(KINFO *k, VARENT *ve __unused)
+{
+ char *str;
+
+ asprintf(&str, "%lu", (u_long)(k->ki_p->ki_size / 1024));
+ return (str);
+}
+
+static char *
+printtime(KINFO *k, VARENT *ve __unused, long secs, long psecs)
+/* psecs is "parts" of a second. first micro, then centi */
+{
+ static char decimal_point;
+ char *str;
+
+ if (decimal_point == '\0')
+ decimal_point = localeconv()->decimal_point[0];
+ if (!k->ki_valid) {
+ secs = 0;
+ psecs = 0;
+ } else {
+ /* round and scale to 100's */
+ psecs = (psecs + 5000) / 10000;
+ secs += psecs / 100;
+ psecs = psecs % 100;
+ }
+ asprintf(&str, "%ld:%02ld%c%02ld",
+ secs / 60, secs % 60, decimal_point, psecs);
+ return (str);
+}
+
+char *
+cputime(KINFO *k, VARENT *ve)
+{
+ long secs, psecs;
+
+ /*
+ * This counts time spent handling interrupts. We could
+ * fix this, but it is not 100% trivial (and interrupt
+ * time fractions only work on the sparc anyway). XXX
+ */
+ secs = k->ki_p->ki_runtime / 1000000;
+ psecs = k->ki_p->ki_runtime % 1000000;
+ if (sumrusage) {
+ secs += k->ki_p->ki_childtime.tv_sec;
+ psecs += k->ki_p->ki_childtime.tv_usec;
+ }
+ return (printtime(k, ve, secs, psecs));
+}
+
+char *
+systime(KINFO *k, VARENT *ve)
+{
+ long secs, psecs;
+
+ secs = k->ki_p->ki_rusage.ru_stime.tv_sec;
+ psecs = k->ki_p->ki_rusage.ru_stime.tv_usec;
+ if (sumrusage) {
+ secs += k->ki_p->ki_childstime.tv_sec;
+ psecs += k->ki_p->ki_childstime.tv_usec;
+ }
+ return (printtime(k, ve, secs, psecs));
+}
+
+char *
+usertime(KINFO *k, VARENT *ve)
+{
+ long secs, psecs;
+
+ secs = k->ki_p->ki_rusage.ru_utime.tv_sec;
+ psecs = k->ki_p->ki_rusage.ru_utime.tv_usec;
+ if (sumrusage) {
+ secs += k->ki_p->ki_childutime.tv_sec;
+ psecs += k->ki_p->ki_childutime.tv_usec;
+ }
+ return (printtime(k, ve, secs, psecs));
+}
+
+char *
+elapsed(KINFO *k, VARENT *ve __unused)
+{
+ time_t val;
+ int days, hours, mins, secs;
+ char *str;
+
+ if (!k->ki_valid)
+ return (NULL);
+ val = now - k->ki_p->ki_start.tv_sec;
+ days = val / (24 * 60 * 60);
+ val %= 24 * 60 * 60;
+ hours = val / (60 * 60);
+ val %= 60 * 60;
+ mins = val / 60;
+ secs = val % 60;
+ if (days != 0)
+ asprintf(&str, "%3d-%02d:%02d:%02d", days, hours, mins, secs);
+ else if (hours != 0)
+ asprintf(&str, "%02d:%02d:%02d", hours, mins, secs);
+ else
+ asprintf(&str, "%02d:%02d", mins, secs);
+
+ return (str);
+}
+
+char *
+elapseds(KINFO *k, VARENT *ve __unused)
+{
+ time_t val;
+ char *str;
+
+ if (!k->ki_valid)
+ return (NULL);
+ val = now - k->ki_p->ki_start.tv_sec;
+ asprintf(&str, "%jd", (intmax_t)val);
+ return (str);
+}
+
+double
+getpcpu(const KINFO *k)
+{
+ static int failure;
+
+ if (!nlistread)
+ failure = donlist();
+ if (failure)
+ return (0.0);
+
+#define fxtofl(fixpt) ((double)(fixpt) / fscale)
+
+ /* XXX - I don't like this */
+ if (k->ki_p->ki_swtime == 0 || (k->ki_p->ki_flag & P_INMEM) == 0)
+ return (0.0);
+ if (rawcpu)
+ return (100.0 * fxtofl(k->ki_p->ki_pctcpu));
+ return (100.0 * fxtofl(k->ki_p->ki_pctcpu) /
+ (1.0 - exp(k->ki_p->ki_swtime * log(fxtofl(ccpu)))));
+}
+
+char *
+pcpu(KINFO *k, VARENT *ve __unused)
+{
+ char *str;
+
+ asprintf(&str, "%.1f", getpcpu(k));
+ return (str);
+}
+
+static double
+getpmem(KINFO *k)
+{
+ static int failure;
+ double fracmem;
+
+ if (!nlistread)
+ failure = donlist();
+ if (failure)
+ return (0.0);
+
+ if ((k->ki_p->ki_flag & P_INMEM) == 0)
+ return (0.0);
+ /* XXX want pmap ptpages, segtab, etc. (per architecture) */
+ /* XXX don't have info about shared */
+ fracmem = ((float)k->ki_p->ki_rssize) / mempages;
+ return (100.0 * fracmem);
+}
+
+char *
+pmem(KINFO *k, VARENT *ve __unused)
+{
+ char *str;
+
+ asprintf(&str, "%.1f", getpmem(k));
+ return (str);
+}
+
+char *
+pagein(KINFO *k, VARENT *ve __unused)
+{
+ char *str;
+
+ asprintf(&str, "%ld", k->ki_valid ? k->ki_p->ki_rusage.ru_majflt : 0);
+ return (str);
+}
+
+/* ARGSUSED */
+char *
+maxrss(KINFO *k __unused, VARENT *ve __unused)
+{
+
+ /* XXX not yet */
+ return (NULL);
+}
+
+char *
+priorityr(KINFO *k, VARENT *ve __unused)
+{
+ struct priority *lpri;
+ char *str;
+ unsigned class, level;
+
+ lpri = &k->ki_p->ki_pri;
+ class = lpri->pri_class;
+ level = lpri->pri_level;
+ switch (class) {
+ case PRI_ITHD:
+ asprintf(&str, "intr:%u", level);
+ break;
+ case PRI_REALTIME:
+ asprintf(&str, "real:%u", level);
+ break;
+ case PRI_TIMESHARE:
+ asprintf(&str, "normal");
+ break;
+ case PRI_IDLE:
+ asprintf(&str, "idle:%u", level);
+ break;
+ default:
+ asprintf(&str, "%u:%u", class, level);
+ break;
+ }
+ return (str);
+}
+
+/*
+ * Generic output routines. Print fields from various prototype
+ * structures.
+ */
+static char *
+printval(void *bp, VAR *v)
+{
+ static char ofmt[32] = "%";
+ const char *fcp;
+ char *cp, *str;
+
+ cp = ofmt + 1;
+ fcp = v->fmt;
+ while ((*cp++ = *fcp++));
+
+#define CHKINF127(n) (((n) > 127) && (v->flag & INF127) ? 127 : (n))
+
+ switch (v->type) {
+ case CHAR:
+ (void)asprintf(&str, ofmt, *(char *)bp);
+ break;
+ case UCHAR:
+ (void)asprintf(&str, ofmt, *(u_char *)bp);
+ break;
+ case SHORT:
+ (void)asprintf(&str, ofmt, *(short *)bp);
+ break;
+ case USHORT:
+ (void)asprintf(&str, ofmt, *(u_short *)bp);
+ break;
+ case INT:
+ (void)asprintf(&str, ofmt, *(int *)bp);
+ break;
+ case UINT:
+ (void)asprintf(&str, ofmt, CHKINF127(*(u_int *)bp));
+ break;
+ case LONG:
+ (void)asprintf(&str, ofmt, *(long *)bp);
+ break;
+ case ULONG:
+ (void)asprintf(&str, ofmt, *(u_long *)bp);
+ break;
+ case KPTR:
+ (void)asprintf(&str, ofmt, *(u_long *)bp);
+ break;
+ case PGTOK:
+ (void)asprintf(&str, ofmt, ps_pgtok(*(u_long *)bp));
+ break;
+ default:
+ xo_errx(1, "unknown type %d", v->type);
+ }
+
+ return (str);
+}
+
+char *
+kvar(KINFO *k, VARENT *ve)
+{
+ VAR *v;
+
+ v = ve->var;
+ return (printval((char *)((char *)k->ki_p + v->off), v));
+}
+
+char *
+rvar(KINFO *k, VARENT *ve)
+{
+ VAR *v;
+
+ v = ve->var;
+ if (!k->ki_valid)
+ return (NULL);
+ return (printval((char *)((char *)(&k->ki_p->ki_rusage) + v->off), v));
+}
+
+char *
+emulname(KINFO *k, VARENT *ve __unused)
+{
+
+ return (strdup(k->ki_p->ki_emul));
+}
+
+char *
+label(KINFO *k, VARENT *ve __unused)
+{
+ char *string;
+ mac_t proclabel;
+ int error;
+
+ string = NULL;
+ if (mac_prepare_process_label(&proclabel) == -1) {
+ xo_warn("mac_prepare_process_label");
+ goto out;
+ }
+ error = mac_get_pid(k->ki_p->ki_pid, proclabel);
+ if (error == 0) {
+ if (mac_to_text(proclabel, &string) == -1)
+ string = NULL;
+ }
+ mac_free(proclabel);
+out:
+ return (string);
+}
+
+char *
+loginclass(KINFO *k, VARENT *ve __unused)
+{
+
+ /*
+ * Don't display login class for system processes;
+ * login classes are used for resource limits,
+ * and limits don't apply to system processes.
+ */
+ if (k->ki_p->ki_flag & P_SYSTEM) {
+ return (strdup("-"));
+ }
+ return (strdup(k->ki_p->ki_loginclass));
+}
diff --git a/bin/ps/ps.1 b/bin/ps/ps.1
new file mode 100644
index 0000000..e00d313
--- /dev/null
+++ b/bin/ps/ps.1
@@ -0,0 +1,780 @@
+.\"-
+.\" Copyright (c) 1980, 1990, 1991, 1993, 1994
+.\" The Regents of the University of California. 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.
+.\" 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.
+.\"
+.\" @(#)ps.1 8.3 (Berkeley) 4/18/94
+.\" $FreeBSD$
+.\"
+.Dd December 1, 2015
+.Dt PS 1
+.Os
+.Sh NAME
+.Nm ps
+.Nd process status
+.Sh SYNOPSIS
+.Nm
+.Op Fl -libxo
+.Op Fl aCcdefHhjlmrSTuvwXxZ
+.Op Fl O Ar fmt | Fl o Ar fmt
+.Op Fl G Ar gid Ns Op , Ns Ar gid Ns Ar ...
+.Op Fl J Ar jid Ns Op , Ns Ar jid Ns Ar ...
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Op Fl p Ar pid Ns Op , Ns Ar pid Ns Ar ...
+.Op Fl t Ar tty Ns Op , Ns Ar tty Ns Ar ...
+.Op Fl U Ar user Ns Op , Ns Ar user Ns Ar ...
+.Nm
+.Op Fl -libxo
+.Op Fl L
+.Sh DESCRIPTION
+The
+.Nm
+utility
+displays a header line, followed by lines containing information about
+all of your
+processes that have controlling terminals.
+If the
+.Fl x
+options is specified,
+.Nm
+will also display processes that do not have controlling terminals.
+.Pp
+A different set of processes can be selected for display by using any
+combination of the
+.Fl a , G , J , p , T , t ,
+and
+.Fl U
+options.
+If more than one of these options are given, then
+.Nm
+will select all processes which are matched by at least one of the
+given options.
+.Pp
+For the processes which have been selected for display,
+.Nm
+will usually display one line per process.
+The
+.Fl H
+option may result in multiple output lines (one line per thread) for
+some processes.
+By default all of these output lines are sorted first by controlling
+terminal, then by process ID.
+The
+.Fl m , r , u ,
+and
+.Fl v
+options will change the sort order.
+If more than one sorting option was given, then the selected processes
+will be sorted by the last sorting option which was specified.
+.Pp
+For the processes which have been selected for display, the information
+to display is selected based on a set of keywords (see the
+.Fl L , O ,
+and
+.Fl o
+options).
+The default output format includes, for each process, the process' ID,
+controlling terminal, state, CPU time (including both user and system time)
+and associated command.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl -libxo
+Generate output via
+.Xr libxo 3
+in a selection of different human and machine readable formats.
+See
+.Xr xo_parse_args 3
+for details on command line arguments.
+.It Fl a
+Display information about other users' processes as well as your own.
+If the
+.Va security.bsd.see_other_uids
+sysctl is set to zero, this option is honored only if the UID of the user is 0.
+.It Fl c
+Change the
+.Dq command
+column output to just contain the executable name,
+rather than the full command line.
+.It Fl C
+Change the way the CPU percentage is calculated by using a
+.Dq raw
+CPU calculation that ignores
+.Dq resident
+time (this normally has
+no effect).
+.It Fl d
+Arrange processes into descendancy order and prefix each command with
+indentation text showing sibling and parent/child relationships.
+If either of the
+.Fl m
+and
+.Fl r
+options are also used, they control how sibling processes are sorted
+relative to each other.
+Note that this option has no effect if the
+.Dq command
+column is not the last column displayed.
+.It Fl e
+Display the environment as well.
+.It Fl f
+Show command-line and environment information about swapped out processes.
+This option is honored only if the UID of the user is 0.
+.It Fl G
+Display information about processes which are running with the specified
+real group IDs.
+.It Fl H
+Show all of the
+.Em kernel visible
+threads associated with each process.
+Depending on the threading package that
+is in use, this may show only the process, only the kernel scheduled entities,
+or all of the process threads.
+.It Fl h
+Repeat the information header as often as necessary to guarantee one
+header per page of information.
+.It Fl j
+Print information associated with the following keywords:
+.Cm user , pid , ppid , pgid , sid , jobc , state , tt , time ,
+and
+.Cm command .
+.It Fl J
+Display information about processes which match the specified jail IDs.
+This may be either the
+.Cm jid
+or
+.Cm name
+of the jail.
+Use
+.Fl J
+.Sy 0
+to display only host processes.
+This flag implies
+.Fl x
+by default.
+.It Fl L
+List the set of keywords available for the
+.Fl O
+and
+.Fl o
+options.
+.It Fl l
+Display information associated with the following keywords:
+.Cm uid , pid , ppid , cpu , pri , nice , vsz , rss , mwchan , state ,
+.Cm tt , time ,
+and
+.Cm command .
+.It Fl M
+Extract values associated with the name list from the specified core
+instead of the currently running system.
+.It Fl m
+Sort by memory usage, instead of the combination of controlling
+terminal and process ID.
+.It Fl N
+Extract the name list from the specified system instead of the default,
+which is the kernel image the system has booted from.
+.It Fl O
+Add the information associated with the space or comma separated list
+of keywords specified, after the process ID,
+in the default information
+display.
+Keywords may be appended with an equals
+.Pq Ql =
+sign and a string.
+This causes the printed header to use the specified string instead of
+the standard header.
+.It Fl o
+Display information associated with the space or comma separated
+list of keywords specified.
+The last keyword in the list may be appended with an equals
+.Pq Ql =
+sign and a string that spans the rest of the argument, and can contain
+space and comma characters.
+This causes the printed header to use the specified string instead of
+the standard header.
+Multiple keywords may also be given in the form of more than one
+.Fl o
+option.
+So the header texts for multiple keywords can be changed.
+If all keywords have empty header texts, no header line is written.
+.It Fl p
+Display information about processes which match the specified process IDs.
+.It Fl r
+Sort by current CPU usage, instead of the combination of controlling
+terminal and process ID.
+.It Fl S
+Change the way the process times, namely cputime, systime, and usertime,
+are calculated by summing all exited children to their parent process.
+.It Fl T
+Display information about processes attached to the device associated
+with the standard input.
+.It Fl t
+Display information about processes attached to the specified terminal
+devices.
+Full pathnames, as well as abbreviations (see explanation of the
+.Cm tt
+keyword) can be specified.
+.It Fl U
+Display the processes belonging to the specified usernames.
+.It Fl u
+Display information associated with the following keywords:
+.Cm user , pid , %cpu , %mem , vsz , rss , tt , state , start , time ,
+and
+.Cm command .
+The
+.Fl u
+option implies the
+.Fl r
+option.
+.It Fl v
+Display information associated with the following keywords:
+.Cm pid , state , time , sl , re , pagein , vsz , rss , lim , tsiz ,
+.Cm %cpu , %mem ,
+and
+.Cm command .
+The
+.Fl v
+option implies the
+.Fl m
+option.
+.It Fl w
+Use 132 columns to display information, instead of the default which
+is your window size.
+If the
+.Fl w
+option is specified more than once,
+.Nm
+will use as many columns as necessary without regard for your window size.
+Note that this option has no effect if the
+.Dq command
+column is not the last column displayed.
+.It Fl X
+When displaying processes matched by other options, skip any processes
+which do not have a controlling terminal.
+This is the default behaviour.
+.It Fl x
+When displaying processes matched by other options, include processes
+which do not have a controlling terminal.
+This is the opposite of the
+.Fl X
+option.
+If both
+.Fl X
+and
+.Fl x
+are specified in the same command, then
+.Nm
+will use the one which was specified last.
+.It Fl Z
+Add
+.Xr mac 4
+label to the list of keywords for which
+.Nm
+will display information.
+.El
+.Pp
+A complete list of the available keywords are listed below.
+Some of these keywords are further specified as follows:
+.Bl -tag -width lockname
+.It Cm %cpu
+The CPU utilization of the process; this is a decaying average over up to
+a minute of previous (real) time.
+Since the time base over which this is computed varies (since processes may
+be very young) it is possible for the sum of all
+.Cm %cpu
+fields to exceed 100%.
+.It Cm %mem
+The percentage of real memory used by this process.
+.It Cm class
+Login class associated with the process.
+.It Cm flags
+The flags associated with the process as in
+the include file
+.In sys/proc.h :
+.Bl -column P_SINGLE_BOUNDARY 0x40000000
+.It Dv "P_ADVLOCK" Ta No "0x00001" Ta "Process may hold a POSIX advisory lock"
+.It Dv "P_CONTROLT" Ta No "0x00002" Ta "Has a controlling terminal"
+.It Dv "P_KTHREAD" Ta No "0x00004" Ta "Kernel thread"
+.It Dv "P_FOLLOWFORK" Ta No "0x00008" Ta "Attach debugger to new children"
+.It Dv "P_PPWAIT" Ta No "0x00010" Ta "Parent is waiting for child to exec/exit"
+.It Dv "P_PROFIL" Ta No "0x00020" Ta "Has started profiling"
+.It Dv "P_STOPPROF" Ta No "0x00040" Ta "Has thread in requesting to stop prof"
+.It Dv "P_HADTHREADS" Ta No "0x00080" Ta "Has had threads (no cleanup shortcuts)"
+.It Dv "P_SUGID" Ta No "0x00100" Ta "Had set id privileges since last exec"
+.It Dv "P_SYSTEM" Ta No "0x00200" Ta "System proc: no sigs, stats or swapping"
+.It Dv "P_SINGLE_EXIT" Ta No "0x00400" Ta "Threads suspending should exit, not wait"
+.It Dv "P_TRACED" Ta No "0x00800" Ta "Debugged process being traced"
+.It Dv "P_WAITED" Ta No "0x01000" Ta "Someone is waiting for us"
+.It Dv "P_WEXIT" Ta No "0x02000" Ta "Working on exiting"
+.It Dv "P_EXEC" Ta No "0x04000" Ta "Process called exec"
+.It Dv "P_WKILLED" Ta No "0x08000" Ta "Killed, shall go to kernel/user boundary ASAP"
+.It Dv "P_CONTINUED" Ta No "0x10000" Ta "Proc has continued from a stopped state"
+.It Dv "P_STOPPED_SIG" Ta No "0x20000" Ta "Stopped due to SIGSTOP/SIGTSTP"
+.It Dv "P_STOPPED_TRACE" Ta No "0x40000" Ta "Stopped because of tracing"
+.It Dv "P_STOPPED_SINGLE" Ta No "0x80000" Ta "Only one thread can continue"
+.It Dv "P_PROTECTED" Ta No "0x100000" Ta "Do not kill on memory overcommit"
+.It Dv "P_SIGEVENT" Ta No "0x200000" Ta "Process pending signals changed"
+.It Dv "P_SINGLE_BOUNDARY" Ta No "0x400000" Ta "Threads should suspend at user boundary"
+.It Dv "P_HWPMC" Ta No "0x800000" Ta "Process is using HWPMCs"
+.It Dv "P_JAILED" Ta No "0x1000000" Ta "Process is in jail"
+.It Dv "P_TOTAL_STOP" Ta No "0x2000000" Ta "Stopped for system suspend"
+.It Dv "P_INEXEC" Ta No "0x4000000" Ta "Process is in execve()"
+.It Dv "P_STATCHILD" Ta No "0x8000000" Ta "Child process stopped or exited"
+.It Dv "P_INMEM" Ta No "0x10000000" Ta "Loaded into memory"
+.It Dv "P_SWAPPINGOUT" Ta No "0x20000000" Ta "Process is being swapped out"
+.It Dv "P_SWAPPINGIN" Ta No "0x40000000" Ta "Process is being swapped in"
+.It Dv "P_PPTRACE" Ta No "0x80000000" Ta "Vforked child issued ptrace(PT_TRACEME)"
+.El
+.It Cm flags2
+The flags kept in
+.Va p_flag2
+associated with the process as in
+the include file
+.In sys/proc.h :
+.Bl -column P2_INHERIT_PROTECTED 0x00000001
+.It Dv "P2_INHERIT_PROTECTED" Ta No "0x00000001" Ta "New children get P_PROTECTED"
+.It Dv "P2_NOTRACE" Ta No "0x00000002" Ta "No ptrace(2) attach or coredumps"
+.It Dv "P2_NOTRACE_EXEC" Ta No "0x00000004" Ta "Keep P2_NOPTRACE on exec(2)"
+.It Dv "P2_AST_SU" Ta No "0x00000008" Ta "Handles SU ast for kthreads"
+.El
+.It Cm label
+The MAC label of the process.
+.It Cm lim
+The soft limit on memory used, specified via a call to
+.Xr setrlimit 2 .
+.It Cm lstart
+The exact time the command started, using the
+.Ql %c
+format described in
+.Xr strftime 3 .
+.It Cm lockname
+The name of the lock that the process is currently blocked on.
+If the name is invalid or unknown, then
+.Dq ???\&
+is displayed.
+.It Cm logname
+The login name associated with the session the process is in (see
+.Xr getlogin 2 ) .
+.It Cm mwchan
+The event name if the process is blocked normally, or the lock name if
+the process is blocked on a lock.
+See the wchan and lockname keywords
+for details.
+.It Cm nice
+The process scheduling increment (see
+.Xr setpriority 2 ) .
+.It Cm rss
+the real memory (resident set) size of the process (in 1024 byte units).
+.It Cm start
+The time the command started.
+If the command started less than 24 hours ago, the start time is
+displayed using the
+.Dq Li %H:%M
+format described in
+.Xr strftime 3 .
+If the command started less than 7 days ago, the start time is
+displayed using the
+.Dq Li %a%H
+format.
+Otherwise, the start time is displayed using the
+.Dq Li %e%b%y
+format.
+.It Cm state
+The state is given by a sequence of characters, for example,
+.Dq Li RWNA .
+The first character indicates the run state of the process:
+.Pp
+.Bl -tag -width indent -compact
+.It Li D
+Marks a process in disk (or other short term, uninterruptible) wait.
+.It Li I
+Marks a process that is idle (sleeping for longer than about 20 seconds).
+.It Li L
+Marks a process that is waiting to acquire a lock.
+.It Li R
+Marks a runnable process.
+.It Li S
+Marks a process that is sleeping for less than about 20 seconds.
+.It Li T
+Marks a stopped process.
+.It Li W
+Marks an idle interrupt thread.
+.It Li Z
+Marks a dead process (a
+.Dq zombie ) .
+.El
+.Pp
+Additional characters after these, if any, indicate additional state
+information:
+.Pp
+.Bl -tag -width indent -compact
+.It Li +
+The process is in the foreground process group of its control terminal.
+.It Li <
+The process has raised CPU scheduling priority.
+.It Li E
+The process is trying to exit.
+.It Li J
+Marks a process which is in
+.Xr jail 2 .
+The hostname of the prison can be found in
+.Pa /proc/ Ns Ao Ar pid Ac Ns Pa /status .
+.It Li L
+The process has pages locked in core (for example, for raw
+.Tn I/O ) .
+.It Li N
+The process has reduced CPU scheduling priority (see
+.Xr setpriority 2 ) .
+.It Li s
+The process is a session leader.
+.It Li V
+The process' parent is suspended during a
+.Xr vfork 2 ,
+waiting for the process to exec or exit.
+.It Li W
+The process is swapped out.
+.It Li X
+The process is being traced or debugged.
+.El
+.It Cm tt
+An abbreviation for the pathname of the controlling terminal, if any.
+The abbreviation consists of the three letters following
+.Pa /dev/tty ,
+or, for pseudo-terminals, the corresponding entry in
+.Pa /dev/pts .
+This is followed by a
+.Ql -
+if the process can no longer reach that
+controlling terminal (i.e., it has been revoked).
+A
+.Ql -
+without a preceding two letter abbreviation or pseudo-terminal device number
+indicates a process which never had a controlling terminal.
+The full pathname of the controlling terminal is available via the
+.Cm tty
+keyword.
+.It Cm wchan
+The event (an address in the system) on which a process waits.
+When printed numerically, the initial part of the address is
+trimmed off and the result is printed in hex, for example, 0x80324000 prints
+as 324000.
+.El
+.Pp
+When printing using the command keyword, a process that has exited and
+has a parent that has not yet waited for the process (in other words, a zombie)
+is listed as
+.Dq Li <defunct> ,
+and a process which is blocked while trying
+to exit is listed as
+.Dq Li <exiting> .
+If the arguments cannot be located (usually because it has not been set, as is
+the case of system processes and/or kernel threads) the command name is printed
+within square brackets.
+The
+.Nm
+utility first tries to obtain the arguments cached by the kernel (if they were
+shorter than the value of the
+.Va kern.ps_arg_cache_limit
+sysctl).
+The process can change the arguments shown with
+.Xr setproctitle 3 .
+Otherwise,
+.Nm
+makes an educated guess as to the file name and arguments given when the
+process was created by examining memory or the swap area.
+The method is inherently somewhat unreliable and in any event a process
+is entitled to destroy this information.
+The ucomm (accounting) keyword can, however, be depended on.
+If the arguments are unavailable or do not agree with the ucomm keyword,
+the value for the ucomm keyword is appended to the arguments in parentheses.
+.Sh KEYWORDS
+The following is a complete list of the available keywords and their
+meanings.
+Several of them have aliases (keywords which are synonyms).
+.Pp
+.Bl -tag -width ".Cm sigignore" -compact
+.It Cm %cpu
+percentage CPU usage (alias
+.Cm pcpu )
+.It Cm %mem
+percentage memory usage (alias
+.Cm pmem )
+.It Cm acflag
+accounting flag (alias
+.Cm acflg )
+.It Cm args
+command and arguments
+.It Cm class
+login class
+.It Cm comm
+command
+.It Cm command
+command and arguments
+.It Cm cow
+number of copy-on-write faults
+.It Cm cpu
+short-term CPU usage factor (for scheduling)
+.It Cm dsiz
+data size (in Kbytes)
+.It Cm emul
+system-call emulation environment
+.It Cm etime
+elapsed running time, format
+.Op days- Ns
+.Op hours: Ns
+minutes:seconds.
+.It Cm etimes
+elapsed running time, in decimal integer seconds
+.It Cm fib
+default FIB number, see
+.Xr setfib 1
+.It Cm flags
+the process flags, in hexadecimal (alias
+.Cm f )
+.It Cm flags2
+the additional set of process flags, in hexadecimal (alias
+.Cm f2 )
+.It Cm gid
+effective group ID (alias
+.Cm egid )
+.It Cm group
+group name (from egid) (alias
+.Cm egroup )
+.It Cm inblk
+total blocks read (alias
+.Cm inblock )
+.It Cm jid
+jail ID
+.It Cm jobc
+job control count
+.It Cm ktrace
+tracing flags
+.It Cm label
+MAC label
+.It Cm lim
+memoryuse limit
+.It Cm lockname
+lock currently blocked on (as a symbolic name)
+.It Cm logname
+login name of user who started the session
+.It Cm lstart
+time started
+.It Cm lwp
+process thread-id
+.It Cm majflt
+total page faults
+.It Cm minflt
+total page reclaims
+.It Cm msgrcv
+total messages received (reads from pipes/sockets)
+.It Cm msgsnd
+total messages sent (writes on pipes/sockets)
+.It Cm mwchan
+wait channel or lock currently blocked on
+.It Cm nice
+nice value (alias
+.Cm ni )
+.It Cm nivcsw
+total involuntary context switches
+.It Cm nlwp
+number of threads tied to a process
+.It Cm nsigs
+total signals taken (alias
+.Cm nsignals )
+.It Cm nswap
+total swaps in/out
+.It Cm nvcsw
+total voluntary context switches
+.It Cm nwchan
+wait channel (as an address)
+.It Cm oublk
+total blocks written (alias
+.Cm oublock )
+.It Cm paddr
+process pointer
+.It Cm pagein
+pageins (same as majflt)
+.It Cm pgid
+process group number
+.It Cm pid
+process ID
+.It Cm ppid
+parent process ID
+.It Cm pri
+scheduling priority
+.It Cm re
+core residency time (in seconds; 127 = infinity)
+.It Cm rgid
+real group ID
+.It Cm rgroup
+group name (from rgid)
+.It Cm rss
+resident set size
+.It Cm rtprio
+realtime priority (101 = not a realtime process)
+.It Cm ruid
+real user ID
+.It Cm ruser
+user name (from ruid)
+.It Cm sid
+session ID
+.It Cm sig
+pending signals (alias
+.Cm pending )
+.It Cm sigcatch
+caught signals (alias
+.Cm caught )
+.It Cm sigignore
+ignored signals (alias
+.Cm ignored )
+.It Cm sigmask
+blocked signals (alias
+.Cm blocked )
+.It Cm sl
+sleep time (in seconds; 127 = infinity)
+.It Cm ssiz
+stack size (in Kbytes)
+.It Cm start
+time started
+.It Cm state
+symbolic process state (alias
+.Cm stat )
+.It Cm svgid
+saved gid from a setgid executable
+.It Cm svuid
+saved UID from a setuid executable
+.It Cm systime
+accumulated system CPU time
+.It Cm tdaddr
+thread address
+.It Cm tdev
+control terminal device number
+.It Cm time
+accumulated CPU time, user + system (alias
+.Cm cputime )
+.It Cm tpgid
+control terminal process group ID
+.It Cm tracer
+tracer process ID
+.\".It Cm trss
+.\"text resident set size (in Kbytes)
+.It Cm tsid
+control terminal session ID
+.It Cm tsiz
+text size (in Kbytes)
+.It Cm tt
+control terminal name (two letter abbreviation)
+.It Cm tty
+full name of control terminal
+.It Cm ucomm
+name to be used for accounting
+.It Cm uid
+effective user ID (alias
+.Cm euid )
+.It Cm upr
+scheduling priority on return from system call (alias
+.Cm usrpri )
+.It Cm uprocp
+process pointer
+.It Cm user
+user name (from UID)
+.It Cm usertime
+accumulated user CPU time
+.It Cm vsz
+virtual size in Kbytes (alias
+.Cm vsize )
+.It Cm wchan
+wait channel (as a symbolic name)
+.It Cm xstat
+exit or stop status (valid only for stopped or zombie process)
+.El
+.Pp
+Note that the
+.Cm pending
+column displays bitmask of signals pending in the process queue when
+.Fl H
+option is not specified, otherwise the per-thread queue of pending signals
+is shown.
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm :
+.Bl -tag -width ".Ev COLUMNS"
+.It Ev COLUMNS
+If set, specifies the user's preferred output width in column positions.
+By default,
+.Nm
+attempts to automatically determine the terminal width.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /boot/kernel/kernel" -compact
+.It Pa /boot/kernel/kernel
+default system namelist
+.El
+.Sh EXAMPLES
+Display information on all system processes:
+.Pp
+.Dl $ ps -auxw
+.Sh SEE ALSO
+.Xr kill 1 ,
+.Xr pgrep 1 ,
+.Xr pkill 1 ,
+.Xr procstat 1 ,
+.Xr w 1 ,
+.Xr kvm 3 ,
+.Xr libxo 3 ,
+.Xr strftime 3 ,
+.Xr xo_parse_args 3 ,
+.Xr mac 4 ,
+.Xr procfs 5 ,
+.Xr pstat 8 ,
+.Xr sysctl 8 ,
+.Xr mutex 9
+.Sh STANDARDS
+For historical reasons, the
+.Nm
+utility under
+.Fx
+supports a different set of options from what is described by
+.St -p1003.2 ,
+and what is supported on
+.No non- Ns Bx
+operating systems.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.At v4 .
+.Sh BUGS
+Since
+.Nm
+cannot run faster than the system and is run as any other scheduled
+process, the information it displays can never be exact.
+.Pp
+The
+.Nm
+utility does not correctly display argument lists containing multibyte
+characters.
diff --git a/bin/ps/ps.c b/bin/ps/ps.c
new file mode 100644
index 0000000..e86cfa8
--- /dev/null
+++ b/bin/ps/ps.c
@@ -0,0 +1,1438 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. 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.
+ * 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.
+ * ------+---------+---------+-------- + --------+---------+---------+---------*
+ * Copyright (c) 2004 - Garance Alistair Drosehn <gad@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Significant modifications made to bring `ps' options somewhat closer
+ * to the standard for `ps' as described in SingleUnixSpec-v3.
+ * ------+---------+---------+-------- + --------+---------+---------+---------*
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <sys/mount.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <jail.h>
+#include <kvm.h>
+#include <limits.h>
+#include <locale.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libxo/xo.h>
+
+#include "ps.h"
+
+#define _PATH_PTS "/dev/pts/"
+
+#define W_SEP " \t" /* "Whitespace" list separators */
+#define T_SEP "," /* "Terminate-element" list separators */
+
+#ifdef LAZY_PS
+#define DEF_UREAD 0
+#define OPT_LAZY_f "f"
+#else
+#define DEF_UREAD 1 /* Always do the more-expensive read. */
+#define OPT_LAZY_f /* I.e., the `-f' option is not added. */
+#endif
+
+/*
+ * isdigit takes an `int', but expects values in the range of unsigned char.
+ * This wrapper ensures that values from a 'char' end up in the correct range.
+ */
+#define isdigitch(Anychar) isdigit((u_char)(Anychar))
+
+int cflag; /* -c */
+int eval; /* Exit value */
+time_t now; /* Current time(3) value */
+int rawcpu; /* -C */
+int sumrusage; /* -S */
+int termwidth; /* Width of the screen (0 == infinity). */
+int showthreads; /* will threads be shown? */
+
+struct velisthead varlist = STAILQ_HEAD_INITIALIZER(varlist);
+
+static int forceuread = DEF_UREAD; /* Do extra work to get u-area. */
+static kvm_t *kd;
+static int needcomm; /* -o "command" */
+static int needenv; /* -e */
+static int needuser; /* -o "user" */
+static int optfatal; /* Fatal error parsing some list-option. */
+static int pid_max; /* kern.max_pid */
+
+static enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
+
+struct listinfo;
+typedef int addelem_rtn(struct listinfo *_inf, const char *_elem);
+
+struct listinfo {
+ int count;
+ int maxcount;
+ int elemsize;
+ addelem_rtn *addelem;
+ const char *lname;
+ union {
+ gid_t *gids;
+ int *jids;
+ pid_t *pids;
+ dev_t *ttys;
+ uid_t *uids;
+ void *ptr;
+ } l;
+};
+
+static int addelem_gid(struct listinfo *, const char *);
+static int addelem_jid(struct listinfo *, const char *);
+static int addelem_pid(struct listinfo *, const char *);
+static int addelem_tty(struct listinfo *, const char *);
+static int addelem_uid(struct listinfo *, const char *);
+static void add_list(struct listinfo *, const char *);
+static void descendant_sort(KINFO *, int);
+static void format_output(KINFO *);
+static void *expand_list(struct listinfo *);
+static const char *
+ fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int),
+ KINFO *, char *, char *, int);
+static void free_list(struct listinfo *);
+static void init_list(struct listinfo *, addelem_rtn, int, const char *);
+static char *kludge_oldps_options(const char *, char *, const char *);
+static int pscomp(const void *, const void *);
+static void saveuser(KINFO *);
+static void scanvars(void);
+static void sizevars(void);
+static void pidmax_init(void);
+static void usage(void);
+
+static char dfmt[] = "pid,tt,state,time,command";
+static char jfmt[] = "user,pid,ppid,pgid,sid,jobc,state,tt,time,command";
+static char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state,"
+ "tt,time,command";
+static char o1[] = "pid";
+static char o2[] = "tt,state,time,command";
+static char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command";
+static char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz,"
+ "%cpu,%mem,command";
+static char Zfmt[] = "label";
+
+#define PS_ARGS "AaCcde" OPT_LAZY_f "G:gHhjJ:LlM:mN:O:o:p:rSTt:U:uvwXxZ"
+
+int
+main(int argc, char *argv[])
+{
+ struct listinfo gidlist, jidlist, pgrplist, pidlist;
+ struct listinfo ruidlist, sesslist, ttylist, uidlist;
+ struct kinfo_proc *kp;
+ KINFO *kinfo = NULL, *next_KINFO;
+ KINFO_STR *ks;
+ struct varent *vent;
+ struct winsize ws = { .ws_row = 0 };
+ const char *nlistf, *memf, *str;
+ char *cols;
+ int all, ch, elem, flag, _fmt, i, lineno, linelen, left;
+ int descendancy, nentries, nkept, nselectors;
+ int prtheader, wflag, what, xkeep, xkeep_implied;
+ int fwidthmin, fwidthmax;
+ char errbuf[_POSIX2_LINE_MAX];
+ char fmtbuf[_POSIX2_LINE_MAX];
+
+ (void) setlocale(LC_ALL, "");
+ time(&now); /* Used by routines in print.c. */
+
+ if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0')
+ termwidth = atoi(cols);
+ else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
+ ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
+ ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) ||
+ ws.ws_col == 0)
+ termwidth = 79;
+ else
+ termwidth = ws.ws_col - 1;
+
+ /*
+ * Hide a number of option-processing kludges in a separate routine,
+ * to support some historical BSD behaviors, such as `ps axu'.
+ */
+ if (argc > 1)
+ argv[1] = kludge_oldps_options(PS_ARGS, argv[1], argv[2]);
+
+ pidmax_init();
+
+ all = descendancy = _fmt = nselectors = optfatal = 0;
+ prtheader = showthreads = wflag = xkeep_implied = 0;
+ xkeep = -1; /* Neither -x nor -X. */
+ init_list(&gidlist, addelem_gid, sizeof(gid_t), "group");
+ init_list(&jidlist, addelem_jid, sizeof(int), "jail id");
+ init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group");
+ init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id");
+ init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser");
+ init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id");
+ init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty");
+ init_list(&uidlist, addelem_uid, sizeof(uid_t), "user");
+ memf = _PATH_DEVNULL;
+ nlistf = NULL;
+
+ argc = xo_parse_args(argc, argv);
+ if (argc < 0)
+ exit(1);
+
+ while ((ch = getopt(argc, argv, PS_ARGS)) != -1)
+ switch (ch) {
+ case 'A':
+ /*
+ * Exactly the same as `-ax'. This has been
+ * added for compatibility with SUSv3, but for
+ * now it will not be described in the man page.
+ */
+ nselectors++;
+ all = xkeep = 1;
+ break;
+ case 'a':
+ nselectors++;
+ all = 1;
+ break;
+ case 'C':
+ rawcpu = 1;
+ break;
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd':
+ descendancy = 1;
+ break;
+ case 'e': /* XXX set ufmt */
+ needenv = 1;
+ break;
+#ifdef LAZY_PS
+ case 'f':
+ if (getuid() == 0 || getgid() == 0)
+ forceuread = 1;
+ break;
+#endif
+ case 'G':
+ add_list(&gidlist, optarg);
+ xkeep_implied = 1;
+ nselectors++;
+ break;
+ case 'g':
+#if 0
+ /*-
+ * XXX - This SUSv3 behavior is still under debate
+ * since it conflicts with the (undocumented)
+ * `-g' option. So we skip it for now.
+ */
+ add_list(&pgrplist, optarg);
+ xkeep_implied = 1;
+ nselectors++;
+ break;
+#else
+ /* The historical BSD-ish (from SunOS) behavior. */
+ break; /* no-op */
+#endif
+ case 'H':
+ showthreads = KERN_PROC_INC_THREAD;
+ break;
+ case 'h':
+ prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
+ break;
+ case 'J':
+ add_list(&jidlist, optarg);
+ xkeep_implied = 1;
+ nselectors++;
+ break;
+ case 'j':
+ parsefmt(jfmt, 0);
+ _fmt = 1;
+ jfmt[0] = '\0';
+ break;
+ case 'L':
+ showkey();
+ exit(0);
+ case 'l':
+ parsefmt(lfmt, 0);
+ _fmt = 1;
+ lfmt[0] = '\0';
+ break;
+ case 'M':
+ memf = optarg;
+ break;
+ case 'm':
+ sortby = SORTMEM;
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'O':
+ parsefmt(o1, 1);
+ parsefmt(optarg, 1);
+ parsefmt(o2, 1);
+ o1[0] = o2[0] = '\0';
+ _fmt = 1;
+ break;
+ case 'o':
+ parsefmt(optarg, 1);
+ _fmt = 1;
+ break;
+ case 'p':
+ add_list(&pidlist, optarg);
+ /*
+ * Note: `-p' does not *set* xkeep, but any values
+ * from pidlist are checked before xkeep is. That
+ * way they are always matched, even if the user
+ * specifies `-X'.
+ */
+ nselectors++;
+ break;
+#if 0
+ case 'R':
+ /*-
+ * XXX - This un-standard option is still under
+ * debate. This is what SUSv3 defines as
+ * the `-U' option, and while it would be
+ * nice to have, it could cause even more
+ * confusion to implement it as `-R'.
+ */
+ add_list(&ruidlist, optarg);
+ xkeep_implied = 1;
+ nselectors++;
+ break;
+#endif
+ case 'r':
+ sortby = SORTCPU;
+ break;
+ case 'S':
+ sumrusage = 1;
+ break;
+#if 0
+ case 's':
+ /*-
+ * XXX - This non-standard option is still under
+ * debate. This *is* supported on Solaris,
+ * Linux, and IRIX, but conflicts with `-s'
+ * on NetBSD and maybe some older BSD's.
+ */
+ add_list(&sesslist, optarg);
+ xkeep_implied = 1;
+ nselectors++;
+ break;
+#endif
+ case 'T':
+ if ((optarg = ttyname(STDIN_FILENO)) == NULL)
+ xo_errx(1, "stdin: not a terminal");
+ /* FALLTHROUGH */
+ case 't':
+ add_list(&ttylist, optarg);
+ xkeep_implied = 1;
+ nselectors++;
+ break;
+ case 'U':
+ /* This is what SUSv3 defines as the `-u' option. */
+ add_list(&uidlist, optarg);
+ xkeep_implied = 1;
+ nselectors++;
+ break;
+ case 'u':
+ parsefmt(ufmt, 0);
+ sortby = SORTCPU;
+ _fmt = 1;
+ ufmt[0] = '\0';
+ break;
+ case 'v':
+ parsefmt(vfmt, 0);
+ sortby = SORTMEM;
+ _fmt = 1;
+ vfmt[0] = '\0';
+ break;
+ case 'w':
+ if (wflag)
+ termwidth = UNLIMITED;
+ else if (termwidth < 131)
+ termwidth = 131;
+ wflag++;
+ break;
+ case 'X':
+ /*
+ * Note that `-X' and `-x' are not standard "selector"
+ * options. For most selector-options, we check *all*
+ * processes to see if any are matched by the given
+ * value(s). After we have a set of all the matched
+ * processes, then `-X' and `-x' govern whether we
+ * modify that *matched* set for processes which do
+ * not have a controlling terminal. `-X' causes
+ * those processes to be deleted from the matched
+ * set, while `-x' causes them to be kept.
+ */
+ xkeep = 0;
+ break;
+ case 'x':
+ xkeep = 1;
+ break;
+ case 'Z':
+ parsefmt(Zfmt, 0);
+ Zfmt[0] = '\0';
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * If there arguments after processing all the options, attempt
+ * to treat them as a list of process ids.
+ */
+ while (*argv) {
+ if (!isdigitch(**argv))
+ break;
+ add_list(&pidlist, *argv);
+ argv++;
+ }
+ if (*argv) {
+ xo_warnx("illegal argument: %s\n", *argv);
+ usage();
+ }
+ if (optfatal)
+ exit(1); /* Error messages already printed. */
+ if (xkeep < 0) /* Neither -X nor -x was specified. */
+ xkeep = xkeep_implied;
+
+ kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
+ if (kd == 0)
+ xo_errx(1, "%s", errbuf);
+
+ if (!_fmt)
+ parsefmt(dfmt, 0);
+
+ if (nselectors == 0) {
+ uidlist.l.ptr = malloc(sizeof(uid_t));
+ if (uidlist.l.ptr == NULL)
+ xo_errx(1, "malloc failed");
+ nselectors = 1;
+ uidlist.count = uidlist.maxcount = 1;
+ *uidlist.l.uids = getuid();
+ }
+
+ /*
+ * scan requested variables, noting what structures are needed,
+ * and adjusting header widths as appropriate.
+ */
+ scanvars();
+
+ /*
+ * Get process list. If the user requested just one selector-
+ * option, then kvm_getprocs can be asked to return just those
+ * processes. Otherwise, have it return all processes, and
+ * then this routine will search that full list and select the
+ * processes which match any of the user's selector-options.
+ */
+ what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC;
+ flag = 0;
+ if (nselectors == 1) {
+ if (gidlist.count == 1) {
+ what = KERN_PROC_RGID | showthreads;
+ flag = *gidlist.l.gids;
+ nselectors = 0;
+ } else if (pgrplist.count == 1) {
+ what = KERN_PROC_PGRP | showthreads;
+ flag = *pgrplist.l.pids;
+ nselectors = 0;
+ } else if (pidlist.count == 1) {
+ what = KERN_PROC_PID | showthreads;
+ flag = *pidlist.l.pids;
+ nselectors = 0;
+ } else if (ruidlist.count == 1) {
+ what = KERN_PROC_RUID | showthreads;
+ flag = *ruidlist.l.uids;
+ nselectors = 0;
+ } else if (sesslist.count == 1) {
+ what = KERN_PROC_SESSION | showthreads;
+ flag = *sesslist.l.pids;
+ nselectors = 0;
+ } else if (ttylist.count == 1) {
+ what = KERN_PROC_TTY | showthreads;
+ flag = *ttylist.l.ttys;
+ nselectors = 0;
+ } else if (uidlist.count == 1) {
+ what = KERN_PROC_UID | showthreads;
+ flag = *uidlist.l.uids;
+ nselectors = 0;
+ } else if (all) {
+ /* No need for this routine to select processes. */
+ nselectors = 0;
+ }
+ }
+
+ /*
+ * select procs
+ */
+ nentries = -1;
+ kp = kvm_getprocs(kd, what, flag, &nentries);
+ if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0))
+ xo_errx(1, "%s", kvm_geterr(kd));
+ nkept = 0;
+ if (nentries > 0) {
+ if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
+ xo_errx(1, "malloc failed");
+ for (i = nentries; --i >= 0; ++kp) {
+ /*
+ * If the user specified multiple selection-criteria,
+ * then keep any process matched by the inclusive OR
+ * of all the selection-criteria given.
+ */
+ if (pidlist.count > 0) {
+ for (elem = 0; elem < pidlist.count; elem++)
+ if (kp->ki_pid == pidlist.l.pids[elem])
+ goto keepit;
+ }
+ /*
+ * Note that we had to process pidlist before
+ * filtering out processes which do not have
+ * a controlling terminal.
+ */
+ if (xkeep == 0) {
+ if ((kp->ki_tdev == NODEV ||
+ (kp->ki_flag & P_CONTROLT) == 0))
+ continue;
+ }
+ if (nselectors == 0)
+ goto keepit;
+ if (gidlist.count > 0) {
+ for (elem = 0; elem < gidlist.count; elem++)
+ if (kp->ki_rgid == gidlist.l.gids[elem])
+ goto keepit;
+ }
+ if (jidlist.count > 0) {
+ for (elem = 0; elem < jidlist.count; elem++)
+ if (kp->ki_jid == jidlist.l.jids[elem])
+ goto keepit;
+ }
+ if (pgrplist.count > 0) {
+ for (elem = 0; elem < pgrplist.count; elem++)
+ if (kp->ki_pgid ==
+ pgrplist.l.pids[elem])
+ goto keepit;
+ }
+ if (ruidlist.count > 0) {
+ for (elem = 0; elem < ruidlist.count; elem++)
+ if (kp->ki_ruid ==
+ ruidlist.l.uids[elem])
+ goto keepit;
+ }
+ if (sesslist.count > 0) {
+ for (elem = 0; elem < sesslist.count; elem++)
+ if (kp->ki_sid == sesslist.l.pids[elem])
+ goto keepit;
+ }
+ if (ttylist.count > 0) {
+ for (elem = 0; elem < ttylist.count; elem++)
+ if (kp->ki_tdev == ttylist.l.ttys[elem])
+ goto keepit;
+ }
+ if (uidlist.count > 0) {
+ for (elem = 0; elem < uidlist.count; elem++)
+ if (kp->ki_uid == uidlist.l.uids[elem])
+ goto keepit;
+ }
+ /*
+ * This process did not match any of the user's
+ * selector-options, so skip the process.
+ */
+ continue;
+
+ keepit:
+ next_KINFO = &kinfo[nkept];
+ next_KINFO->ki_p = kp;
+ next_KINFO->ki_d.level = 0;
+ next_KINFO->ki_d.prefix = NULL;
+ next_KINFO->ki_pcpu = getpcpu(next_KINFO);
+ if (sortby == SORTMEM)
+ next_KINFO->ki_memsize = kp->ki_tsize +
+ kp->ki_dsize + kp->ki_ssize;
+ if (needuser)
+ saveuser(next_KINFO);
+ nkept++;
+ }
+ }
+
+ sizevars();
+
+ if (nkept == 0) {
+ printheader();
+ exit(1);
+ }
+
+ /*
+ * sort proc list
+ */
+ qsort(kinfo, nkept, sizeof(KINFO), pscomp);
+
+ /*
+ * We want things in descendant order
+ */
+ if (descendancy)
+ descendant_sort(kinfo, nkept);
+
+
+ /*
+ * Prepare formatted output.
+ */
+ for (i = 0; i < nkept; i++)
+ format_output(&kinfo[i]);
+
+ /*
+ * Print header.
+ */
+ xo_open_container("process-information");
+ printheader();
+ if (xo_get_style(NULL) != XO_STYLE_TEXT)
+ termwidth = UNLIMITED;
+
+ /*
+ * Output formatted lines.
+ */
+ xo_open_list("process");
+ for (i = lineno = 0; i < nkept; i++) {
+ linelen = 0;
+ xo_open_instance("process");
+ STAILQ_FOREACH(vent, &varlist, next_ve) {
+ ks = STAILQ_FIRST(&kinfo[i].ki_ks);
+ STAILQ_REMOVE_HEAD(&kinfo[i].ki_ks, ks_next);
+ /* Truncate rightmost column if necessary. */
+ fwidthmax = _POSIX2_LINE_MAX;
+ if (STAILQ_NEXT(vent, next_ve) == NULL &&
+ termwidth != UNLIMITED && ks->ks_str != NULL) {
+ left = termwidth - linelen;
+ if (left > 0 && left < (int)strlen(ks->ks_str))
+ fwidthmax = left;
+ }
+
+ str = ks->ks_str;
+ if (str == NULL)
+ str = "-";
+ /* No padding for the last column, if it's LJUST. */
+ fwidthmin = (xo_get_style(NULL) != XO_STYLE_TEXT ||
+ (STAILQ_NEXT(vent, next_ve) == NULL &&
+ (vent->var->flag & LJUST))) ? 0 : vent->var->width;
+ snprintf(fmtbuf, sizeof(fmtbuf), "{:%s/%%%s%d..%ds}",
+ vent->var->field ?: vent->var->name,
+ (vent->var->flag & LJUST) ? "-" : "",
+ fwidthmin, fwidthmax);
+ xo_emit(fmtbuf, str);
+ linelen += fwidthmin;
+
+ if (ks->ks_str != NULL) {
+ free(ks->ks_str);
+ ks->ks_str = NULL;
+ }
+ free(ks);
+ ks = NULL;
+
+ if (STAILQ_NEXT(vent, next_ve) != NULL) {
+ xo_emit("{P: }");
+ linelen++;
+ }
+ }
+ xo_emit("\n");
+ xo_close_instance("process");
+ if (prtheader && lineno++ == prtheader - 4) {
+ xo_emit("\n");
+ printheader();
+ lineno = 0;
+ }
+ }
+ xo_close_list("process");
+ xo_close_container("process-information");
+ xo_finish();
+
+ free_list(&gidlist);
+ free_list(&jidlist);
+ free_list(&pidlist);
+ free_list(&pgrplist);
+ free_list(&ruidlist);
+ free_list(&sesslist);
+ free_list(&ttylist);
+ free_list(&uidlist);
+ for (i = 0; i < nkept; i++)
+ free(kinfo[i].ki_d.prefix);
+ free(kinfo);
+
+ exit(eval);
+}
+
+static int
+addelem_gid(struct listinfo *inf, const char *elem)
+{
+ struct group *grp;
+ const char *nameorID;
+ char *endp;
+ u_long bigtemp;
+
+ if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
+ if (*elem == '\0')
+ xo_warnx("Invalid (zero-length) %s name", inf->lname);
+ else
+ xo_warnx("%s name too long: %s", inf->lname, elem);
+ optfatal = 1;
+ return (0); /* Do not add this value. */
+ }
+
+ /*
+ * SUSv3 states that `ps -G grouplist' should match "real-group
+ * ID numbers", and does not mention group-names. I do want to
+ * also support group-names, so this tries for a group-id first,
+ * and only tries for a name if that doesn't work. This is the
+ * opposite order of what is done in addelem_uid(), but in
+ * practice the order would only matter for group-names which
+ * are all-numeric.
+ */
+ grp = NULL;
+ nameorID = "named";
+ errno = 0;
+ bigtemp = strtoul(elem, &endp, 10);
+ if (errno == 0 && *endp == '\0' && bigtemp <= GID_MAX) {
+ nameorID = "name or ID matches";
+ grp = getgrgid((gid_t)bigtemp);
+ }
+ if (grp == NULL)
+ grp = getgrnam(elem);
+ if (grp == NULL) {
+ xo_warnx("No %s %s '%s'", inf->lname, nameorID, elem);
+ optfatal = 1;
+ return (0);
+ }
+ if (inf->count >= inf->maxcount)
+ expand_list(inf);
+ inf->l.gids[(inf->count)++] = grp->gr_gid;
+ return (1);
+}
+
+static int
+addelem_jid(struct listinfo *inf, const char *elem)
+{
+ int tempid;
+
+ if (*elem == '\0') {
+ warnx("Invalid (zero-length) jail id");
+ optfatal = 1;
+ return (0); /* Do not add this value. */
+ }
+
+ tempid = jail_getid(elem);
+ if (tempid < 0) {
+ warnx("Invalid %s: %s", inf->lname, elem);
+ optfatal = 1;
+ return (0);
+ }
+
+ if (inf->count >= inf->maxcount)
+ expand_list(inf);
+ inf->l.jids[(inf->count)++] = tempid;
+ return (1);
+}
+
+static int
+addelem_pid(struct listinfo *inf, const char *elem)
+{
+ char *endp;
+ long tempid;
+
+ if (*elem == '\0') {
+ xo_warnx("Invalid (zero-length) process id");
+ optfatal = 1;
+ return (0); /* Do not add this value. */
+ }
+
+ errno = 0;
+ tempid = strtol(elem, &endp, 10);
+ if (*endp != '\0' || tempid < 0 || elem == endp) {
+ xo_warnx("Invalid %s: %s", inf->lname, elem);
+ errno = ERANGE;
+ } else if (errno != 0 || tempid > pid_max) {
+ xo_warnx("%s too large: %s", inf->lname, elem);
+ errno = ERANGE;
+ }
+ if (errno == ERANGE) {
+ optfatal = 1;
+ return (0);
+ }
+ if (inf->count >= inf->maxcount)
+ expand_list(inf);
+ inf->l.pids[(inf->count)++] = tempid;
+ return (1);
+}
+
+/*-
+ * The user can specify a device via one of three formats:
+ * 1) fully qualified, e.g.: /dev/ttyp0 /dev/console /dev/pts/0
+ * 2) missing "/dev", e.g.: ttyp0 console pts/0
+ * 3) two-letters, e.g.: p0 co 0
+ * (matching letters that would be seen in the "TT" column)
+ */
+static int
+addelem_tty(struct listinfo *inf, const char *elem)
+{
+ const char *ttypath;
+ struct stat sb;
+ char pathbuf[PATH_MAX], pathbuf2[PATH_MAX], pathbuf3[PATH_MAX];
+
+ ttypath = NULL;
+ pathbuf2[0] = '\0';
+ pathbuf3[0] = '\0';
+ switch (*elem) {
+ case '/':
+ ttypath = elem;
+ break;
+ case 'c':
+ if (strcmp(elem, "co") == 0) {
+ ttypath = _PATH_CONSOLE;
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ strlcpy(pathbuf, _PATH_DEV, sizeof(pathbuf));
+ strlcat(pathbuf, elem, sizeof(pathbuf));
+ ttypath = pathbuf;
+ if (strncmp(pathbuf, _PATH_TTY, strlen(_PATH_TTY)) == 0)
+ break;
+ if (strncmp(pathbuf, _PATH_PTS, strlen(_PATH_PTS)) == 0)
+ break;
+ if (strcmp(pathbuf, _PATH_CONSOLE) == 0)
+ break;
+ /* Check to see if /dev/tty${elem} exists */
+ strlcpy(pathbuf2, _PATH_TTY, sizeof(pathbuf2));
+ strlcat(pathbuf2, elem, sizeof(pathbuf2));
+ if (stat(pathbuf2, &sb) == 0 && S_ISCHR(sb.st_mode)) {
+ /* No need to repeat stat() && S_ISCHR() checks */
+ ttypath = NULL;
+ break;
+ }
+ /* Check to see if /dev/pts/${elem} exists */
+ strlcpy(pathbuf3, _PATH_PTS, sizeof(pathbuf3));
+ strlcat(pathbuf3, elem, sizeof(pathbuf3));
+ if (stat(pathbuf3, &sb) == 0 && S_ISCHR(sb.st_mode)) {
+ /* No need to repeat stat() && S_ISCHR() checks */
+ ttypath = NULL;
+ break;
+ }
+ break;
+ }
+ if (ttypath) {
+ if (stat(ttypath, &sb) == -1) {
+ if (pathbuf3[0] != '\0')
+ xo_warn("%s, %s, and %s", pathbuf3, pathbuf2,
+ ttypath);
+ else
+ xo_warn("%s", ttypath);
+ optfatal = 1;
+ return (0);
+ }
+ if (!S_ISCHR(sb.st_mode)) {
+ if (pathbuf3[0] != '\0')
+ xo_warnx("%s, %s, and %s: Not a terminal",
+ pathbuf3, pathbuf2, ttypath);
+ else
+ xo_warnx("%s: Not a terminal", ttypath);
+ optfatal = 1;
+ return (0);
+ }
+ }
+ if (inf->count >= inf->maxcount)
+ expand_list(inf);
+ inf->l.ttys[(inf->count)++] = sb.st_rdev;
+ return (1);
+}
+
+static int
+addelem_uid(struct listinfo *inf, const char *elem)
+{
+ struct passwd *pwd;
+ char *endp;
+ u_long bigtemp;
+
+ if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
+ if (*elem == '\0')
+ xo_warnx("Invalid (zero-length) %s name", inf->lname);
+ else
+ xo_warnx("%s name too long: %s", inf->lname, elem);
+ optfatal = 1;
+ return (0); /* Do not add this value. */
+ }
+
+ pwd = getpwnam(elem);
+ if (pwd == NULL) {
+ errno = 0;
+ bigtemp = strtoul(elem, &endp, 10);
+ if (errno != 0 || *endp != '\0' || bigtemp > UID_MAX)
+ xo_warnx("No %s named '%s'", inf->lname, elem);
+ else {
+ /* The string is all digits, so it might be a userID. */
+ pwd = getpwuid((uid_t)bigtemp);
+ if (pwd == NULL)
+ xo_warnx("No %s name or ID matches '%s'",
+ inf->lname, elem);
+ }
+ }
+ if (pwd == NULL) {
+ /*
+ * These used to be treated as minor warnings (and the
+ * option was simply ignored), but now they are fatal
+ * errors (and the command will be aborted).
+ */
+ optfatal = 1;
+ return (0);
+ }
+ if (inf->count >= inf->maxcount)
+ expand_list(inf);
+ inf->l.uids[(inf->count)++] = pwd->pw_uid;
+ return (1);
+}
+
+static void
+add_list(struct listinfo *inf, const char *argp)
+{
+ const char *savep;
+ char *cp, *endp;
+ int toolong;
+ char elemcopy[PATH_MAX];
+
+ if (*argp == '\0')
+ inf->addelem(inf, argp);
+ while (*argp != '\0') {
+ while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
+ argp++;
+ savep = argp;
+ toolong = 0;
+ cp = elemcopy;
+ if (strchr(T_SEP, *argp) == NULL) {
+ endp = elemcopy + sizeof(elemcopy) - 1;
+ while (*argp != '\0' && cp <= endp &&
+ strchr(W_SEP T_SEP, *argp) == NULL)
+ *cp++ = *argp++;
+ if (cp > endp)
+ toolong = 1;
+ }
+ if (!toolong) {
+ *cp = '\0';
+ /*
+ * Add this single element to the given list.
+ */
+ inf->addelem(inf, elemcopy);
+ } else {
+ /*
+ * The string is too long to copy. Find the end
+ * of the string to print out the warning message.
+ */
+ while (*argp != '\0' && strchr(W_SEP T_SEP,
+ *argp) == NULL)
+ argp++;
+ xo_warnx("Value too long: %.*s", (int)(argp - savep),
+ savep);
+ optfatal = 1;
+ }
+ /*
+ * Skip over any number of trailing whitespace characters,
+ * but only one (at most) trailing element-terminating
+ * character.
+ */
+ while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
+ argp++;
+ if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) {
+ argp++;
+ /* Catch case where string ended with a comma. */
+ if (*argp == '\0')
+ inf->addelem(inf, argp);
+ }
+ }
+}
+
+static void
+descendant_sort(KINFO *ki, int items)
+{
+ int dst, lvl, maxlvl, n, ndst, nsrc, siblings, src;
+ unsigned char *path;
+ KINFO kn;
+
+ /*
+ * First, sort the entries by descendancy, tracking the descendancy
+ * depth in the ki_d.level field.
+ */
+ src = 0;
+ maxlvl = 0;
+ while (src < items) {
+ if (ki[src].ki_d.level) {
+ src++;
+ continue;
+ }
+ for (nsrc = 1; src + nsrc < items; nsrc++)
+ if (!ki[src + nsrc].ki_d.level)
+ break;
+
+ for (dst = 0; dst < items; dst++) {
+ if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_pid)
+ continue;
+ if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_ppid)
+ break;
+ }
+
+ if (dst == items) {
+ src += nsrc;
+ continue;
+ }
+
+ for (ndst = 1; dst + ndst < items; ndst++)
+ if (ki[dst + ndst].ki_d.level <= ki[dst].ki_d.level)
+ break;
+
+ for (n = src; n < src + nsrc; n++) {
+ ki[n].ki_d.level += ki[dst].ki_d.level + 1;
+ if (maxlvl < ki[n].ki_d.level)
+ maxlvl = ki[n].ki_d.level;
+ }
+
+ while (nsrc) {
+ if (src < dst) {
+ kn = ki[src];
+ memmove(ki + src, ki + src + 1,
+ (dst - src + ndst - 1) * sizeof *ki);
+ ki[dst + ndst - 1] = kn;
+ nsrc--;
+ dst--;
+ ndst++;
+ } else if (src != dst + ndst) {
+ kn = ki[src];
+ memmove(ki + dst + ndst + 1, ki + dst + ndst,
+ (src - dst - ndst) * sizeof *ki);
+ ki[dst + ndst] = kn;
+ ndst++;
+ nsrc--;
+ src++;
+ } else {
+ ndst += nsrc;
+ src += nsrc;
+ nsrc = 0;
+ }
+ }
+ }
+
+ /*
+ * Now populate ki_d.prefix (instead of ki_d.level) with the command
+ * prefix used to show descendancies.
+ */
+ path = malloc((maxlvl + 7) / 8);
+ memset(path, '\0', (maxlvl + 7) / 8);
+ for (src = 0; src < items; src++) {
+ if ((lvl = ki[src].ki_d.level) == 0) {
+ ki[src].ki_d.prefix = NULL;
+ continue;
+ }
+ if ((ki[src].ki_d.prefix = malloc(lvl * 2 + 1)) == NULL)
+ xo_errx(1, "malloc failed");
+ for (n = 0; n < lvl - 2; n++) {
+ ki[src].ki_d.prefix[n * 2] =
+ path[n / 8] & 1 << (n % 8) ? '|' : ' ';
+ ki[src].ki_d.prefix[n * 2 + 1] = ' ';
+ }
+ if (n == lvl - 2) {
+ /* Have I any more siblings? */
+ for (siblings = 0, dst = src + 1; dst < items; dst++) {
+ if (ki[dst].ki_d.level > lvl)
+ continue;
+ if (ki[dst].ki_d.level == lvl)
+ siblings = 1;
+ break;
+ }
+ if (siblings)
+ path[n / 8] |= 1 << (n % 8);
+ else
+ path[n / 8] &= ~(1 << (n % 8));
+ ki[src].ki_d.prefix[n * 2] = siblings ? '|' : '`';
+ ki[src].ki_d.prefix[n * 2 + 1] = '-';
+ n++;
+ }
+ strcpy(ki[src].ki_d.prefix + n * 2, "- ");
+ }
+ free(path);
+}
+
+static void *
+expand_list(struct listinfo *inf)
+{
+ void *newlist;
+ int newmax;
+
+ newmax = (inf->maxcount + 1) << 1;
+ newlist = realloc(inf->l.ptr, newmax * inf->elemsize);
+ if (newlist == NULL) {
+ free(inf->l.ptr);
+ xo_errx(1, "realloc to %d %ss failed", newmax, inf->lname);
+ }
+ inf->maxcount = newmax;
+ inf->l.ptr = newlist;
+
+ return (newlist);
+}
+
+static void
+free_list(struct listinfo *inf)
+{
+
+ inf->count = inf->elemsize = inf->maxcount = 0;
+ if (inf->l.ptr != NULL)
+ free(inf->l.ptr);
+ inf->addelem = NULL;
+ inf->lname = NULL;
+ inf->l.ptr = NULL;
+}
+
+static void
+init_list(struct listinfo *inf, addelem_rtn artn, int elemsize,
+ const char *lname)
+{
+
+ inf->count = inf->maxcount = 0;
+ inf->elemsize = elemsize;
+ inf->addelem = artn;
+ inf->lname = lname;
+ inf->l.ptr = NULL;
+}
+
+VARENT *
+find_varentry(VAR *v)
+{
+ struct varent *vent;
+
+ STAILQ_FOREACH(vent, &varlist, next_ve) {
+ if (strcmp(vent->var->name, v->name) == 0)
+ return vent;
+ }
+ return NULL;
+}
+
+static void
+scanvars(void)
+{
+ struct varent *vent;
+ VAR *v;
+
+ STAILQ_FOREACH(vent, &varlist, next_ve) {
+ v = vent->var;
+ if (v->flag & USER)
+ needuser = 1;
+ if (v->flag & COMM)
+ needcomm = 1;
+ }
+}
+
+static void
+format_output(KINFO *ki)
+{
+ struct varent *vent;
+ VAR *v;
+ KINFO_STR *ks;
+ char *str;
+ int len;
+
+ STAILQ_INIT(&ki->ki_ks);
+ STAILQ_FOREACH(vent, &varlist, next_ve) {
+ v = vent->var;
+ str = (v->oproc)(ki, vent);
+ ks = malloc(sizeof(*ks));
+ if (ks == NULL)
+ xo_errx(1, "malloc failed");
+ ks->ks_str = str;
+ STAILQ_INSERT_TAIL(&ki->ki_ks, ks, ks_next);
+ if (str != NULL) {
+ len = strlen(str);
+ } else
+ len = 1; /* "-" */
+ if (v->width < len)
+ v->width = len;
+ }
+}
+
+static void
+sizevars(void)
+{
+ struct varent *vent;
+ VAR *v;
+ int i;
+
+ STAILQ_FOREACH(vent, &varlist, next_ve) {
+ v = vent->var;
+ i = strlen(vent->header);
+ if (v->width < i)
+ v->width = i;
+ }
+}
+
+static const char *
+fmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki,
+ char *comm, char *thread, int maxlen)
+{
+ const char *s;
+
+ s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm,
+ showthreads && ki->ki_p->ki_numthreads > 1 ? thread : NULL, maxlen);
+ return (s);
+}
+
+#define UREADOK(ki) (forceuread || (ki->ki_p->ki_flag & P_INMEM))
+
+static void
+saveuser(KINFO *ki)
+{
+
+ if (ki->ki_p->ki_flag & P_INMEM) {
+ /*
+ * The u-area might be swapped out, and we can't get
+ * at it because we have a crashdump and no swap.
+ * If it's here fill in these fields, otherwise, just
+ * leave them 0.
+ */
+ ki->ki_valid = 1;
+ } else
+ ki->ki_valid = 0;
+ /*
+ * save arguments if needed
+ */
+ if (needcomm) {
+ if (ki->ki_p->ki_stat == SZOMB)
+ ki->ki_args = strdup("<defunct>");
+ else if (UREADOK(ki) || (ki->ki_p->ki_args != NULL))
+ ki->ki_args = strdup(fmt(kvm_getargv, ki,
+ ki->ki_p->ki_comm, ki->ki_p->ki_tdname, MAXCOMLEN));
+ else
+ asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm);
+ if (ki->ki_args == NULL)
+ xo_errx(1, "malloc failed");
+ } else {
+ ki->ki_args = NULL;
+ }
+ if (needenv) {
+ if (UREADOK(ki))
+ ki->ki_env = strdup(fmt(kvm_getenvv, ki,
+ (char *)NULL, (char *)NULL, 0));
+ else
+ ki->ki_env = strdup("()");
+ if (ki->ki_env == NULL)
+ xo_errx(1, "malloc failed");
+ } else {
+ ki->ki_env = NULL;
+ }
+}
+
+/* A macro used to improve the readability of pscomp(). */
+#define DIFF_RETURN(a, b, field) do { \
+ if ((a)->field != (b)->field) \
+ return (((a)->field < (b)->field) ? -1 : 1); \
+} while (0)
+
+static int
+pscomp(const void *a, const void *b)
+{
+ const KINFO *ka, *kb;
+
+ ka = a;
+ kb = b;
+ /* SORTCPU and SORTMEM are sorted in descending order. */
+ if (sortby == SORTCPU)
+ DIFF_RETURN(kb, ka, ki_pcpu);
+ if (sortby == SORTMEM)
+ DIFF_RETURN(kb, ka, ki_memsize);
+ /*
+ * TTY's are sorted in ascending order, except that all NODEV
+ * processes come before all processes with a device.
+ */
+ if (ka->ki_p->ki_tdev != kb->ki_p->ki_tdev) {
+ if (ka->ki_p->ki_tdev == NODEV)
+ return (-1);
+ if (kb->ki_p->ki_tdev == NODEV)
+ return (1);
+ DIFF_RETURN(ka, kb, ki_p->ki_tdev);
+ }
+
+ /* PID's and TID's (threads) are sorted in ascending order. */
+ DIFF_RETURN(ka, kb, ki_p->ki_pid);
+ DIFF_RETURN(ka, kb, ki_p->ki_tid);
+ return (0);
+}
+#undef DIFF_RETURN
+
+/*
+ * ICK (all for getopt), would rather hide the ugliness
+ * here than taint the main code.
+ *
+ * ps foo -> ps -foo
+ * ps 34 -> ps -p34
+ *
+ * The old convention that 't' with no trailing tty arg means the users
+ * tty, is only supported if argv[1] doesn't begin with a '-'. This same
+ * feature is available with the option 'T', which takes no argument.
+ */
+static char *
+kludge_oldps_options(const char *optlist, char *origval, const char *nextarg)
+{
+ size_t len;
+ char *argp, *cp, *newopts, *ns, *optp, *pidp;
+
+ /*
+ * See if the original value includes any option which takes an
+ * argument (and will thus use up the remainder of the string).
+ */
+ argp = NULL;
+ if (optlist != NULL) {
+ for (cp = origval; *cp != '\0'; cp++) {
+ optp = strchr(optlist, *cp);
+ if ((optp != NULL) && *(optp + 1) == ':') {
+ argp = cp;
+ break;
+ }
+ }
+ }
+ if (argp != NULL && *origval == '-')
+ return (origval);
+
+ /*
+ * if last letter is a 't' flag with no argument (in the context
+ * of the oldps options -- option string NOT starting with a '-' --
+ * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
+ *
+ * However, if a flag accepting a string argument is found earlier
+ * in the option string (including a possible `t' flag), then the
+ * remainder of the string must be the argument to that flag; so
+ * do not modify that argument. Note that a trailing `t' would
+ * cause argp to be set, if argp was not already set by some
+ * earlier option.
+ */
+ len = strlen(origval);
+ cp = origval + len - 1;
+ pidp = NULL;
+ if (*cp == 't' && *origval != '-' && cp == argp) {
+ if (nextarg == NULL || *nextarg == '-' || isdigitch(*nextarg))
+ *cp = 'T';
+ } else if (argp == NULL) {
+ /*
+ * The original value did not include any option which takes
+ * an argument (and that would include `p' and `t'), so check
+ * the value for trailing number, or comma-separated list of
+ * numbers, which we will treat as a pid request.
+ */
+ if (isdigitch(*cp)) {
+ while (cp >= origval && (*cp == ',' || isdigitch(*cp)))
+ --cp;
+ pidp = cp + 1;
+ }
+ }
+
+ /*
+ * If nothing needs to be added to the string, then return
+ * the "original" (although possibly modified) value.
+ */
+ if (*origval == '-' && pidp == NULL)
+ return (origval);
+
+ /*
+ * Create a copy of the string to add '-' and/or 'p' to the
+ * original value.
+ */
+ if ((newopts = ns = malloc(len + 3)) == NULL)
+ xo_errx(1, "malloc failed");
+
+ if (*origval != '-')
+ *ns++ = '-'; /* add option flag */
+
+ if (pidp == NULL)
+ strcpy(ns, origval);
+ else {
+ /*
+ * Copy everything before the pid string, add the `p',
+ * and then copy the pid string.
+ */
+ len = pidp - origval;
+ memcpy(ns, origval, len);
+ ns += len;
+ *ns++ = 'p';
+ strcpy(ns, pidp);
+ }
+
+ return (newopts);
+}
+
+static void
+pidmax_init(void)
+{
+ size_t intsize;
+
+ intsize = sizeof(pid_max);
+ if (sysctlbyname("kern.pid_max", &pid_max, &intsize, NULL, 0) < 0) {
+ xo_warn("unable to read kern.pid_max");
+ pid_max = 99999;
+ }
+}
+
+static void
+usage(void)
+{
+#define SINGLE_OPTS "[-aCcde" OPT_LAZY_f "HhjlmrSTuvwXxZ]"
+
+ (void)xo_error("%s\n%s\n%s\n%s\n",
+ "usage: ps " SINGLE_OPTS " [-O fmt | -o fmt] [-G gid[,gid...]]",
+ " [-J jid[,jid...]] [-M core] [-N system]",
+ " [-p pid[,pid...]] [-t tty[,tty...]] [-U user[,user...]]",
+ " ps [-L]");
+ exit(1);
+}
diff --git a/bin/ps/ps.h b/bin/ps/ps.h
new file mode 100644
index 0000000..314fbf2
--- /dev/null
+++ b/bin/ps/ps.h
@@ -0,0 +1,88 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. 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.
+ * 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.
+ *
+ * @(#)ps.h 8.1 (Berkeley) 5/31/93
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+
+#define UNLIMITED 0 /* unlimited terminal width */
+enum type { CHAR, UCHAR, SHORT, USHORT, INT, UINT, LONG, ULONG, KPTR, PGTOK };
+
+typedef struct kinfo_str {
+ STAILQ_ENTRY(kinfo_str) ks_next;
+ char *ks_str; /* formatted string */
+} KINFO_STR;
+
+typedef struct kinfo {
+ struct kinfo_proc *ki_p; /* kinfo_proc structure */
+ char *ki_args; /* exec args */
+ char *ki_env; /* environment */
+ int ki_valid; /* 1 => uarea stuff valid */
+ double ki_pcpu; /* calculated in main() */
+ segsz_t ki_memsize; /* calculated in main() */
+ union {
+ int level; /* used in decendant_sort() */
+ char *prefix; /* calculated in decendant_sort() */
+ } ki_d;
+ STAILQ_HEAD(, kinfo_str) ki_ks;
+} KINFO;
+
+/* Variables. */
+typedef struct varent {
+ STAILQ_ENTRY(varent) next_ve;
+ const char *header;
+ struct var *var;
+} VARENT;
+
+typedef struct var {
+ const char *name; /* name(s) of variable */
+ const char *header; /* default header */
+ const char *alias; /* aliases */
+ const char *field; /* xo field name */
+#define COMM 0x01 /* needs exec arguments and environment (XXX) */
+#define LJUST 0x02 /* left adjust on output (trailing blanks) */
+#define USER 0x04 /* needs user structure */
+#define INF127 0x10 /* values >127 displayed as 127 */
+ u_int flag;
+ /* output routine */
+ char *(*oproc)(struct kinfo *, struct varent *);
+ /*
+ * The following (optional) elements are hooks for passing information
+ * to the generic output routine pvar (which prints simple elements
+ * from the well known kinfo_proc structure).
+ */
+ size_t off; /* offset in structure */
+ enum type type; /* type of element */
+ const char *fmt; /* printf format */
+
+ short width; /* calculated width */
+} VAR;
+
+#include "extern.h"
OpenPOWER on IntegriCloud