summaryrefslogtreecommitdiff
path: root/bin/ps
diff options
context:
space:
mode:
Diffstat (limited to 'bin/ps')
-rw-r--r--bin/ps/extern.h99
-rw-r--r--bin/ps/fmt.c60
-rw-r--r--bin/ps/keyword.c415
-rw-r--r--bin/ps/nlist.c234
-rw-r--r--bin/ps/print.c1413
-rw-r--r--bin/ps/ps.1706
-rw-r--r--bin/ps/ps.c947
-rw-r--r--bin/ps/ps.h104
8 files changed, 3978 insertions, 0 deletions
diff --git a/bin/ps/extern.h b/bin/ps/extern.h
new file mode 100644
index 0000000..6ec96ca
--- /dev/null
+++ b/bin/ps/extern.h
@@ -0,0 +1,99 @@
+/* $NetBSD: extern.h,v 1.39 2017/12/09 14:56:54 kamil Exp $ */
+
+/*-
+ * 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.
+ * 3. 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
+ */
+
+/*
+ * We expect to be included by ps.h, which will already have
+ * defined the types we use.
+ */
+
+extern double log_ccpu;
+extern int eval, fscale, mempages, nlistread, maxslp, uspace;
+extern int sumrusage, termwidth, totwidth;
+extern int needenv, needcomm, commandonly;
+extern uid_t myuid;
+extern kvm_t *kd;
+extern VAR var[];
+extern VARLIST displaylist;
+extern VARLIST sortlist;
+
+void command(struct pinfo *, VARENT *, enum mode);
+void cpuid(struct pinfo *, VARENT *, enum mode);
+void cputime(struct pinfo *, VARENT *, enum mode);
+void donlist(void);
+void donlist_sysctl(void);
+void fmt_puts(char *, int *);
+void fmt_putc(int, int *);
+void elapsed(struct pinfo *, VARENT *, enum mode);
+double getpcpu(const struct kinfo_proc2 *);
+double getpmem(const struct kinfo_proc2 *);
+void gname(struct pinfo *, VARENT *, enum mode);
+void groups(struct pinfo *, VARENT *, enum mode);
+void groupnames(struct pinfo *, VARENT *, enum mode);
+void lcputime(struct pinfo *, VARENT *, enum mode);
+void logname(struct pinfo *, VARENT *, enum mode);
+void longtname(struct pinfo *, VARENT *, enum mode);
+void lname(struct pinfo *, VARENT *, enum mode);
+void lstarted(struct pinfo *, VARENT *, enum mode);
+void lstate(struct pinfo *, VARENT *, enum mode);
+void maxrss(struct pinfo *, VARENT *, enum mode);
+void nlisterr(struct nlist *);
+void p_rssize(struct pinfo *, VARENT *, enum mode);
+void pagein(struct pinfo *, VARENT *, enum mode);
+void parsefmt(const char *);
+void parsefmt_insert(const char *, VARENT **);
+void parsesort(const char *);
+VARENT * varlist_find(VARLIST *, const char *);
+void emul(struct pinfo *, VARENT *, enum mode);
+void pcpu(struct pinfo *, VARENT *, enum mode);
+void pmem(struct pinfo *, VARENT *, enum mode);
+void pnice(struct pinfo *, VARENT *, enum mode);
+void pri(struct pinfo *, VARENT *, enum mode);
+void printheader(void);
+void putimeval(struct pinfo *, VARENT *, enum mode);
+void pvar(struct pinfo *, VARENT *, enum mode);
+void rgname(struct pinfo *, VARENT *, enum mode);
+void rssize(struct pinfo *, VARENT *, enum mode);
+void runame(struct pinfo *, VARENT *, enum mode);
+void showkey(void);
+void started(struct pinfo *, VARENT *, enum mode);
+void state(struct pinfo *, VARENT *, enum mode);
+void svgname(struct pinfo *, VARENT *, enum mode);
+void svuname(struct pinfo *, VARENT *, enum mode);
+void tdev(struct pinfo *, VARENT *, enum mode);
+void tname(struct pinfo *, VARENT *, enum mode);
+void tsize(struct pinfo *, VARENT *, enum mode);
+void ucomm(struct pinfo *, VARENT *, enum mode);
+void usrname(struct pinfo *, VARENT *, enum mode);
+void uvar(struct pinfo *, VARENT *, enum mode);
+void vsize(struct pinfo *, VARENT *, enum mode);
+void wchan(struct pinfo *, VARENT *, enum mode);
diff --git a/bin/ps/fmt.c b/bin/ps/fmt.c
new file mode 100644
index 0000000..489a0ec
--- /dev/null
+++ b/bin/ps/fmt.c
@@ -0,0 +1,60 @@
+/* $NetBSD: fmt.c,v 1.21 2007/12/12 22:55:43 lukem Exp $ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: fmt.c,v 1.21 2007/12/12 22:55:43 lukem Exp $");
+
+#include <kvm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <vis.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include "ps.h"
+
+void
+fmt_puts(char *s, int *leftp)
+{
+ static char *v = 0;
+ static int maxlen = 0;
+ char *nv;
+ int len, nlen;
+
+ if (*leftp == 0)
+ return;
+ len = strlen(s) * 4 + 1;
+ if (len > maxlen) {
+ if (maxlen == 0)
+ nlen = getpagesize();
+ else
+ nlen = maxlen;
+ while (len > nlen)
+ nlen *= 2;
+ nv = realloc(v, nlen);
+ if (nv == 0)
+ return;
+ v = nv;
+ maxlen = nlen;
+ }
+ len = strvis(v, s, VIS_TAB | VIS_NL | VIS_CSTYLE);
+ if (*leftp != -1) {
+ if (len > *leftp) {
+ v[*leftp] = '\0';
+ *leftp = 0;
+ } else
+ *leftp -= len;
+ }
+ (void)printf("%s", v);
+}
+
+void
+fmt_putc(int c, int *leftp)
+{
+
+ if (*leftp == 0)
+ return;
+ if (*leftp != -1)
+ *leftp -= 1;
+ putchar(c);
+}
diff --git a/bin/ps/keyword.c b/bin/ps/keyword.c
new file mode 100644
index 0000000..f7504ca
--- /dev/null
+++ b/bin/ps/keyword.c
@@ -0,0 +1,415 @@
+/* $NetBSD: keyword.c,v 1.56 2018/04/11 18:52:05 christos Exp $ */
+
+/*-
+ * 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.
+ * 3. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)keyword.c 8.5 (Berkeley) 4/2/94";
+#else
+__RCSID("$NetBSD: keyword.c,v 1.56 2018/04/11 18:52:05 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/lwp.h>
+#include <sys/proc.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#include <sys/ucred.h>
+
+#include <err.h>
+#include <errno.h>
+#include <kvm.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include "ps.h"
+
+static VAR *findvar(const char *);
+static int vcmp(const void *, const void *);
+
+#if 0 /* kernel doesn't calculate these */
+ PUVAR("idrss", "IDRSS", 0, p_uru_idrss, UINT64, PRIu64),
+ PUVAR("isrss", "ISRSS", 0, p_uru_isrss, UINT64, PRId64),
+ PUVAR("ixrss", "IXRSS", 0, p_uru_ixrss, UINT64, PRId64),
+ PUVAR("maxrss", "MAXRSS", 0, p_uru_maxrss, UINT64, PRIu64),
+#endif
+
+/* Compute offset in common structures. */
+#define POFF(x) offsetof(struct kinfo_proc2, x)
+#define LOFF(x) offsetof(struct kinfo_lwp, x)
+
+#define UIDFMT "u"
+#define UID(n1, n2, of) \
+ { .name = n1, .header = n2, .flag = 0, .oproc = pvar, \
+ .off = POFF(of), .type = UINT32, .fmt = UIDFMT }
+#define GID(n1, n2, off) UID(n1, n2, off)
+
+#define PIDFMT "d"
+#define PID(n1, n2, of) \
+ { .name = n1, .header = n2, .flag = 0, .oproc = pvar, \
+ .off = POFF(of), .type = INT32, .fmt = PIDFMT }
+
+#define LVAR(n1, n2, fl, of, ty, fm) \
+ { .name = n1, .header = n2, .flag = (fl) | LWP, .oproc = pvar, \
+ .off = LOFF(of), .type = ty, .fmt = fm }
+#define PVAR(n1, n2, fl, of, ty, fm) \
+ { .name = n1, .header = n2, .flag = (fl) | 0, .oproc = pvar, \
+ .off = POFF(of), .type = ty, .fmt = fm }
+#define PUVAR(n1, n2, fl, of, ty, fm) \
+ { .name = n1, .header = n2, .flag = (fl) | UAREA, .oproc = pvar, \
+ .off = POFF(of), .type = ty, .fmt = fm }
+#define VAR3(n1, n2, fl) \
+ { .name = n1, .header = n2, .flag = fl }
+#define VAR4(n1, n2, fl, op) \
+ { .name = n1, .header = n2, .flag = fl, .oproc = op, }
+#define VAR6(n1, n2, fl, op, of, ty) \
+ { .name = n1, .header = n2, .flag = fl, .oproc = op, \
+ .off = of, .type = ty }
+
+/* NB: table must be sorted, in vi use:
+ * :/^VAR/,/end_sort/! sort -t\" +1
+ * breaking long lines just makes the sort harder
+ *
+ * We support all the fields required by P1003.1-2004 (SUSv3), with
+ * the correct default headers, except for the "tty" field, where the
+ * standard says the header should be "TT", but we have "TTY".
+ */
+VAR var[] = {
+ VAR6("%cpu", "%CPU", 0, pcpu, 0, PCPU),
+ VAR6("%mem", "%MEM", 0, pmem, POFF(p_vm_rssize), INT32),
+ PVAR("acflag", "ACFLG", 0, p_acflag, USHORT, "x"),
+ VAR3("acflg", "acflag", ALIAS),
+ VAR3("args", "command", ALIAS),
+ VAR3("blocked", "sigmask", ALIAS),
+ VAR3("caught", "sigcatch", ALIAS),
+ VAR4("comm", "COMMAND", COMM|ARGV0|LJUST, command),
+ VAR4("command", "COMMAND", COMM|LJUST, command),
+ PVAR("cpu", "CPU", 0, p_estcpu, UINT, "u"),
+ VAR4("cpuid", "CPUID", LWP, cpuid),
+ VAR3("cputime", "time", ALIAS),
+ VAR6("ctime", "CTIME", 0, putimeval, POFF(p_uctime_sec), TIMEVAL),
+ GID("egid", "EGID", p_gid),
+ VAR4("egroup", "EGROUP", LJUST, gname),
+ VAR4("emul", "EMUL", LJUST, emul),
+ VAR6("etime", "ELAPSED", 0, elapsed, POFF(p_ustart_sec), TIMEVAL),
+ UID("euid", "EUID", p_uid),
+ VAR4("euser", "EUSER", LJUST, usrname),
+ PVAR("f", "F", 0, p_flag, INT, "x"),
+ VAR3("flags", "f", ALIAS),
+ GID("gid", "GID", p_gid),
+ VAR4("group", "GROUP", LJUST, gname),
+ VAR4("groupnames", "GROUPNAMES", LJUST, groupnames),
+ VAR4("groups", "GROUPS", LJUST, groups),
+ /* holdcnt: unused, left for compat. */
+ LVAR("holdcnt", "HOLDCNT", 0, l_holdcnt, INT, "d"),
+ VAR3("ignored", "sigignore", ALIAS),
+ PUVAR("inblk", "INBLK", 0, p_uru_inblock, UINT64, PRIu64),
+ VAR3("inblock", "inblk", ALIAS),
+ PVAR("jobc", "JOBC", 0, p_jobc, SHORT, "d"),
+ PVAR("ktrace", "KTRACE", 0, p_traceflag, INT, "x"),
+/*XXX*/ PVAR("ktracep", "KTRACEP", 0, p_tracep, KPTR, PRIx64),
+ LVAR("laddr", "LADDR", 0, l_laddr, KPTR, PRIx64),
+ LVAR("lid", "LID", 0, l_lid, INT32, "d"),
+ VAR4("lim", "LIM", 0, maxrss),
+ VAR4("lname", "LNAME", LJUST|LWP, lname),
+ VAR4("login", "LOGIN", LJUST, logname),
+ VAR3("logname", "login", ALIAS),
+ VAR6("lstart", "STARTED", LJUST, lstarted, POFF(p_ustart_sec), UINT32),
+ VAR4("lstate", "STAT", LJUST|LWP, lstate),
+ VAR6("ltime", "LTIME", LWP, lcputime, 0, CPUTIME),
+ PUVAR("majflt", "MAJFLT", 0, p_uru_majflt, UINT64, PRIu64),
+ PUVAR("minflt", "MINFLT", 0, p_uru_minflt, UINT64, PRIu64),
+ PUVAR("msgrcv", "MSGRCV", 0, p_uru_msgrcv, UINT64, PRIu64),
+ PUVAR("msgsnd", "MSGSND", 0, p_uru_msgsnd, UINT64, PRIu64),
+ VAR3("ni", "nice", ALIAS),
+ VAR6("nice", "NI", 0, pnice, POFF(p_nice), UCHAR),
+ PUVAR("nivcsw", "NIVCSW", 0, p_uru_nivcsw, UINT64, PRIu64),
+ PVAR("nlwp", "NLWP", 0, p_nlwps, UINT64, PRId64),
+ VAR3("nsignals", "nsigs", ALIAS),
+ PUVAR("nsigs", "NSIGS", 0, p_uru_nsignals, UINT64, PRIu64),
+ /* nswap: unused, left for compat. */
+ PUVAR("nswap", "NSWAP", 0, p_uru_nswap, UINT64, PRIu64),
+ PUVAR("nvcsw", "NVCSW", 0, p_uru_nvcsw, UINT64, PRIu64),
+/*XXX*/ LVAR("nwchan", "WCHAN", 0, l_wchan, KPTR, PRIx64),
+ PUVAR("oublk", "OUBLK", 0, p_uru_oublock, UINT64, PRIu64),
+ VAR3("oublock", "oublk", ALIAS),
+/*XXX*/ PVAR("p_ru", "P_RU", 0, p_ru, KPTR, PRIx64),
+/*XXX*/ PVAR("paddr", "PADDR", 0, p_paddr, KPTR, PRIx64),
+ PUVAR("pagein", "PAGEIN", 0, p_uru_majflt, UINT64, PRIu64),
+ VAR3("pcpu", "%cpu", ALIAS),
+ VAR3("pending", "sig", ALIAS),
+ PID("pgid", "PGID", p__pgid),
+ PID("pid", "PID", p_pid),
+ VAR3("pmem", "%mem", ALIAS),
+ PID("ppid", "PPID", p_ppid),
+ VAR4("pri", "PRI", LWP, pri),
+ LVAR("re", "RE", INF127, l_swtime, UINT, "u"),
+ GID("rgid", "RGID", p_rgid),
+ VAR4("rgroup", "RGROUP", LJUST, rgname),
+/*XXX*/ LVAR("rlink", "RLINK", 0, l_back, KPTR, PRIx64),
+ PVAR("rlwp", "RLWP", 0, p_nrlwps, UINT64, PRId64),
+ VAR6("rss", "RSS", 0, p_rssize, POFF(p_vm_rssize), INT32),
+ VAR3("rssize", "rsz", ALIAS),
+ VAR6("rsz", "RSZ", 0, rssize, POFF(p_vm_rssize), INT32),
+ UID("ruid", "RUID", p_ruid),
+ VAR4("ruser", "RUSER", LJUST, runame),
+ PVAR("sess", "SESS", 0, p_sess, KPTR24, PRIx64),
+ PID("sid", "SID", p_sid),
+ PVAR("sig", "PENDING", 0, p_siglist, SIGLIST, "s"),
+ PVAR("sigcatch", "CAUGHT", 0, p_sigcatch, SIGLIST, "s"),
+ PVAR("sigignore", "IGNORED", 0, p_sigignore, SIGLIST, "s"),
+ PVAR("sigmask", "BLOCKED", 0, p_sigmask, SIGLIST, "s"),
+ LVAR("sl", "SL", INF127, l_slptime, UINT, "u"),
+ VAR6("start", "STARTED", 0, started, POFF(p_ustart_sec), UINT32),
+ VAR3("stat", "state", ALIAS),
+ VAR4("state", "STAT", LJUST, state),
+ VAR6("stime", "STIME", 0, putimeval, POFF(p_ustime_sec), TIMEVAL),
+ GID("svgid", "SVGID", p_svgid),
+ VAR4("svgroup", "SVGROUP", LJUST, svgname),
+ UID("svuid", "SVUID", p_svuid),
+ VAR4("svuser", "SVUSER", LJUST, svuname),
+ /* "tdev" is UINT32, but we do this for sorting purposes */
+ VAR6("tdev", "TDEV", 0, tdev, POFF(p_tdev), INT32),
+ VAR6("time", "TIME", 0, cputime, 0, CPUTIME),
+ PID("tpgid", "TPGID", p_tpgid),
+ PVAR("tsess", "TSESS", 0, p_tsess, KPTR, PRIx64),
+ VAR6("tsiz", "TSIZ", 0, tsize, POFF(p_vm_tsize), INT32),
+ VAR6("tt", "TTY", LJUST, tname, POFF(p_tdev), INT32),
+ VAR6("tty", "TTY", LJUST, longtname, POFF(p_tdev), INT32),
+ LVAR("uaddr", "UADDR", 0, l_addr, KPTR, PRIx64),
+ VAR4("ucomm", "UCOMM", LJUST, ucomm),
+ UID("uid", "UID", p_uid),
+ LVAR("upr", "UPR", 0, l_usrpri, UCHAR, "u"),
+ VAR4("user", "USER", LJUST, usrname),
+ VAR3("usrpri", "upr", ALIAS),
+ VAR6("utime", "UTIME", 0, putimeval, POFF(p_uutime_sec), TIMEVAL),
+ VAR3("vsize", "vsz", ALIAS),
+ VAR6("vsz", "VSZ", 0, vsize, 0, VSIZE),
+ VAR4("wchan", "WCHAN", LJUST|LWP, wchan),
+ PVAR("xstat", "XSTAT", 0, p_xstat, USHORT, "x"),
+/* "zzzz" end_sort */
+ { .name = "" },
+};
+
+void
+showkey(void)
+{
+ VAR *v;
+ int i;
+ const char *p;
+ const char *sep;
+
+ i = 0;
+ sep = "";
+ for (v = var; *(p = v->name); ++v) {
+ int len = strlen(p);
+ if (termwidth && (i += len + 1) > termwidth) {
+ i = len;
+ sep = "\n";
+ }
+ (void)printf("%s%s", sep, p);
+ sep = " ";
+ }
+ (void)printf("\n");
+}
+
+/*
+ * Parse the string pp, and insert or append entries to the list
+ * referenced by listptr. If pos in non-null and *pos is non-null, then
+ * *pos specifies where to insert (instead of appending). If pos is
+ * non-null, then a new value is returned through *pos referring to the
+ * last item inserted.
+ */
+static void
+parsevarlist(const char *pp, struct varlist *listptr, struct varent **pos)
+{
+ char *p, *sp, *equalsp;
+
+ /* dup to avoid zapping arguments. We will free sp later. */
+ p = sp = strdup(pp);
+
+ /*
+ * Everything after the first '=' is part of a custom header.
+ * Temporarily replace it with '\0' to simplify other code.
+ */
+ equalsp = strchr(p, '=');
+ if (equalsp)
+ *equalsp = '\0';
+
+#define FMTSEP " \t,\n"
+ while (p && *p) {
+ char *cp;
+ VAR *v;
+ struct varent *vent;
+
+ /*
+ * skip separators before the first keyword, and
+ * look for the separator after the keyword.
+ */
+ for (cp = p; *cp != '\0'; cp++) {
+ p = strpbrk(cp, FMTSEP);
+ if (p != cp)
+ break;
+ }
+ if (*cp == '\0')
+ break;
+ /*
+ * Now cp points to the start of a keyword,
+ * and p is NULL or points past the end of the keyword.
+ *
+ * Terminate the keyword with '\0', or reinstate the
+ * '=' that was removed earlier, if appropriate.
+ */
+ if (p) {
+ *p = '\0';
+ p++;
+ } else if (equalsp) {
+ *equalsp = '=';
+ }
+
+ /*
+ * If findvar() likes the keyword or keyword=header,
+ * add it to our list. If findvar() doesn't like it,
+ * it will print a warning, so we ignore it.
+ */
+ if ((v = findvar(cp)) == NULL)
+ continue;
+ if ((vent = malloc(sizeof(struct varent))) == NULL)
+ err(EXIT_FAILURE, NULL);
+ vent->var = v;
+ if (pos && *pos)
+ SIMPLEQ_INSERT_AFTER(listptr, *pos, vent, next);
+ else {
+ SIMPLEQ_INSERT_TAIL(listptr, vent, next);
+ }
+ if (pos)
+ *pos = vent;
+ }
+ free(sp);
+ if (SIMPLEQ_EMPTY(listptr))
+ errx(EXIT_FAILURE, "no valid keywords");
+}
+
+void
+parsefmt(const char *p)
+{
+
+ parsevarlist(p, &displaylist, NULL);
+}
+
+void
+parsefmt_insert(const char *p, struct varent **pos)
+{
+
+ parsevarlist(p, &displaylist, pos);
+}
+
+void
+parsesort(const char *p)
+{
+
+ parsevarlist(p, &sortlist, NULL);
+}
+
+/* Search through a list for an entry with a specified name. */
+struct varent *
+varlist_find(struct varlist *list, const char *name)
+{
+ struct varent *vent;
+
+ SIMPLEQ_FOREACH(vent, list, next) {
+ if (strcmp(vent->var->name, name) == 0)
+ break;
+ }
+ return vent;
+}
+
+static VAR *
+findvar(const char *p)
+{
+ VAR *v;
+ char *hp;
+
+ hp = strchr(p, '=');
+ if (hp)
+ *hp++ = '\0';
+
+ v = bsearch(p, var, sizeof(var)/sizeof(VAR) - 1, sizeof(VAR), vcmp);
+ if (v && v->flag & ALIAS)
+ v = findvar(v->header);
+ if (!v) {
+ warnx("%s: keyword not found", p);
+ eval = 1;
+ return NULL;
+ }
+
+ if (v && hp) {
+ /*
+ * Override the header.
+ *
+ * We need to copy the entry first, and override the
+ * header in the copy, because the same field might be
+ * used multiple times with different headers. We also
+ * need to strdup the header.
+ */
+ struct var *newvar;
+ char *newheader;
+
+ if ((newvar = malloc(sizeof(struct var))) == NULL)
+ err(EXIT_FAILURE, NULL);
+ if ((newheader = strdup(hp)) == NULL)
+ err(EXIT_FAILURE, NULL);
+ memcpy(newvar, v, sizeof(struct var));
+ newvar->header = newheader;
+
+ /*
+ * According to P1003.1-2004, if the header text is null,
+ * such as -o user=, the field width will be at least as
+ * wide as the default header text.
+ */
+ if (*hp == '\0')
+ newvar->width = strlen(v->header);
+
+ v = newvar;
+ }
+ return v;
+}
+
+static int
+vcmp(const void *a, const void *b)
+{
+ return strcmp(a, ((const VAR *)b)->name);
+}
diff --git a/bin/ps/nlist.c b/bin/ps/nlist.c
new file mode 100644
index 0000000..42471ea
--- /dev/null
+++ b/bin/ps/nlist.c
@@ -0,0 +1,234 @@
+/* $NetBSD: nlist.c,v 1.27 2016/11/28 08:19:23 rin Exp $ */
+
+/*
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Simon Burge.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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) 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.
+ * 3. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)nlist.c 8.4 (Berkeley) 4/2/94";
+#else
+__RCSID("$NetBSD: nlist.c,v 1.27 2016/11/28 08:19:23 rin Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/lwp.h>
+#include <sys/proc.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <kvm.h>
+#include <math.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ps.h"
+
+struct nlist psnl[] = {
+ { .n_name = "_fscale" },
+#define X_FSCALE 0
+ { .n_name = "_ccpu" },
+#define X_CCPU 1
+ { .n_name = "_physmem" },
+#define X_PHYSMEM 2
+ { .n_name = "_maxslp" },
+#define X_MAXSLP 3
+ { .n_name = NULL }
+};
+
+double log_ccpu; /* log of kernel _ccpu variable */
+int nlistread; /* if nlist already read. */
+int mempages; /* number of pages of phys. memory */
+int fscale; /* kernel _fscale variable */
+int maxslp; /* kernel _maxslp variable */
+int uspace; /* kernel USPACE value */
+
+/* XXX Hopefully reasonable default */
+#define MEMPAGES 0
+#ifndef FSCALE
+#define FSCALE (1 << 8)
+#endif
+#define LOG_CCPU (-1.0 / 20.0)
+#ifndef MAXSLP
+#define MAXSLP 20
+#endif
+#ifndef USPACE
+#define USPACE (getpagesize())
+#endif
+
+#define kread(x, v) \
+ kvm_read(kd, psnl[x].n_value, (char *)&v, sizeof v) != sizeof(v)
+
+void
+donlist(void)
+{
+ fixpt_t xccpu;
+
+ nlistread = 1;
+
+ if (kvm_nlist(kd, psnl)) {
+ nlisterr(psnl);
+ eval = 1;
+ fscale = FSCALE;
+ mempages = MEMPAGES;
+ log_ccpu = LOG_CCPU;
+ maxslp = MAXSLP;
+ return;
+ }
+
+ if (kread(X_FSCALE, fscale)) {
+ warnx("fscale: %s", kvm_geterr(kd));
+ eval = 1;
+ fscale = FSCALE;
+ }
+
+ if (kread(X_PHYSMEM, mempages)) {
+ warnx("avail_start: %s", kvm_geterr(kd));
+ eval = 1;
+ mempages = MEMPAGES;
+ }
+
+ if (kread(X_CCPU, xccpu)) {
+ warnx("ccpu: %s", kvm_geterr(kd));
+ eval = 1;
+ log_ccpu = LOG_CCPU;
+ } else
+ log_ccpu = log((double)xccpu / fscale);
+
+ if (kread(X_MAXSLP, maxslp)) {
+ warnx("maxslp: %s", kvm_geterr(kd));
+ eval = 1;
+ maxslp = MAXSLP;
+ }
+}
+
+void
+donlist_sysctl(void)
+{
+ int mib[2];
+ size_t size;
+ fixpt_t xccpu;
+ uint64_t memsize;
+
+ nlistread = 1;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_FSCALE;
+ size = sizeof(fscale);
+ if (sysctl(mib, 2, &fscale, &size, NULL, 0)) {
+ warn("fscale");
+ eval = 1;
+ fscale = FSCALE;
+ }
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_PHYSMEM64;
+ size = sizeof(memsize);
+ if (sysctl(mib, 2, &memsize, &size, NULL, 0)) {
+ warn("avail_start");
+ eval = 1;
+ mempages = MEMPAGES;
+ } else
+ mempages = memsize / getpagesize();
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_CCPU;
+ size = sizeof(xccpu);
+ if (sysctl(mib, 2, &xccpu, &size, NULL, 0)) {
+ warn("ccpu");
+ eval = 1;
+ log_ccpu = LOG_CCPU;
+ } else
+ log_ccpu = log((double)xccpu / fscale);
+
+ mib[0] = CTL_VM;
+ mib[1] = VM_MAXSLP;
+ size = sizeof(maxslp);
+ if (sysctl(mib, 2, &maxslp, &size, NULL, 0)) {
+ warn("maxslp");
+ eval = 1;
+ maxslp = MAXSLP;
+ }
+
+ mib[0] = CTL_VM;
+ mib[1] = VM_USPACE;
+ size = sizeof(uspace);
+ if (sysctl(mib, 2, &uspace, &size, NULL, 0)) {
+ warn("uspace");
+ eval = 1;
+ uspace = USPACE;
+ }
+}
+
+void
+nlisterr(struct nlist nl[])
+{
+ int i;
+
+ (void)fprintf(stderr, "ps: nlist: can't find following symbols:");
+ for (i = 0; nl[i].n_name != NULL; i++)
+ if (nl[i].n_value == 0)
+ (void)fprintf(stderr, " %s", nl[i].n_name);
+ (void)fprintf(stderr, "\n");
+}
diff --git a/bin/ps/print.c b/bin/ps/print.c
new file mode 100644
index 0000000..e725ef0
--- /dev/null
+++ b/bin/ps/print.c
@@ -0,0 +1,1413 @@
+/* $NetBSD: print.c,v 1.130 2018/09/19 15:20:39 maxv Exp $ */
+
+/*
+ * Copyright (c) 2000, 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Simon Burge.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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) 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.
+ * 3. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)print.c 8.6 (Berkeley) 4/16/94";
+#else
+__RCSID("$NetBSD: print.c,v 1.130 2018/09/19 15:20:39 maxv Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/lwp.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/ucred.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <grp.h>
+#include <kvm.h>
+#include <math.h>
+#include <nlist.h>
+#include <pwd.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <tzfile.h>
+#include <unistd.h>
+
+#include "ps.h"
+
+static char *cmdpart(char *);
+static void printval(void *, VAR *, enum mode);
+static int titlecmp(char *, char **);
+
+static void doubleprintorsetwidth(VAR *, double, int, enum mode);
+static void intprintorsetwidth(VAR *, int, enum mode);
+static void strprintorsetwidth(VAR *, const char *, enum mode);
+
+static time_t now;
+
+#define min(a,b) ((a) <= (b) ? (a) : (b))
+
+static int
+iwidth(u_int64_t v)
+{
+ u_int64_t nlim, lim;
+ int w = 1;
+
+ for (lim = 10; v >= lim; lim = nlim) {
+ nlim = lim * 10;
+ w++;
+ if (nlim < lim)
+ break;
+ }
+ return w;
+}
+
+static char *
+cmdpart(char *arg0)
+{
+ char *cp;
+
+ return ((cp = strrchr(arg0, '/')) != NULL ? cp + 1 : arg0);
+}
+
+void
+printheader(void)
+{
+ int len;
+ VAR *v;
+ struct varent *vent;
+ static int firsttime = 1;
+ static int noheader = 0;
+
+ /*
+ * If all the columns have user-specified null headers,
+ * don't print the blank header line at all.
+ */
+ if (firsttime) {
+ SIMPLEQ_FOREACH(vent, &displaylist, next) {
+ if (vent->var->header[0])
+ break;
+ }
+ if (vent == NULL) {
+ noheader = 1;
+ firsttime = 0;
+ }
+
+ }
+ if (noheader)
+ return;
+
+ SIMPLEQ_FOREACH(vent, &displaylist, next) {
+ v = vent->var;
+ if (firsttime) {
+ len = strlen(v->header);
+ if (len > v->width)
+ v->width = len;
+ totwidth += v->width + 1; /* +1 for space */
+ }
+ if (v->flag & LJUST) {
+ if (SIMPLEQ_NEXT(vent, next) == NULL) /* last one */
+ (void)printf("%s", v->header);
+ else
+ (void)printf("%-*s", v->width,
+ v->header);
+ } else
+ (void)printf("%*s", v->width, v->header);
+ if (SIMPLEQ_NEXT(vent, next) != NULL)
+ (void)putchar(' ');
+ }
+ (void)putchar('\n');
+ if (firsttime) {
+ firsttime = 0;
+ totwidth--; /* take off last space */
+ }
+}
+
+/*
+ * Return 1 if the command name in the argument vector (u-area) does
+ * not match the command name (p_comm)
+ */
+static int
+titlecmp(char *name, char **argv)
+{
+ char *title;
+ int namelen;
+
+
+ /* no argument vector == no match; system processes/threads do that */
+ if (argv == 0 || argv[0] == 0)
+ return (1);
+
+ title = cmdpart(argv[0]);
+
+ /* the basename matches */
+ if (!strcmp(name, title))
+ return (0);
+
+ /* handle login shells, by skipping the leading - */
+ if (title[0] == '-' && !strcmp(name, title + 1))
+ return (0);
+
+ namelen = strlen(name);
+
+ /* handle daemons that report activity as daemonname: activity */
+ if (argv[1] == 0 &&
+ !strncmp(name, title, namelen) &&
+ title[namelen + 0] == ':' &&
+ title[namelen + 1] == ' ')
+ return (0);
+
+ return (1);
+}
+
+static void
+doubleprintorsetwidth(VAR *v, double val, int prec, enum mode mode)
+{
+ int fmtlen;
+
+ if (mode == WIDTHMODE) {
+ if (val < 0.0 && val < v->longestnd) {
+ fmtlen = (int)log10(-val) + prec + 2;
+ v->longestnd = val;
+ if (fmtlen > v->width)
+ v->width = fmtlen;
+ } else if (val > 0.0 && val > v->longestpd) {
+ fmtlen = (int)log10(val) + prec + 1;
+ v->longestpd = val;
+ if (fmtlen > v->width)
+ v->width = fmtlen;
+ }
+ } else {
+ (void)printf("%*.*f", v->width, prec, val);
+ }
+}
+
+static void
+intprintorsetwidth(VAR *v, int val, enum mode mode)
+{
+ int fmtlen;
+
+ if (mode == WIDTHMODE) {
+ if (val < 0 && val < v->longestn) {
+ v->longestn = val;
+ fmtlen = iwidth(-val) + 1;
+ if (fmtlen > v->width)
+ v->width = fmtlen;
+ } else if (val > 0 && val > v->longestp) {
+ v->longestp = val;
+ fmtlen = iwidth(val);
+ if (fmtlen > v->width)
+ v->width = fmtlen;
+ }
+ } else
+ (void)printf("%*d", v->width, val);
+}
+
+static void
+strprintorsetwidth(VAR *v, const char *str, enum mode mode)
+{
+ int len;
+
+ if (mode == WIDTHMODE) {
+ len = strlen(str);
+ if (len > v->width)
+ v->width = len;
+ } else {
+ if (v->flag & LJUST)
+ (void)printf("%-*.*s", v->width, v->width, str);
+ else
+ (void)printf("%*.*s", v->width, v->width, str);
+ }
+}
+
+void
+command(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *ki = pi->ki;
+ VAR *v;
+ int left;
+ char **argv, **p, *name;
+
+ if (mode == WIDTHMODE)
+ return;
+
+ v = ve->var;
+ if (SIMPLEQ_NEXT(ve, next) != NULL || termwidth != UNLIMITED) {
+ if (SIMPLEQ_NEXT(ve, next) == NULL) {
+ left = termwidth - (totwidth - v->width);
+ if (left < 1) /* already wrapped, just use std width */
+ left = v->width;
+ } else
+ left = v->width;
+ } else
+ left = -1;
+ if (needenv && kd) {
+ argv = kvm_getenvv2(kd, ki, termwidth);
+ if ((p = argv) != NULL) {
+ while (*p) {
+ fmt_puts(*p, &left);
+ p++;
+ fmt_putc(' ', &left);
+ }
+ }
+ }
+ if (needcomm) {
+ if (pi->prefix)
+ (void)fmt_puts(pi->prefix, &left);
+ name = ki->p_comm;
+ if (!commandonly) {
+ argv = kvm_getargv2(kd, ki, termwidth);
+ if ((p = argv) != NULL) {
+ while (*p) {
+ fmt_puts(*p, &left);
+ p++;
+ fmt_putc(' ', &left);
+ if (v->flag & ARGV0)
+ break;
+ }
+ if (!(v->flag & ARGV0) &&
+ titlecmp(name, argv)) {
+ /*
+ * append the real command name within
+ * parentheses, if the command name
+ * does not match the one in the
+ * argument vector
+ */
+ fmt_putc('(', &left);
+ fmt_puts(name, &left);
+ fmt_putc(')', &left);
+ }
+ } else {
+ /*
+ * Commands that don't set an argv vector
+ * are printed with square brackets if they
+ * are system commands. Otherwise they are
+ * printed within parentheses.
+ */
+ if (ki->p_flag & P_SYSTEM) {
+ fmt_putc('[', &left);
+ fmt_puts(name, &left);
+ fmt_putc(']', &left);
+ } else {
+ fmt_putc('(', &left);
+ fmt_puts(name, &left);
+ fmt_putc(')', &left);
+ }
+ }
+ } else {
+ fmt_puts(name, &left);
+ }
+ }
+ if (SIMPLEQ_NEXT(ve, next) != NULL && left > 0)
+ (void)printf("%*s", left, "");
+}
+
+void
+groups(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *ki = pi->ki;
+ VAR *v;
+ int left, i;
+ char buf[16], *p;
+
+ if (mode == WIDTHMODE)
+ return;
+
+ v = ve->var;
+ if (SIMPLEQ_NEXT(ve, next) != NULL || termwidth != UNLIMITED) {
+ if (SIMPLEQ_NEXT(ve, next) == NULL) {
+ left = termwidth - (totwidth - v->width);
+ if (left < 1) /* already wrapped, just use std width */
+ left = v->width;
+ } else
+ left = v->width;
+ } else
+ left = -1;
+
+ if (ki->p_ngroups == 0)
+ fmt_putc('-', &left);
+
+ for (i = 0; i < ki->p_ngroups; i++) {
+ (void)snprintf(buf, sizeof(buf), "%d", ki->p_groups[i]);
+ if (i)
+ fmt_putc(' ', &left);
+ for (p = &buf[0]; *p; p++)
+ fmt_putc(*p, &left);
+ }
+
+ if (SIMPLEQ_NEXT(ve, next) != NULL && left > 0)
+ (void)printf("%*s", left, "");
+}
+
+void
+groupnames(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *ki = pi->ki;
+ VAR *v;
+ int left, i;
+ const char *p;
+
+ if (mode == WIDTHMODE)
+ return;
+
+ v = ve->var;
+ if (SIMPLEQ_NEXT(ve, next) != NULL || termwidth != UNLIMITED) {
+ if (SIMPLEQ_NEXT(ve, next) == NULL) {
+ left = termwidth - (totwidth - v->width);
+ if (left < 1) /* already wrapped, just use std width */
+ left = v->width;
+ } else
+ left = v->width;
+ } else
+ left = -1;
+
+ if (ki->p_ngroups == 0)
+ fmt_putc('-', &left);
+
+ for (i = 0; i < ki->p_ngroups; i++) {
+ if (i)
+ fmt_putc(' ', &left);
+ for (p = group_from_gid(ki->p_groups[i], 0); *p; p++)
+ fmt_putc(*p, &left);
+ }
+
+ if (SIMPLEQ_NEXT(ve, next) != NULL && left > 0)
+ (void)printf("%*s", left, "");
+}
+
+void
+ucomm(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ char buf[MAXPATHLEN], *p;
+ VAR *v;
+
+ v = ve->var;
+ if (pi->prefix)
+ snprintf(p = buf, sizeof(buf), "%s%s", pi->prefix, k->p_comm);
+ else
+ p = k->p_comm;
+ strprintorsetwidth(v, p, mode);
+}
+
+void
+emul(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ strprintorsetwidth(v, k->p_ename, mode);
+}
+
+void
+logname(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ strprintorsetwidth(v, k->p_login, mode);
+}
+
+void
+state(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ int flag, is_zombie;
+ char *cp;
+ VAR *v;
+ char buf[16];
+
+ is_zombie = 0;
+ v = ve->var;
+ flag = k->p_flag;
+ cp = buf;
+
+ /*
+ * NOTE: There are historical letters, which are no longer used:
+ *
+ * - W: indicated that process is swapped out.
+ * - L: indicated non-zero l_holdcnt (i.e. that process was
+ * prevented from swapping-out.
+ *
+ * These letters should not be used for new states to avoid
+ * conflicts with old applications which might depend on them.
+ */
+ switch (k->p_stat) {
+
+ case LSSTOP:
+ *cp = 'T';
+ break;
+
+ case LSSLEEP:
+ if (flag & L_SINTR) /* interruptable (long) */
+ *cp = (int)k->p_slptime >= maxslp ? 'I' : 'S';
+ else
+ *cp = 'D';
+ break;
+
+ case LSRUN:
+ case LSIDL:
+ *cp = 'R';
+ break;
+
+ case LSONPROC:
+ *cp = 'O';
+ break;
+
+ case LSZOMB:
+ *cp = 'Z';
+ is_zombie = 1;
+ break;
+
+ case LSSUSPENDED:
+ *cp = 'U';
+ break;
+
+ default:
+ *cp = '?';
+ }
+ cp++;
+ if (k->p_nice < NZERO)
+ *cp++ = '<';
+ else if (k->p_nice > NZERO)
+ *cp++ = 'N';
+ if (flag & P_TRACED)
+ *cp++ = 'X';
+ if (flag & P_WEXIT && !is_zombie)
+ *cp++ = 'E';
+ if (flag & P_PPWAIT)
+ *cp++ = 'V';
+ if (flag & P_SYSTEM)
+ *cp++ = 'K';
+ if (k->p_eflag & EPROC_SLEADER)
+ *cp++ = 's';
+ if (flag & P_SA)
+ *cp++ = 'a';
+ else if (k->p_nlwps > 1)
+ *cp++ = 'l';
+ if ((flag & P_CONTROLT) && k->p__pgid == k->p_tpgid)
+ *cp++ = '+';
+ *cp = '\0';
+ strprintorsetwidth(v, buf, mode);
+}
+
+void
+lstate(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_lwp *k = pi->li;
+ int flag;
+ char *cp;
+ VAR *v;
+ char buf[16];
+
+ v = ve->var;
+ flag = k->l_flag;
+ cp = buf;
+
+ switch (k->l_stat) {
+
+ case LSSTOP:
+ *cp = 'T';
+ break;
+
+ case LSSLEEP:
+ if (flag & L_SINTR) /* interruptible (long) */
+ *cp = (int)k->l_slptime >= maxslp ? 'I' : 'S';
+ else
+ *cp = 'D';
+ break;
+
+ case LSRUN:
+ case LSIDL:
+ *cp = 'R';
+ break;
+
+ case LSONPROC:
+ *cp = 'O';
+ break;
+
+ case LSZOMB:
+ case LSDEAD:
+ *cp = 'Z';
+ break;
+
+ case LSSUSPENDED:
+ *cp = 'U';
+ break;
+
+ default:
+ *cp = '?';
+ }
+ cp++;
+ if (flag & L_SYSTEM)
+ *cp++ = 'K';
+ if (flag & L_SA)
+ *cp++ = 'a';
+ if (flag & L_DETACHED)
+ *cp++ = '-';
+ *cp = '\0';
+ strprintorsetwidth(v, buf, mode);
+}
+
+void
+pnice(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ intprintorsetwidth(v, k->p_nice - NZERO, mode);
+}
+
+void
+pri(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_lwp *l = pi->li;
+ VAR *v;
+
+ v = ve->var;
+ intprintorsetwidth(v, l->l_priority, mode);
+}
+
+void
+usrname(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ strprintorsetwidth(v, user_from_uid(k->p_uid, 0), mode);
+}
+
+void
+runame(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ strprintorsetwidth(v, user_from_uid(k->p_ruid, 0), mode);
+}
+
+void
+svuname(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ strprintorsetwidth(v, user_from_uid(k->p_svuid, 0), mode);
+}
+
+void
+gname(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ strprintorsetwidth(v, group_from_gid(k->p_gid, 0), mode);
+}
+
+void
+rgname(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ strprintorsetwidth(v, group_from_gid(k->p_rgid, 0), mode);
+}
+
+void
+svgname(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ strprintorsetwidth(v, group_from_gid(k->p_svgid, 0), mode);
+}
+
+void
+tdev(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+ dev_t dev;
+ char buff[16];
+
+ v = ve->var;
+ dev = k->p_tdev;
+ if (dev == NODEV) {
+ if (mode == PRINTMODE)
+ (void)printf("%*s", v->width, "?");
+ else
+ if (v->width < 2)
+ v->width = 2;
+ } else {
+ (void)snprintf(buff, sizeof(buff),
+ "%lld/%lld", (long long)major(dev), (long long)minor(dev));
+ strprintorsetwidth(v, buff, mode);
+ }
+}
+
+void
+tname(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+ dev_t dev;
+ const char *ttname;
+ int noctty;
+
+ v = ve->var;
+ dev = k->p_tdev;
+ if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) {
+ if (mode == PRINTMODE)
+ (void)printf("%-*s", v->width, "?");
+ else
+ if (v->width < 2)
+ v->width = 2;
+ } else {
+ noctty = !(k->p_eflag & EPROC_CTTY) ? 1 : 0;
+ if (mode == WIDTHMODE) {
+ int fmtlen;
+
+ fmtlen = strlen(ttname) + noctty;
+ if (v->width < fmtlen)
+ v->width = fmtlen;
+ } else {
+ if (noctty)
+ (void)printf("%-*s-", v->width - 1, ttname);
+ else
+ (void)printf("%-*s", v->width, ttname);
+ }
+ }
+}
+
+void
+longtname(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+ dev_t dev;
+ const char *ttname;
+
+ v = ve->var;
+ dev = k->p_tdev;
+ if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) {
+ if (mode == PRINTMODE)
+ (void)printf("%-*s", v->width, "?");
+ else
+ if (v->width < 2)
+ v->width = 2;
+ } else {
+ strprintorsetwidth(v, ttname, mode);
+ }
+}
+
+void
+started(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+ time_t startt;
+ struct tm *tp;
+ char buf[100], *cp;
+
+ v = ve->var;
+ if (!k->p_uvalid) {
+ if (mode == PRINTMODE)
+ (void)printf("%*s", v->width, "-");
+ return;
+ }
+
+ startt = k->p_ustart_sec;
+ tp = localtime(&startt);
+ if (now == 0)
+ (void)time(&now);
+ if (now - k->p_ustart_sec < SECSPERDAY)
+ /* I *hate* SCCS... */
+ (void)strftime(buf, sizeof(buf) - 1, "%l:%" "M%p", tp);
+ else if (now - k->p_ustart_sec < DAYSPERWEEK * SECSPERDAY)
+ /* I *hate* SCCS... */
+ (void)strftime(buf, sizeof(buf) - 1, "%a%" "I%p", tp);
+ else
+ (void)strftime(buf, sizeof(buf) - 1, "%e%b%y", tp);
+ /* %e and %l can start with a space. */
+ cp = buf;
+ if (*cp == ' ')
+ cp++;
+ strprintorsetwidth(v, cp, mode);
+}
+
+void
+lstarted(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+ time_t startt;
+ char buf[100];
+
+ v = ve->var;
+ if (!k->p_uvalid) {
+ /*
+ * Minimum width is less than header - we don't
+ * need to check it every time.
+ */
+ if (mode == PRINTMODE)
+ (void)printf("%*s", v->width, "-");
+ return;
+ }
+ startt = k->p_ustart_sec;
+
+ /* assume all times are the same length */
+ if (mode != WIDTHMODE || v->width == 0) {
+ (void)strftime(buf, sizeof(buf) -1, "%c",
+ localtime(&startt));
+ strprintorsetwidth(v, buf, mode);
+ }
+}
+
+void
+elapsed(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+ int32_t origseconds, secs, mins, hours, days;
+ int fmtlen, printed_something;
+
+ v = ve->var;
+ if (k->p_uvalid == 0) {
+ origseconds = 0;
+ } else {
+ if (now == 0)
+ (void)time(&now);
+ origseconds = now - k->p_ustart_sec;
+ if (origseconds < 0) {
+ /*
+ * Don't try to be fancy if the machine's
+ * clock has been rewound to before the
+ * process "started".
+ */
+ origseconds = 0;
+ }
+ }
+
+ secs = origseconds;
+ mins = secs / SECSPERMIN;
+ secs %= SECSPERMIN;
+ hours = mins / MINSPERHOUR;
+ mins %= MINSPERHOUR;
+ days = hours / HOURSPERDAY;
+ hours %= HOURSPERDAY;
+
+ if (mode == WIDTHMODE) {
+ if (origseconds == 0)
+ /* non-zero so fmtlen is calculated at least once */
+ origseconds = 1;
+
+ if (origseconds > v->longestp) {
+ v->longestp = origseconds;
+
+ if (days > 0) {
+ /* +9 for "-hh:mm:ss" */
+ fmtlen = iwidth(days) + 9;
+ } else if (hours > 0) {
+ /* +6 for "mm:ss" */
+ fmtlen = iwidth(hours) + 6;
+ } else {
+ /* +3 for ":ss" */
+ fmtlen = iwidth(mins) + 3;
+ }
+
+ if (fmtlen > v->width)
+ v->width = fmtlen;
+ }
+ } else {
+ printed_something = 0;
+ fmtlen = v->width;
+
+ if (days > 0) {
+ (void)printf("%*d", fmtlen - 9, days);
+ printed_something = 1;
+ } else if (fmtlen > 9) {
+ (void)printf("%*s", fmtlen - 9, "");
+ }
+ if (fmtlen > 9)
+ fmtlen = 9;
+
+ if (printed_something) {
+ (void)printf("-%.*d", fmtlen - 7, hours);
+ printed_something = 1;
+ } else if (hours > 0) {
+ (void)printf("%*d", fmtlen - 6, hours);
+ printed_something = 1;
+ } else if (fmtlen > 6) {
+ (void)printf("%*s", fmtlen - 6, "");
+ }
+ if (fmtlen > 6)
+ fmtlen = 6;
+
+ /* Don't need to set fmtlen or printed_something any more... */
+ if (printed_something) {
+ (void)printf(":%.*d", fmtlen - 4, mins);
+ } else if (mins > 0) {
+ (void)printf("%*d", fmtlen - 3, mins);
+ } else if (fmtlen > 3) {
+ (void)printf("%*s", fmtlen - 3, "0");
+ }
+
+ (void)printf(":%.2d", secs);
+ }
+}
+
+void
+wchan(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_lwp *l = pi->li;
+ VAR *v;
+
+ v = ve->var;
+ if (l->l_wmesg[0]) {
+ strprintorsetwidth(v, l->l_wmesg, mode);
+ v->width = min(v->width, KI_WMESGLEN);
+ } else {
+ if (mode == PRINTMODE)
+ (void)printf("%-*s", v->width, "-");
+ }
+}
+
+#define pgtok(a) (((a)*(size_t)getpagesize())/1024)
+
+void
+vsize(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ intprintorsetwidth(v, pgtok(k->p_vm_msize), mode);
+}
+
+void
+rssize(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ /* XXX don't have info about shared */
+ intprintorsetwidth(v, pgtok(k->p_vm_rssize), mode);
+}
+
+void
+p_rssize(struct pinfo *pi, VARENT *ve, enum mode mode) /* doesn't account for text */
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ intprintorsetwidth(v, pgtok(k->p_vm_rssize), mode);
+}
+
+void
+cpuid(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_lwp *l = pi->li;
+ VAR *v;
+
+ v = ve->var;
+ intprintorsetwidth(v, l->l_cpuid, mode);
+}
+
+static void
+cputime1(int32_t secs, int32_t psecs, VAR *v, enum mode mode)
+{
+ int fmtlen;
+
+ /*
+ * round and scale to 100's
+ */
+ psecs = (psecs + 5000) / 10000;
+ secs += psecs / 100;
+ psecs = psecs % 100;
+
+ if (mode == WIDTHMODE) {
+ /*
+ * Ugg, this is the only field where a value of 0 is longer
+ * than the column title.
+ * Use SECSPERMIN, because secs is divided by that when
+ * passed to iwidth().
+ */
+ if (secs == 0)
+ secs = SECSPERMIN;
+
+ if (secs > v->longestp) {
+ v->longestp = secs;
+ /* "+6" for the ":%02ld.%02ld" in the printf() below */
+ fmtlen = iwidth(secs / SECSPERMIN) + 6;
+ if (fmtlen > v->width)
+ v->width = fmtlen;
+ }
+ } else {
+ (void)printf("%*ld:%02ld.%02ld", v->width - 6,
+ (long)(secs / SECSPERMIN), (long)(secs % SECSPERMIN),
+ (long)psecs);
+ }
+}
+
+void
+cputime(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+ int32_t secs;
+ int32_t psecs; /* "parts" of a second. first micro, then centi */
+
+ v = ve->var;
+
+ /*
+ * 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->p_rtime_sec;
+ psecs = k->p_rtime_usec;
+ if (sumrusage) {
+ secs += k->p_uctime_sec;
+ psecs += k->p_uctime_usec;
+ }
+
+ cputime1(secs, psecs, v, mode);
+}
+
+void
+lcputime(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_lwp *l = pi->li;
+ VAR *v;
+ int32_t secs;
+ int32_t psecs; /* "parts" of a second. first micro, then centi */
+
+ v = ve->var;
+
+ secs = l->l_rtime_sec;
+ psecs = l->l_rtime_usec;
+
+ cputime1(secs, psecs, v, mode);
+}
+
+void
+pcpu(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ VAR *v;
+ double dbl;
+
+ v = ve->var;
+ dbl = pi->pcpu;
+ doubleprintorsetwidth(v, dbl, (dbl >= 99.95) ? 0 : 1, mode);
+}
+
+double
+getpmem(const struct kinfo_proc2 *k)
+{
+ double fracmem;
+ int szptudot;
+
+ if (!nlistread)
+ donlist();
+
+ /* XXX want pmap ptpages, segtab, etc. (per architecture) */
+ szptudot = uspace/getpagesize();
+ /* XXX don't have info about shared */
+ fracmem = ((float)k->p_vm_rssize + szptudot)/mempages;
+ return (100.0 * fracmem);
+}
+
+void
+pmem(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ doubleprintorsetwidth(v, getpmem(k), 1, mode);
+}
+
+void
+pagein(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ intprintorsetwidth(v, k->p_uvalid ? k->p_uru_majflt : 0, mode);
+}
+
+void
+maxrss(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ VAR *v;
+
+ v = ve->var;
+ /* No need to check width! */
+ if (mode == PRINTMODE)
+ (void)printf("%*s", v->width, "-");
+}
+
+void
+tsize(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_proc2 *k = pi->ki;
+ VAR *v;
+
+ v = ve->var;
+ intprintorsetwidth(v, pgtok(k->p_vm_tsize), mode);
+}
+
+/*
+ * Generic output routines. Print fields from various prototype
+ * structures.
+ */
+static void
+printval(void *bp, VAR *v, enum mode mode)
+{
+ static char ofmt[32] = "%";
+ int width, vok, fmtlen;
+ const char *fcp;
+ char *cp;
+ int64_t val;
+ u_int64_t uval;
+
+ val = 0; /* XXXGCC -Wuninitialized [hpcarm] */
+ uval = 0; /* XXXGCC -Wuninitialized [hpcarm] */
+
+ /*
+ * Note that the "INF127" check is nonsensical for types
+ * that are or can be signed.
+ */
+#define GET(type) (*(type *)bp)
+#define CHK_INF127(n) (((n) > 127) && (v->flag & INF127) ? 127 : (n))
+
+#define VSIGN 1
+#define VUNSIGN 2
+#define VPTR 3
+
+ if (mode == WIDTHMODE) {
+ vok = 0;
+ switch (v->type) {
+ case CHAR:
+ val = GET(char);
+ vok = VSIGN;
+ break;
+ case UCHAR:
+ uval = CHK_INF127(GET(u_char));
+ vok = VUNSIGN;
+ break;
+ case SHORT:
+ val = GET(short);
+ vok = VSIGN;
+ break;
+ case USHORT:
+ uval = CHK_INF127(GET(u_short));
+ vok = VUNSIGN;
+ break;
+ case INT32:
+ val = GET(int32_t);
+ vok = VSIGN;
+ break;
+ case INT:
+ val = GET(int);
+ vok = VSIGN;
+ break;
+ case UINT:
+ case UINT32:
+ uval = CHK_INF127(GET(u_int));
+ vok = VUNSIGN;
+ break;
+ case LONG:
+ val = GET(long);
+ vok = VSIGN;
+ break;
+ case ULONG:
+ uval = CHK_INF127(GET(u_long));
+ vok = VUNSIGN;
+ break;
+ case KPTR:
+ uval = GET(u_int64_t);
+ vok = VPTR;
+ break;
+ case KPTR24:
+ uval = GET(u_int64_t);
+ uval &= 0xffffff;
+ vok = VPTR;
+ break;
+ case INT64:
+ val = GET(int64_t);
+ vok = VSIGN;
+ break;
+ case UINT64:
+ uval = CHK_INF127(GET(u_int64_t));
+ vok = VUNSIGN;
+ break;
+
+ case SIGLIST:
+ default:
+ /* nothing... */;
+ }
+ switch (vok) {
+ case VSIGN:
+ if (val < 0 && val < v->longestn) {
+ v->longestn = val;
+ fmtlen = iwidth(-val) + 1;
+ if (fmtlen > v->width)
+ v->width = fmtlen;
+ } else if (val > 0 && val > v->longestp) {
+ v->longestp = val;
+ fmtlen = iwidth(val);
+ if (fmtlen > v->width)
+ v->width = fmtlen;
+ }
+ return;
+ case VUNSIGN:
+ if (uval > v->longestu) {
+ v->longestu = uval;
+ v->width = iwidth(uval);
+ }
+ return;
+ case VPTR:
+ fmtlen = 0;
+ while (uval > 0) {
+ uval >>= 4;
+ fmtlen++;
+ }
+ if (fmtlen > v->width)
+ v->width = fmtlen;
+ return;
+ }
+ }
+
+ width = v->width;
+ cp = ofmt + 1;
+ fcp = v->fmt;
+ if (v->flag & LJUST)
+ *cp++ = '-';
+ *cp++ = '*';
+ while ((*cp++ = *fcp++) != '\0')
+ continue;
+
+ switch (v->type) {
+ case CHAR:
+ (void)printf(ofmt, width, GET(char));
+ return;
+ case UCHAR:
+ (void)printf(ofmt, width, CHK_INF127(GET(u_char)));
+ return;
+ case SHORT:
+ (void)printf(ofmt, width, GET(short));
+ return;
+ case USHORT:
+ (void)printf(ofmt, width, CHK_INF127(GET(u_short)));
+ return;
+ case INT:
+ (void)printf(ofmt, width, GET(int));
+ return;
+ case UINT:
+ (void)printf(ofmt, width, CHK_INF127(GET(u_int)));
+ return;
+ case LONG:
+ (void)printf(ofmt, width, GET(long));
+ return;
+ case ULONG:
+ (void)printf(ofmt, width, CHK_INF127(GET(u_long)));
+ return;
+ case KPTR:
+ (void)printf(ofmt, width, GET(u_int64_t));
+ return;
+ case KPTR24:
+ (void)printf(ofmt, width, GET(u_int64_t) & 0xffffff);
+ return;
+ case INT32:
+ (void)printf(ofmt, width, GET(int32_t));
+ return;
+ case UINT32:
+ (void)printf(ofmt, width, CHK_INF127(GET(u_int32_t)));
+ return;
+ case SIGLIST:
+ {
+ sigset_t *s = (sigset_t *)(void *)bp;
+ size_t i;
+#define SIGSETSIZE (sizeof(s->__bits) / sizeof(s->__bits[0]))
+ char buf[SIGSETSIZE * 8 + 1];
+
+ for (i = 0; i < SIGSETSIZE; i++)
+ (void)snprintf(&buf[i * 8], 9, "%.8x",
+ s->__bits[(SIGSETSIZE - 1) - i]);
+
+ /* Skip leading zeroes */
+ for (i = 0; buf[i] == '0'; i++)
+ continue;
+
+ if (buf[i] == '\0')
+ i--;
+ strprintorsetwidth(v, buf + i, mode);
+#undef SIGSETSIZE
+ }
+ return;
+ case INT64:
+ (void)printf(ofmt, width, GET(int64_t));
+ return;
+ case UINT64:
+ (void)printf(ofmt, width, CHK_INF127(GET(u_int64_t)));
+ return;
+ default:
+ errx(EXIT_FAILURE, "unknown type %d", v->type);
+ }
+#undef GET
+#undef CHK_INF127
+}
+
+void
+pvar(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ VAR *v = ve->var;
+ char *b = (v->flag & LWP) ? (char *)pi->li : (char *)pi->ki;
+
+ if ((v->flag & UAREA) && !pi->ki->p_uvalid) {
+ if (mode == PRINTMODE)
+ (void)printf("%*s", v->width, "-");
+ return;
+ }
+
+ (void)printval(b + v->off, v, mode);
+}
+
+void
+putimeval(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ VAR *v = ve->var;
+ struct kinfo_proc2 *k = pi->ki;
+ char *b = (v->flag & LWP) ? (char *)pi->li : (char *)pi->ki;
+ ulong secs = *(uint32_t *)(b + v->off);
+ ulong usec = *(uint32_t *)(b + v->off + sizeof (uint32_t));
+ int fmtlen;
+
+ if (!k->p_uvalid) {
+ if (mode == PRINTMODE)
+ (void)printf("%*s", v->width, "-");
+ return;
+ }
+
+ if (mode == WIDTHMODE) {
+ if (secs == 0)
+ /* non-zero so fmtlen is calculated at least once */
+ secs = 1;
+ if (secs > v->longestu) {
+ v->longestu = secs;
+ if (secs <= 999)
+ /* sss.ssssss */
+ fmtlen = iwidth(secs) + 6 + 1;
+ else
+ /* hh:mm:ss.ss */
+ fmtlen = iwidth((secs + 1) / SECSPERHOUR)
+ + 2 + 1 + 2 + 1 + 2 + 1;
+ if (fmtlen > v->width)
+ v->width = fmtlen;
+ }
+ return;
+ }
+
+ if (secs < 999)
+ (void)printf( "%*lu.%.6lu", v->width - 6 - 1, secs, usec);
+ else {
+ uint h, m;
+ usec += 5000;
+ if (usec >= 1000000) {
+ usec -= 1000000;
+ secs++;
+ }
+ m = secs / SECSPERMIN;
+ secs -= m * SECSPERMIN;
+ h = m / MINSPERHOUR;
+ m -= h * MINSPERHOUR;
+ (void)printf( "%*u:%.2u:%.2lu.%.2lu", v->width - 9, h, m, secs,
+ usec / 10000u );
+ }
+}
+
+void
+lname(struct pinfo *pi, VARENT *ve, enum mode mode)
+{
+ struct kinfo_lwp *l = pi->li;
+ VAR *v;
+
+ v = ve->var;
+ if (l->l_name[0] != '\0') {
+ strprintorsetwidth(v, l->l_name, mode);
+ v->width = min(v->width, KI_LNAMELEN);
+ } else {
+ if (mode == PRINTMODE)
+ (void)printf("%-*s", v->width, "-");
+ }
+}
diff --git a/bin/ps/ps.1 b/bin/ps/ps.1
new file mode 100644
index 0000000..635ec3b
--- /dev/null
+++ b/bin/ps/ps.1
@@ -0,0 +1,706 @@
+.\" $NetBSD: ps.1,v 1.109 2017/08/28 05:57:37 wiz Exp $
+.\"
+.\" 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.
+.\" 3. 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
+.\"
+.Dd August 28, 2017
+.Dt PS 1
+.Os
+.Sh NAME
+.Nm ps
+.Nd process status
+.Sh SYNOPSIS
+.Nm
+.Op Fl AaCcdehjlmrSsTuvwx
+.Op Fl k Ar key
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Op Fl O Ar fmt
+.Op Fl o Ar fmt
+.Op Fl p Ar pid
+.Op Fl t Ar tty
+.Op Fl U Ar user
+.Op Fl W Ar swap
+.Nm
+.Fl L
+.Sh DESCRIPTION
+.Nm
+displays a header line followed by lines containing information about
+running processes.
+By default, the display includes only processes that have
+controlling terminals and are owned by your uid.
+The default sort order of controlling terminal and
+(among processes with the same controlling terminal) process ID
+may be changed using the
+.Fl k , Fl m ,
+or
+.Fl r
+options.
+.Pp
+The information displayed for each process
+is selected based on a set of keywords (see the
+.Fl L ,
+.Fl O ,
+and
+.Fl o
+options).
+The default output format includes, for each process, the process' ID,
+controlling terminal, CPU time (including both user and system time),
+state, and associated command.
+.Pp
+The options are as follows:
+.Bl -tag -width XNXsystemXX
+.It Fl A
+Display information about all processes.
+This is equivalent to
+.Fl a Fl x .
+.It Fl a
+Display information about other users' processes as well as your own.
+Note that this does not display information about processes
+without controlling terminals.
+.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 c
+Do not display full command with arguments, but only the
+executable name.
+This may be somewhat confusing; for example, all
+.Xr sh 1
+scripts will show as
+.Dq sh .
+.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.
+.It Fl e
+Display the environment as well.
+The environment for other
+users' processes can only be displayed by the super-user.
+.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:
+.Ar user , pid , ppid , pgid , sess , jobc , state , tt , time ,
+and
+.Ar command .
+.It Fl k Ar key
+Sort the output using the space or comma separated list of keywords.
+Multiple sort keys may be specified, using any of the
+.Fl k , Fl m ,
+or
+.Fl r
+options.
+The default sort order is equivalent to
+.Fl k Ar tdev,pid .
+.It Fl L
+List the set of available keywords.
+.It Fl l
+Display information associated with the following keywords:
+.Ar uid , pid , ppid , cpu , pri , nice , vsz , rss , wchan , state ,
+.Ar tt , time ,
+and
+.Ar command .
+.It Fl M Ar core
+Extract values from the specified core file instead of the running system.
+.It Fl m
+Sort by memory usage.
+This is equivalent to
+.Fl k Ar vsz .
+.It Fl N Ar system
+Extract the name list from the specified system instead of the default,
+.Dq Pa /netbsd .
+Ignored unless
+.Fl M
+is specified.
+.It Fl O Ar fmt
+Display information associated with the space or comma separated list
+of keywords specified.
+The
+.Fl O
+option does not suppress the default display;
+it inserts additional keywords just after the
+.Ar pid
+keyword in the default display, or after the
+.Ar pid
+keyword (if any) in a non-default display specified before the
+first use of the
+.Fl O
+flag.
+Keywords inserted by multiple
+.Fl O
+options will be adjacent.
+.Pp
+An equals sign
+.Pq Dq \&=
+followed by a customised header string may be appended to a keyword,
+as described in more detail under the
+.Fl o
+option.
+.It Fl o Ar fmt
+Display information associated with the space or comma separated list
+of keywords specified.
+Use of the
+.Fl o
+option suppresses the set of keywords that would be displayed by default,
+or appends to the set of keywords specified by other options.
+.Pp
+An equals sign
+.Pq Dq \&=
+followed by a customised header string may be appended to a keyword.
+This causes the printed header to use the specified string instead of
+the default header associated with the keyword.
+.Pp
+Everything after the first equals sign is part of the customised
+header text, and this may include embedded spaces
+.Pq Dq " " ,
+commas
+.Pq Dq \&, ,
+or equals signs
+.Pq Dq \&= .
+To specify multiple keywords with customised headers, use multiple
+.Fl o
+or
+.Fl O
+options.
+.Pp
+If all the keywords to be displayed have customised headers,
+and all the customised headers are entirely empty,
+then the header line is not printed at all.
+.It Fl p Ar pid
+Display information associated with the specified process ID.
+.It Fl r
+Sort by current CPU usage.
+This is equivalent to
+.Fl k Ar %cpu .
+.It Fl S
+Change the way the process time is calculated by summing all exited
+children to their parent process.
+.It Fl s
+Display one line for each LWP, rather than one line for each process,
+and display information associated with the following keywords:
+.Ar uid , pid , ppid , cpu , lid , nlwp , pri , nice , vsz , rss ,
+.Ar wchan , lstate , tt , time ,
+and
+.Ar command .
+.It Fl T
+Display information about processes attached to the device associated
+with the standard input.
+.It Fl t Ar tty
+Display information about processes attached to the specified terminal
+device.
+Use a question mark
+.Pq Dq \&?
+for processes not attached to a
+terminal device and a minus sign
+.Pq Dq -
+for processes that have
+been revoked from their terminal device.
+.It Fl U Ar user
+Display processes belonging to the specified user,
+given either as a user name or a uid.
+.It Fl u
+Display information associated with the following keywords:
+.Ar user , pid , %cpu , %mem , vsz , rss , tt , state , start , time ,
+and
+.Ar command .
+The
+.Fl u
+option implies the
+.Fl r
+option.
+.It Fl v
+Display information associated with the following keywords:
+.Ar pid , state , time , sl , re , pagein , vsz , rss , lim , tsiz ,
+.Ar %cpu , %mem ,
+and
+.Ar command .
+The
+.Fl v
+option implies the
+.Fl m
+option.
+.It Fl W Ar swap
+Extract swap information from the specified file instead of the default,
+.Dq Pa /dev/drum .
+Ignored unless
+.Fl M
+is specified.
+.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 to your window size.
+.It Fl x
+Also display information about processes without controlling terminals.
+.El
+.Pp
+A complete list of the available keywords are listed below.
+Some of these keywords are further specified as follows:
+.Bl -tag -width indent
+.It Ar %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 %CPU fields to exceed 100%.
+.It Ar %mem
+The percentage of real memory used by this process.
+.It Ar flags
+The flags (in hexadecimal) associated with the process as in
+the include file
+.In sys/proc.h :
+.Bl -column P_NOCLDSTOP P_NOCLDSTOP compact
+.It Dv "P_ADVLOCK" Ta No "0x00000001 process may hold a POSIX advisory lock"
+.It Dv "P_CONTROLT" Ta No "0x00000002 process has a controlling terminal"
+.It Dv "P_NOCLDSTOP" Ta No "0x00000008 no" Dv SIGCHLD No when children stop
+.It Dv "P_PPWAIT" Ta No "0x00000010 parent is waiting for child to exec/exit"
+.It Dv "P_PROFIL" Ta No "0x00000020 process has started profiling"
+.It Dv "P_SELECT" Ta No "0x00000040 selecting; wakeup/waiting danger"
+.It Dv "P_SINTR" Ta No "0x00000080 sleep is interruptible"
+.It Dv "P_SUGID" Ta No "0x00000100 process had set id privileges since last exec"
+.It Dv "P_SYSTEM" Ta No "0x00000200 system process: no sigs or stats"
+.It Dv "P_TIMEOUT" Ta No "0x00000400 timing out during sleep"
+.It Dv "P_TRACED" Ta No "0x00000800 process is being traced"
+.It Dv "P_WAITED" Ta No "0x00001000 debugging process has waited for child"
+.It Dv "P_WEXIT" Ta No "0x00002000 working on exiting"
+.It Dv "P_EXEC" Ta No "0x00004000 process called" Xr execve 2
+.It Dv "P_OWEUPC" Ta No "0x00008000 owe process an addupc() call at next ast"
+.\" the routine addupc is not documented in the man pages
+.It Dv "P_NOCLDWAIT" Ta No "0x00020000 no zombies when children die"
+.It Dv "P_32" Ta No "0x00040000 32-bit process (used on 64-bit kernels)"
+.It Dv "P_BIGLOCK" Ta No "0x00080000 process needs kernel ``big lock'' to run"
+.It Dv "P_INEXEC" Ta No "0x00100000 process is exec'ing and cannot be traced"
+.El
+.It Ar lim
+The soft limit on memory used, specified via a call to
+.Xr setrlimit 2 .
+.It Ar lstart
+The exact time the command started, using the
+.Dq \&%c
+format described in
+.Xr strftime 3 .
+.It Ar nice
+The process scheduling increment (see
+.Xr setpriority 2 ) .
+.It Ar rss
+the real memory (resident set) size of the process (in 1024 byte units).
+.It Ar start
+The time the command started.
+If the command started less than 24 hours ago, the start time is
+displayed using the
+.Dq %l:%M%p
+format described in
+.Xr strftime 3 .
+If the command started less than 7 days ago, the start time is
+displayed using the
+.Dq %a%p
+format.
+Otherwise, the start time is displayed using the
+.Dq %e%b%y
+format.
+.It Ar state
+The state is given by a sequence of letters, for example,
+.Dq RNs .
+The first letter indicates the run state of the process:
+.Pp
+.Bl -tag -width indent -compact
+.It D
+Marks a process in device or other short term, uninterruptible wait.
+.It I
+Marks a process that is idle (sleeping interruptibly for longer than about
+.Dv MAXSLP
+(default 20) seconds).
+.It O
+Marks a process running on a processor.
+.It R
+Marks a runnable process, or one that is in the process of creation.
+.It S
+Marks a process that is sleeping interruptibly for less than about
+.Dv MAXSLP
+(default 20) seconds.
+.It T
+Marks a stopped process.
+.It U
+Marks a suspended process.
+.It Z
+Marks a dead process that has exited, but not been waited for (a
+.Dq zombie ) .
+.El
+.Pp
+Additional characters after these, if any, indicate additional state
+information:
+.Pp
+.Bl -tag -width indent -compact
+.It +
+The process is in the foreground process group of its control terminal.
+.It -
+The LWP is detached (can't be waited for).
+.It <
+The process has raised CPU scheduling priority.
+.It a
+The process is using scheduler activations (deprecated).
+.It E
+The process is in the process of exiting.
+.It K
+The process is a kernel thread or system process.
+.It l
+The process has multiple LWPs.
+.It N
+The process is niced (has reduced CPU scheduling priority) (see
+.Xr setpriority 2 ) .
+.It s
+The process is a session leader.
+.It V
+The process is suspended during a
+.Xr vfork 2 .
+.It X
+The process is being traced or debugged.
+.El
+.It Ar tt
+An abbreviation for the pathname of the controlling terminal, if any.
+The abbreviation consists of the two letters following
+.Dq Pa /dev/tty
+or, for the console,
+.Dq co .
+This is followed by a
+.Dq \&-
+if the process can no longer reach that
+controlling terminal (i.e., it has been revoked).
+.It Ar 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
+.Ar 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 Aq defunct ,
+and a process which is blocked while trying to exit is listed as
+.Dq Aq exiting .
+.Pp
+.Nm
+will try to locate the processes' argument vector from the user
+area in order to print the command name and arguments.
+This method is not reliable because a process is allowed to destroy this
+information.
+The
+.Ar ucomm
+(accounting) keyword will always contain the real command name as
+contained in the process structure's
+.Va p_comm
+field.
+.Pp
+If the command vector 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.
+.Pp
+To indicate that the argument vector has been tampered with,
+.Nm
+will append the real command name to the output within parentheses
+if the basename of the first argument in the argument vector
+does not match the contents of the real command name.
+.Pp
+In addition,
+.Nm
+checks for the following two situations and does not append the
+real command name parenthesized:
+.Bl -tag -width indent
+.It -shellname
+The login process traditionally adds a
+.Sq -
+in front of the shell name to indicate a login shell.
+.Nm
+will not append parenthesized the command name if it matches with
+the name in the first argument of the argument vector, skipping
+the leading
+.Sq - .
+.It daemonname: current-activity
+Daemon processes frequently report their current activity by setting
+their name to be like
+.Dq daemonname: current-activity .
+.Nm
+will not append parenthesized the command name, if the string preceding the
+.Sq \&:
+in the first argument of the argument vector matches the command name.
+.El
+.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 groupnames -compact
+.It Ar %cpu
+percentage CPU usage (alias
+.Ar pcpu )
+.It Ar %mem
+percentage memory usage (alias
+.Ar pmem )
+.It Ar acflag
+accounting flag (alias
+.Ar acflg )
+.It Ar comm
+command (the argv[0] value)
+.It Ar command
+command and arguments (alias
+.Ar args )
+.It Ar cpu
+short-term CPU usage factor (for scheduling)
+.It Ar cpuid
+CPU number the current process or lwp is running on.
+.It Ar ctime
+accumulated CPU time of all children that have exited
+.It Ar egid
+effective group id
+.It Ar egroup
+group name (from egid)
+.It Ar emul
+emulation name
+.It Ar etime
+elapsed time since the process was started, in the form
+.Li [[dd-]hh:]mm:ss
+.It Ar euid
+effective user id
+.It Ar euser
+user name (from euid)
+.It Ar flags
+the process flags, in hexadecimal (alias
+.Ar f )
+.It Ar gid
+effective group id
+.It Ar group
+group name (from gid)
+.It Ar groupnames
+group names (from group access list)
+.It Ar groups
+group access list
+.It Ar inblk
+total blocks read (alias
+.Ar inblock )
+.It Ar jobc
+job control count
+.It Ar ktrace
+tracing flags
+.It Ar ktracep
+tracing vnode
+.It Ar laddr
+kernel virtual address of the
+.Ft "struct lwp"
+belonging to the LWP.
+.It Ar lid
+ID of the LWP
+.It Ar lim
+memory use limit
+.It Ar lname
+descriptive name of the LWP
+.It Ar logname
+login name of user who started the process (alias
+.Ar login )
+.It Ar lstart
+time started
+.It Ar lstate
+symbolic LWP state
+.It Ar ltime
+CPU time of the LWP
+.It Ar majflt
+total page faults
+.It Ar minflt
+total page reclaims
+.It Ar msgrcv
+total messages received (reads from pipes/sockets)
+.It Ar msgsnd
+total messages sent (writes on pipes/sockets)
+.It Ar nice
+nice value (alias
+.Ar ni )
+.It Ar nivcsw
+total involuntary context switches
+.It Ar nlwp
+number of LWPs in the process
+.It Ar nsigs
+total signals taken (alias
+.Ar nsignals )
+.It Ar nvcsw
+total voluntary context switches
+.It Ar nwchan
+wait channel (as an address)
+.It Ar oublk
+total blocks written (alias
+.Ar oublock )
+.It Ar p_ru
+resource usage pointer (valid only for zombie)
+.It Ar paddr
+kernel virtual address of the
+.Ft "struct proc"
+belonging to the process.
+.It Ar pagein
+pageins (same as majflt)
+.It Ar pgid
+process group number
+.It Ar pid
+process ID
+.It Ar ppid
+parent process ID
+.It Ar pri
+scheduling priority
+.It Ar re
+core residency time (in seconds; 127 = infinity)
+.It Ar rgid
+real group ID
+.It Ar rlink
+reverse link on run queue, or 0
+.It Ar rlwp
+number of LWPs on a processor or run queue
+.It Ar rss
+resident set size
+.It Ar rsz
+resident set size + (text size / text use count) (alias
+.Ar rssize )
+.It Ar ruid
+real user ID
+.It Ar ruser
+user name (from ruid)
+.It Ar sess
+session pointer
+.It Ar sid
+session ID
+.It Ar sig
+pending signals (alias
+.Ar pending )
+.It Ar sigcatch
+caught signals (alias
+.Ar caught )
+.It Ar sigignore
+ignored signals (alias
+.Ar ignored )
+.It Ar sigmask
+blocked signals (alias
+.Ar blocked )
+.It Ar sl
+sleep time (in seconds; 127 = infinity)
+.It Ar start
+time started
+.It Ar state
+symbolic process state (alias
+.Ar stat )
+.It Ar stime
+accumulated system CPU time
+.It Ar svgid
+saved gid from a setgid executable
+.It Ar svgroup
+group name (from svgid)
+.It Ar svuid
+saved uid from a setuid executable
+.It Ar svuser
+user name (from svuid)
+.It Ar tdev
+control terminal device number
+.It Ar time
+accumulated CPU time, user + system (alias
+.Ar cputime )
+.It Ar tpgid
+control terminal process group ID
+.It Ar tsess
+control terminal session pointer
+.It Ar tsiz
+text size (in Kbytes)
+.It Ar tt
+control terminal name (two letter abbreviation)
+.It Ar tty
+full name of control terminal
+.It Ar uaddr
+kernel virtual address of the
+.Ft "struct user"
+belonging to the LWP.
+.It Ar ucomm
+name to be used for accounting
+.It Ar uid
+effective user ID
+.It Ar upr
+scheduling priority on return from system call (alias
+.Ar usrpri )
+.It Ar user
+user name (from uid)
+.It Ar utime
+accumulated user CPU time
+.It Ar vsz
+virtual size in Kbytes (alias
+.Ar vsize )
+.It Ar wchan
+wait channel (as a symbolic name)
+.It Ar xstat
+exit or stop status (valid only for stopped or zombie process)
+.El
+.Sh FILES
+.Bl -tag -width /var/run/kvm.db -compact
+.It Pa /dev
+special files and device names
+.It Pa /dev/drum
+default swap device
+.It Pa /var/run/dev.cdb
+/dev name database
+.It Pa /var/db/kvm.db
+system name list database
+.It Pa /netbsd
+default system name list
+.El
+.Sh SEE ALSO
+.Xr kill 1 ,
+.Xr pgrep 1 ,
+.Xr pkill 1 ,
+.Xr sh 1 ,
+.Xr w 1 ,
+.Xr kvm 3 ,
+.Xr strftime 3 ,
+.Xr dev_mkdb 8 ,
+.Xr pstat 8
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v3
+in section 8 of the manual.
+.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.
diff --git a/bin/ps/ps.c b/bin/ps/ps.c
new file mode 100644
index 0000000..da26fc1
--- /dev/null
+++ b/bin/ps/ps.c
@@ -0,0 +1,947 @@
+/* $NetBSD: ps.c,v 1.91 2018/04/11 18:52:29 christos Exp $ */
+
+/*
+ * Copyright (c) 2000-2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Simon Burge.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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) 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.
+ * 3. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94";
+#else
+__RCSID("$NetBSD: ps.c,v 1.91 2018/04/11 18:52:29 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/lwp.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+
+#include <stddef.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <locale.h>
+#include <math.h>
+#include <nlist.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ps.h"
+
+/*
+ * ARGOPTS must contain all option characters that take arguments
+ * (except for 't'!) - it is used in kludge_oldps_options()
+ */
+#define GETOPTSTR "aAcCdeghjk:LlM:mN:O:o:p:rSsTt:U:uvW:wx"
+#define ARGOPTS "kMNOopUW"
+
+struct varlist displaylist = SIMPLEQ_HEAD_INITIALIZER(displaylist);
+struct varlist sortlist = SIMPLEQ_HEAD_INITIALIZER(sortlist);
+
+int eval; /* exit value */
+int sumrusage; /* -S */
+int termwidth; /* width of screen (0 == infinity) */
+int totwidth; /* calculated width of requested variables */
+
+int needcomm, needenv, commandonly;
+uid_t myuid;
+
+static struct kinfo_lwp
+ *pick_representative_lwp(struct kinfo_proc2 *,
+ struct kinfo_lwp *, int);
+static struct kinfo_proc2
+ *getkinfo_kvm(kvm_t *, int, int, int *);
+static struct pinfo
+ *setpinfo(struct kinfo_proc2 *, int, int, int);
+static char *kludge_oldps_options(char *);
+static int pscomp(const void *, const void *);
+static void scanvars(void);
+__dead static void usage(void);
+static int parsenum(const char *, const char *);
+static void descendant_sort(struct pinfo *, int);
+
+char dfmt[] = "pid tt state time command";
+char jfmt[] = "user pid ppid pgid sess jobc state tt time command";
+char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command";
+char sfmt[] = "uid pid ppid cpu lid nlwp pri nice vsz rss wchan lstate tt "
+ "ltime command";
+char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command";
+char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command";
+
+const char *default_fmt = dfmt;
+
+struct varent *Opos = NULL; /* -O flag inserts after this point */
+
+kvm_t *kd;
+
+static long long
+ttyname2dev(const char *ttname, int *xflg, int *what)
+{
+ struct stat sb;
+ const char *ttypath;
+ char pathbuf[MAXPATHLEN];
+
+ ttypath = NULL;
+ if (strcmp(ttname, "?") == 0) {
+ *xflg = 1;
+ return KERN_PROC_TTY_NODEV;
+ }
+ if (strcmp(ttname, "-") == 0)
+ return KERN_PROC_TTY_REVOKE;
+
+ if (strcmp(ttname, "co") == 0)
+ ttypath = _PATH_CONSOLE;
+ else if (strncmp(ttname, "pts/", 4) == 0 ||
+ strncmp(ttname, "tty", 3) == 0) {
+ (void)snprintf(pathbuf,
+ sizeof(pathbuf), "%s%s", _PATH_DEV, ttname);
+ ttypath = pathbuf;
+ } else if (*ttname != '/') {
+ (void)snprintf(pathbuf,
+ sizeof(pathbuf), "%s%s", _PATH_TTY, ttname);
+ ttypath = pathbuf;
+ } else
+ ttypath = ttname;
+ *what = KERN_PROC_TTY;
+ if (stat(ttypath, &sb) == -1) {
+ devmajor_t pts;
+ int serrno;
+
+ serrno = errno;
+ pts = getdevmajor("pts", S_IFCHR);
+ if (pts != NODEVMAJOR && strncmp(ttname, "pts/", 4) == 0) {
+ int ptsminor = atoi(ttname + 4);
+
+ snprintf(pathbuf, sizeof(pathbuf), "pts/%d", ptsminor);
+ if (strcmp(pathbuf, ttname) == 0 && ptsminor >= 0)
+ return makedev(pts, ptsminor);
+ }
+ errno = serrno;
+ err(EXIT_FAILURE, "%s", ttypath);
+ }
+ if (!S_ISCHR(sb.st_mode))
+ errx(EXIT_FAILURE, "%s: not a terminal", ttypath);
+ return sb.st_rdev;
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct kinfo_proc2 *kinfo;
+ struct pinfo *pinfo;
+ struct varent *vent;
+ struct winsize ws;
+ struct kinfo_lwp *kl, *l;
+ int ch, i, j, fmt, lineno, descendancy, nentries, nlwps;
+ long long flag;
+ int calc_pcpu, prtheader, wflag, what, xflg, rawcpu, showlwps;
+ char *nlistf, *memf, *swapf, errbuf[_POSIX2_LINE_MAX];
+ char *ttname;
+
+ setprogname(argv[0]);
+ (void)setlocale(LC_ALL, "");
+
+ 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;
+
+ if (argc > 1)
+ argv[1] = kludge_oldps_options(argv[1]);
+
+ descendancy = fmt = prtheader = wflag = xflg = rawcpu = showlwps = 0;
+ what = KERN_PROC_UID;
+ flag = myuid = getuid();
+ memf = nlistf = swapf = NULL;
+
+ while ((ch = getopt(argc, argv, GETOPTSTR)) != -1)
+ switch((char)ch) {
+ case 'A':
+ /* "-A" shows all processes, like "-ax" */
+ xflg = 1;
+ /*FALLTHROUGH*/
+ case 'a':
+ what = KERN_PROC_ALL;
+ flag = 0;
+ break;
+ case 'c':
+ commandonly = 1;
+ break;
+ case 'd':
+ descendancy = 1;
+ break;
+ case 'e': /* XXX set ufmt */
+ needenv = 1;
+ break;
+ case 'C':
+ rawcpu = 1;
+ break;
+ case 'g':
+ break; /* no-op */
+ case 'h':
+ prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
+ break;
+ case 'j':
+ parsefmt(jfmt);
+ fmt = 1;
+ jfmt[0] = '\0';
+ break;
+ case 'k':
+ parsesort(optarg);
+ break;
+ case 'K':
+ break; /* no-op - was dontuseprocfs */
+ case 'L':
+ showkey();
+ return 0;
+ case 'l':
+ parsefmt(lfmt);
+ fmt = 1;
+ lfmt[0] = '\0';
+ break;
+ case 'M':
+ memf = optarg;
+ break;
+ case 'm':
+ parsesort("vsz");
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'O':
+ /*
+ * If this is not the first -O option, insert
+ * just after the previous one.
+ *
+ * If there is no format yet, start with the default
+ * format, and insert after the pid column.
+ *
+ * If there is already a format, insert after
+ * the pid column, or at the end if there's no
+ * pid column.
+ */
+ if (!Opos) {
+ if (!fmt)
+ parsefmt(default_fmt);
+ Opos = varlist_find(&displaylist, "pid");
+ }
+ parsefmt_insert(optarg, &Opos);
+ fmt = 1;
+ break;
+ case 'o':
+ parsefmt(optarg);
+ fmt = 1;
+ break;
+ case 'p':
+ what = KERN_PROC_PID;
+ flag = parsenum(optarg, "process id");
+ xflg = 1;
+ break;
+ case 'r':
+ parsesort("%cpu");
+ break;
+ case 'S':
+ sumrusage = 1;
+ break;
+ case 's':
+ /* -L was already taken... */
+ showlwps = 1;
+ default_fmt = sfmt;
+ break;
+ case 'T':
+ if ((ttname = ttyname(STDIN_FILENO)) == NULL)
+ errx(EXIT_FAILURE, "stdin: not a terminal");
+ flag = ttyname2dev(ttname, &xflg, &what);
+ break;
+ case 't':
+ flag = ttyname2dev(optarg, &xflg, &what);
+ break;
+ case 'U':
+ if (*optarg != '\0') {
+ struct passwd *pw;
+
+ what = KERN_PROC_UID;
+ pw = getpwnam(optarg);
+ if (pw == NULL) {
+ flag = parsenum(optarg, "user name");
+ } else
+ flag = pw->pw_uid;
+ }
+ break;
+ case 'u':
+ parsefmt(ufmt);
+ parsesort("%cpu");
+ fmt = 1;
+ ufmt[0] = '\0';
+ break;
+ case 'v':
+ parsefmt(vfmt);
+ parsesort("vsz");
+ fmt = 1;
+ vfmt[0] = '\0';
+ break;
+ case 'W':
+ swapf = optarg;
+ break;
+ case 'w':
+ if (wflag)
+ termwidth = UNLIMITED;
+ else if (termwidth < 131)
+ termwidth = 131;
+ wflag++;
+ break;
+ case 'x':
+ xflg = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+#define BACKWARD_COMPATIBILITY
+#ifdef BACKWARD_COMPATIBILITY
+ if (*argv) {
+ nlistf = *argv;
+ if (*++argv) {
+ memf = *argv;
+ if (*++argv)
+ swapf = *argv;
+ }
+ }
+#endif
+
+ if (memf == NULL) {
+ kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
+ donlist_sysctl();
+ } else
+ kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf);
+
+ if (kd == NULL)
+ errx(EXIT_FAILURE, "%s", errbuf);
+
+ if (!fmt)
+ parsefmt(default_fmt);
+
+ /* Add default sort criteria */
+ parsesort("tdev,pid");
+ calc_pcpu = 0;
+ SIMPLEQ_FOREACH(vent, &sortlist, next) {
+ if (vent->var->flag & LWP || vent->var->type == UNSPECIFIED)
+ warnx("Cannot sort on %s, sort key ignored",
+ vent->var->name);
+ if (vent->var->type == PCPU)
+ calc_pcpu = 1;
+ }
+ if (!calc_pcpu)
+ SIMPLEQ_FOREACH(vent, &displaylist, next)
+ if (vent->var->type == PCPU) {
+ calc_pcpu = 1;
+ break;
+ }
+
+ /*
+ * scan requested variables, noting what structures are needed.
+ */
+ scanvars();
+
+ /*
+ * select procs
+ */
+ if (!(kinfo = getkinfo_kvm(kd, what, flag, &nentries)))
+ errx(EXIT_FAILURE, "%s", kvm_geterr(kd));
+ if (nentries == 0) {
+ printheader();
+ return 1;
+ }
+ pinfo = setpinfo(kinfo, nentries, calc_pcpu, rawcpu);
+
+ /*
+ * sort proc list
+ */
+ qsort(pinfo, nentries, sizeof(struct pinfo), pscomp);
+
+ /*
+ * We want things in descendant order
+ */
+ if (descendancy)
+ descendant_sort(pinfo, nentries);
+
+ /*
+ * For each proc, call each variable output function in
+ * "setwidth" mode to determine the widest element of
+ * the column.
+ */
+
+ for (i = 0; i < nentries; i++) {
+ struct pinfo *pi = &pinfo[i];
+ struct kinfo_proc2 *ki = pi->ki;
+
+ if (xflg == 0 && (ki->p_tdev == (uint32_t)NODEV ||
+ (ki->p_flag & P_CONTROLT) == 0))
+ continue;
+
+ kl = kvm_getlwps(kd, ki->p_pid, ki->p_paddr,
+ sizeof(struct kinfo_lwp), &nlwps);
+ if (kl == 0)
+ nlwps = 0;
+ if (showlwps == 0) {
+ l = pick_representative_lwp(ki, kl, nlwps);
+ SIMPLEQ_FOREACH(vent, &displaylist, next)
+ OUTPUT(vent, l, pi, ki, WIDTHMODE);
+ } else {
+ /* The printing is done with the loops
+ * reversed, but here we don't need that,
+ * and this improves the code locality a bit.
+ */
+ SIMPLEQ_FOREACH(vent, &displaylist, next)
+ for (j = 0; j < nlwps; j++)
+ OUTPUT(vent, &kl[j], pi, ki, WIDTHMODE);
+ }
+ }
+ /*
+ * Print header - AFTER determining process field widths.
+ * printheader() also adds up the total width of all
+ * fields the first time it's called.
+ */
+ printheader();
+ /*
+ * For each proc, call each variable output function in
+ * print mode.
+ */
+ for (i = lineno = 0; i < nentries; i++) {
+ struct pinfo *pi = &pinfo[i];
+ struct kinfo_proc2 *ki = pi->ki;
+
+ if (xflg == 0 && (ki->p_tdev == (uint32_t)NODEV ||
+ (ki->p_flag & P_CONTROLT ) == 0))
+ continue;
+ kl = kvm_getlwps(kd, ki->p_pid, (u_long)ki->p_paddr,
+ sizeof(struct kinfo_lwp), &nlwps);
+ if (kl == 0)
+ nlwps = 0;
+ if (showlwps == 0) {
+ l = pick_representative_lwp(ki, kl, nlwps);
+ SIMPLEQ_FOREACH(vent, &displaylist, next) {
+ OUTPUT(vent, l, pi, ki, PRINTMODE);
+ if (SIMPLEQ_NEXT(vent, next) != NULL)
+ (void)putchar(' ');
+ }
+ (void)putchar('\n');
+ if (prtheader && lineno++ == prtheader - 4) {
+ (void)putchar('\n');
+ printheader();
+ lineno = 0;
+ }
+ } else {
+ for (j = 0; j < nlwps; j++) {
+ SIMPLEQ_FOREACH(vent, &displaylist, next) {
+ OUTPUT(vent, &kl[j], pi, ki, PRINTMODE);
+ if (SIMPLEQ_NEXT(vent, next) != NULL)
+ (void)putchar(' ');
+ }
+ (void)putchar('\n');
+ if (prtheader && lineno++ == prtheader - 4) {
+ (void)putchar('\n');
+ printheader();
+ lineno = 0;
+ }
+ }
+ }
+ }
+ return eval;
+}
+
+static struct kinfo_lwp *
+pick_representative_lwp(struct kinfo_proc2 *ki, struct kinfo_lwp *kl, int nlwps)
+{
+ int i, onproc, running, sleeping, stopped, suspended;
+ static struct kinfo_lwp zero_lwp;
+
+ if (kl == 0)
+ return &zero_lwp;
+
+ /* Trivial case: only one LWP */
+ if (nlwps == 1)
+ return kl;
+
+ switch (ki->p_realstat) {
+ case SSTOP:
+ case SACTIVE:
+ /* Pick the most live LWP */
+ onproc = running = sleeping = stopped = suspended = -1;
+ for (i = 0; i < nlwps; i++) {
+ switch (kl[i].l_stat) {
+ case LSONPROC:
+ onproc = i;
+ break;
+ case LSRUN:
+ running = i;
+ break;
+ case LSSLEEP:
+ sleeping = i;
+ break;
+ case LSSTOP:
+ stopped = i;
+ break;
+ case LSSUSPENDED:
+ suspended = i;
+ break;
+ }
+ }
+ if (onproc != -1)
+ return &kl[onproc];
+ if (running != -1)
+ return &kl[running];
+ if (sleeping != -1)
+ return &kl[sleeping];
+ if (stopped != -1)
+ return &kl[stopped];
+ if (suspended != -1)
+ return &kl[suspended];
+ break;
+ case SZOMB:
+ /* First will do */
+ return kl;
+ break;
+ }
+ /* Error condition! */
+ warnx("Inconsistent LWP state for process %d", ki->p_pid);
+ return kl;
+}
+
+static struct kinfo_proc2 *
+getkinfo_kvm(kvm_t *kdp, int what, int flag, int *nentriesp)
+{
+
+ return (kvm_getproc2(kdp, what, flag, sizeof(struct kinfo_proc2),
+ nentriesp));
+}
+
+static struct pinfo *
+setpinfo(struct kinfo_proc2 *ki, int nentries, int calc_pcpu, int rawcpu)
+{
+ struct pinfo *pi;
+ int i;
+
+ pi = calloc(nentries, sizeof(*pi));
+ if (pi == NULL)
+ err(EXIT_FAILURE, "calloc");
+
+ if (calc_pcpu && !nlistread)
+ donlist();
+
+ for (i = 0; i < nentries; i++) {
+ pi[i].ki = &ki[i];
+ if (!calc_pcpu)
+ continue;
+ if (ki[i].p_swtime == 0 || ki[i].p_realstat == SZOMB) {
+ pi[i].pcpu = 0.0;
+ continue;
+ }
+ pi[i].pcpu = 100.0 * (double)ki[i].p_pctcpu / fscale;
+ if (!rawcpu)
+ pi[i].pcpu /= 1.0 - exp(ki[i].p_swtime * log_ccpu);
+ }
+
+ return pi;
+}
+
+static void
+scanvars(void)
+{
+ struct varent *vent;
+ VAR *v;
+
+ SIMPLEQ_FOREACH(vent, &displaylist, next) {
+ v = vent->var;
+ if (v->flag & COMM) {
+ needcomm = 1;
+ break;
+ }
+ }
+}
+
+static int
+pscomp(const void *a, const void *b)
+{
+ const struct pinfo *pa = (const struct pinfo *)a;
+ const struct pinfo *pb = (const struct pinfo *)b;
+ const struct kinfo_proc2 *ka = pa->ki;
+ const struct kinfo_proc2 *kb = pb->ki;
+
+ int i;
+ int64_t i64;
+ VAR *v;
+ struct varent *ve;
+ const sigset_t *sa, *sb;
+
+#define V_SIZE(k) ((k)->p_vm_msize)
+#define RDIFF_N(t, n) \
+ if (((const t *)((const char *)ka + v->off))[n] > ((const t *)((const char *)kb + v->off))[n]) \
+ return 1; \
+ if (((const t *)((const char *)ka + v->off))[n] < ((const t *)((const char *)kb + v->off))[n]) \
+ return -1;
+
+#define RDIFF(type) RDIFF_N(type, 0); continue
+
+ SIMPLEQ_FOREACH(ve, &sortlist, next) {
+ v = ve->var;
+ if (v->flag & LWP)
+ /* LWP structure not available (yet) */
+ continue;
+ /* Sort on pvar() fields, + a few others */
+ switch (v->type) {
+ case CHAR:
+ RDIFF(char);
+ case UCHAR:
+ RDIFF(u_char);
+ case SHORT:
+ RDIFF(short);
+ case USHORT:
+ RDIFF(ushort);
+ case INT:
+ RDIFF(int);
+ case UINT:
+ RDIFF(uint);
+ case LONG:
+ RDIFF(long);
+ case ULONG:
+ RDIFF(ulong);
+ case INT32:
+ RDIFF(int32_t);
+ case UINT32:
+ RDIFF(uint32_t);
+ case SIGLIST:
+ sa = (const void *)((const char *)ka + v->off);
+ sb = (const void *)((const char *)kb + v->off);
+ i = 0;
+ do {
+ if (sa->__bits[i] > sb->__bits[i])
+ return 1;
+ if (sa->__bits[i] < sb->__bits[i])
+ return -1;
+ i++;
+ } while (i < (int)__arraycount(sa->__bits));
+ continue;
+ case INT64:
+ RDIFF(int64_t);
+ case KPTR:
+ case KPTR24:
+ case UINT64:
+ RDIFF(uint64_t);
+ case TIMEVAL:
+ /* compare xxx_sec then xxx_usec */
+ RDIFF_N(uint32_t, 0);
+ RDIFF_N(uint32_t, 1);
+ continue;
+ case CPUTIME:
+ i64 = ka->p_rtime_sec * 1000000 + ka->p_rtime_usec;
+ i64 -= kb->p_rtime_sec * 1000000 + kb->p_rtime_usec;
+ if (sumrusage) {
+ i64 += ka->p_uctime_sec * 1000000
+ + ka->p_uctime_usec;
+ i64 -= kb->p_uctime_sec * 1000000
+ + kb->p_uctime_usec;
+ }
+ if (i64 != 0)
+ return i64 > 0 ? 1 : -1;
+ continue;
+ case PCPU:
+ i = pb->pcpu - pa->pcpu;
+ if (i != 0)
+ return i;
+ continue;
+ case VSIZE:
+ i = V_SIZE(kb) - V_SIZE(ka);
+ if (i != 0)
+ return i;
+ continue;
+
+ default:
+ /* Ignore everything else */
+ break;
+ }
+ }
+ return 0;
+
+#undef VSIZE
+}
+
+/*
+ * 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 user's
+ * 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(char *s)
+{
+ size_t len;
+ char *newopts, *ns, *cp;
+
+ len = strlen(s);
+ if ((newopts = ns = malloc(len + 3)) == NULL)
+ err(EXIT_FAILURE, NULL);
+ /*
+ * options begin with '-'
+ */
+ if (*s != '-')
+ *ns++ = '-'; /* add option flag */
+ /*
+ * gaze to end of argv[1]
+ */
+ cp = s + len - 1;
+ /*
+ * if the last letter is a 't' flag and there are no other option
+ * characters that take arguments (eg U, p, o) in the option
+ * string and the option string doesn't start with a '-' then
+ * convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
+ */
+ if (*cp == 't' && *s != '-' && strpbrk(s, ARGOPTS) == NULL)
+ *cp = 'T';
+ else {
+ /*
+ * otherwise check for trailing number, which *may* be a
+ * pid.
+ */
+ while (cp >= s && isdigit((unsigned char)*cp))
+ --cp;
+ }
+ cp++;
+ memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */
+ ns += cp - s;
+ /*
+ * if there's a trailing number, and not a preceding 'p' (pid) or
+ * 't' (tty) flag, then assume it's a pid and insert a 'p' flag.
+ */
+ if (isdigit((unsigned char)*cp) &&
+ (cp == s || (cp[-1] != 'U' && cp[-1] != 't' && cp[-1] != 'p' &&
+ cp[-1] != '/' && (cp - 1 == s || cp[-2] != 't'))))
+ *ns++ = 'p';
+ /* and append the number */
+ (void)strcpy(ns, cp); /* XXX strcpy is safe here */
+
+ return (newopts);
+}
+
+static int
+parsenum(const char *str, const char *msg)
+{
+ char *ep;
+ unsigned long ul;
+
+ ul = strtoul(str, &ep, 0);
+
+ if (*str == '\0' || *ep != '\0')
+ errx(EXIT_FAILURE, "Invalid %s: `%s'", msg, str);
+
+ if (ul > INT_MAX)
+ errx(EXIT_FAILURE, "Out of range %s: `%s'", msg, str);
+
+ return (int)ul;
+}
+
+static void
+descendant_sort(struct pinfo *ki, int items)
+{
+ int dst, lvl, maxlvl, n, ndst, nsrc, siblings, src;
+ unsigned char *path;
+ struct pinfo kn;
+
+ /*
+ * First, sort the entries by descendancy, tracking the descendancy
+ * depth in the level field.
+ */
+ src = 0;
+ maxlvl = 0;
+ while (src < items) {
+ if (ki[src].level) {
+ src++;
+ continue;
+ }
+ for (nsrc = 1; src + nsrc < items; nsrc++)
+ if (!ki[src + nsrc].level)
+ break;
+
+ for (dst = 0; dst < items; dst++) {
+ if (ki[dst].ki->p_pid == ki[src].ki->p_pid)
+ continue;
+ if (ki[dst].ki->p_pid == ki[src].ki->p_ppid)
+ break;
+ }
+
+ if (dst == items) {
+ src += nsrc;
+ continue;
+ }
+
+ for (ndst = 1; dst + ndst < items; ndst++)
+ if (ki[dst + ndst].level <= ki[dst].level)
+ break;
+
+ for (n = src; n < src + nsrc; n++) {
+ ki[n].level += ki[dst].level + 1;
+ if (maxlvl < ki[n].level)
+ maxlvl = ki[n].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 prefix (instead of 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].level) == 0) {
+ ki[src].prefix = NULL;
+ continue;
+ }
+ if ((ki[src].prefix = malloc(lvl * 2 + 1)) == NULL)
+ errx(EXIT_FAILURE, "malloc failed");
+ for (n = 0; n < lvl - 2; n++) {
+ ki[src].prefix[n * 2] =
+ path[n / 8] & 1 << (n % 8) ? '|' : ' ';
+ ki[src].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].level > lvl)
+ continue;
+ if (ki[dst].level == lvl)
+ siblings = 1;
+ break;
+ }
+ if (siblings)
+ path[n / 8] |= 1 << (n % 8);
+ else
+ path[n / 8] &= ~(1 << (n % 8));
+ ki[src].prefix[n * 2] = siblings ? '|' : '`';
+ ki[src].prefix[n * 2 + 1] = '-';
+ n++;
+ }
+ strcpy(ki[src].prefix + n * 2, "- ");
+ }
+ free(path);
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr,
+ "usage:\t%s\n\t %s\n\t%s\n",
+ "ps [-AaCcdehjlmrSsTuvwx] [-k key] [-M core] [-N system] [-O fmt]",
+ "[-o fmt] [-p pid] [-t tty] [-U user] [-W swap]",
+ "ps -L");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/bin/ps/ps.h b/bin/ps/ps.h
new file mode 100644
index 0000000..7d8ddb8
--- /dev/null
+++ b/bin/ps/ps.h
@@ -0,0 +1,104 @@
+/* $NetBSD: ps.h,v 1.29 2016/12/02 21:59:03 christos Exp $ */
+
+/*-
+ * 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.
+ * 3. 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
+ */
+
+#include <sys/queue.h>
+
+#define UNLIMITED 0 /* unlimited terminal width */
+
+enum mode {
+ PRINTMODE = 0, /* print values */
+ WIDTHMODE = 1 /* determine width of column */
+};
+
+enum type {
+ UNSPECIFIED,
+ CHAR, UCHAR, SHORT, USHORT, INT, UINT, LONG, ULONG,
+ KPTR, KPTR24, INT32, UINT32, SIGLIST, INT64, UINT64,
+ TIMEVAL, CPUTIME, PCPU, VSIZE
+};
+
+/* Variables. */
+typedef SIMPLEQ_HEAD(varlist, varent) VARLIST;
+
+typedef struct varent {
+ SIMPLEQ_ENTRY(varent) next;
+ struct var *var;
+} VARENT;
+
+struct pinfo {
+ struct kinfo_proc2 *ki;
+ struct kinfo_lwp *li;
+ char *prefix;
+ int level;
+ double pcpu;
+};
+
+typedef struct var {
+ const char *name; /* name(s) of variable */
+ const char *header; /* header, possibly changed from default */
+#define COMM 0x01 /* needs exec arguments and environment (XXX) */
+#define ARGV0 0x02 /* only print argv[0] */
+#define LJUST 0x04 /* left adjust on output (trailing blanks) */
+#define INF127 0x08 /* 127 = infinity: if > 127, print 127. */
+#define LWP 0x10 /* dispatch to kinfo_lwp routine */
+#define UAREA 0x20 /* need to check p_uvalid */
+#define ALIAS 0x40 /* entry is alias for 'header' */
+ u_int flag;
+ /* output routine */
+ void (*oproc)(struct pinfo *pi, struct varent *, enum mode);
+ /*
+ * The following (optional) elements are hooks for passing information
+ * to the generic output routine: pvar (that which prints simple
+ * elements from struct kinfo_proc2).
+ */
+ int off; /* offset in structure */
+ enum type type; /* type of element */
+ const char *fmt; /* printf format */
+
+ /* current longest element */
+ int width; /* printing width */
+ int64_t longestp; /* longest positive signed value */
+ int64_t longestn; /* longest negative signed value */
+ u_int64_t longestu; /* longest unsigned value */
+ double longestpd; /* longest positive double */
+ double longestnd; /* longest negative double */
+} VAR;
+
+
+#define OUTPUT(vent, kl, pi, ki, mode) do { \
+ if ((vent)->var->flag & LWP) \
+ pi->li = kl; \
+ ((vent)->var->oproc)(pi, (vent), (mode)); \
+ } while (/*CONSTCOND*/ 0)
+
+#include "extern.h"