aboutsummaryrefslogtreecommitdiff
path: root/bin/ps
diff options
context:
space:
mode:
Diffstat (limited to 'bin/ps')
-rw-r--r--bin/ps/Makefile13
-rw-r--r--bin/ps/Makefile.depend6
-rw-r--r--bin/ps/extern.h13
-rw-r--r--bin/ps/fmt.c9
-rw-r--r--bin/ps/keyword.c651
-rw-r--r--bin/ps/nlist.c9
-rw-r--r--bin/ps/print.c39
-rw-r--r--bin/ps/ps.1889
-rw-r--r--bin/ps/ps.c407
-rw-r--r--bin/ps/ps.h50
10 files changed, 1233 insertions, 853 deletions
diff --git a/bin/ps/Makefile b/bin/ps/Makefile
index 596aa57fd2df..94a6b07757f0 100644
--- a/bin/ps/Makefile
+++ b/bin/ps/Makefile
@@ -1,17 +1,10 @@
-# $FreeBSD$
-# @(#)Makefile 8.1 (Berkeley) 6/2/93
-
PACKAGE=runtime
PROG= ps
SRCS= fmt.c keyword.c nlist.c print.c ps.c
-#
-# To support "lazy" ps for non root/wheel users
-# add -DLAZY_PS to the cflags. This helps
-# keep ps from being an unnecessary load
-# on large systems.
-#
-CFLAGS+=-DLAZY_PS
LIBADD= m kvm jail xo
+.ifdef PS_CHECK_KEYWORDS
+CFLAGS+=-DPS_CHECK_KEYWORDS
+.endif
.include <bsd.prog.mk>
diff --git a/bin/ps/Makefile.depend b/bin/ps/Makefile.depend
index eefffb37d491..521210d8ba8e 100644
--- a/bin/ps/Makefile.depend
+++ b/bin/ps/Makefile.depend
@@ -1,18 +1,14 @@
-# $FreeBSD$
# Autogenerated - do NOT edit!
DIRDEPS = \
- gnu/lib/csu \
include \
include/xlocale \
lib/${CSU_DIR} \
lib/libc \
lib/libcompiler_rt \
- lib/libelf \
lib/libjail \
lib/libkvm \
- lib/libutil \
- lib/libxo \
+ lib/libxo/libxo \
lib/msun \
diff --git a/bin/ps/extern.h b/bin/ps/extern.h
index ea4362eeadd9..bb7a21bbc8be 100644
--- a/bin/ps/extern.h
+++ b/bin/ps/extern.h
@@ -27,9 +27,6 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
- *
- * @(#)extern.h 8.3 (Berkeley) 4/2/94
- * $FreeBSD$
*/
struct kinfo;
@@ -42,10 +39,12 @@ extern int cflag, eval, fscale, nlistread, rawcpu;
extern unsigned long mempages;
extern time_t now;
extern int showthreads, sumrusage, termwidth;
-extern STAILQ_HEAD(velisthead, varent) varlist;
+extern struct velisthead varlist;
+extern const size_t known_keywords_nb;
__BEGIN_DECLS
char *arguments(KINFO *, VARENT *);
+void check_keywords(void);
char *command(KINFO *, VARENT *);
char *cputime(KINFO *, VARENT *);
char *cpunum(KINFO *, VARENT *);
@@ -53,10 +52,11 @@ int donlist(void);
char *elapsed(KINFO *, VARENT *);
char *elapseds(KINFO *, VARENT *);
char *emulname(KINFO *, VARENT *);
-VARENT *find_varentry(VAR *);
+VARENT *find_varentry(const char *);
const char *fmt_argv(char **, char *, char *, size_t);
double getpcpu(const KINFO *);
char *jailname(KINFO *, VARENT *);
+size_t aliased_keyword_index(const VAR *);
char *kvar(KINFO *, VARENT *);
char *label(KINFO *, VARENT *);
char *loginclass(KINFO *, VARENT *);
@@ -68,7 +68,7 @@ char *lockname(KINFO *, VARENT *);
char *mwchan(KINFO *, VARENT *);
char *nwchan(KINFO *, VARENT *);
char *pagein(KINFO *, VARENT *);
-void parsefmt(const char *, int);
+void parsefmt(const char *, struct velisthead *, int);
char *pcpu(KINFO *, VARENT *);
char *pmem(KINFO *, VARENT *);
char *pri(KINFO *, VARENT *);
@@ -76,6 +76,7 @@ void printheader(void);
char *priorityr(KINFO *, VARENT *);
char *egroupname(KINFO *, VARENT *);
char *rgroupname(KINFO *, VARENT *);
+void resolve_aliases(void);
char *runame(KINFO *, VARENT *);
char *rvar(KINFO *, VARENT *);
void showkey(void);
diff --git a/bin/ps/fmt.c b/bin/ps/fmt.c
index c89ea78d7373..87e2e0f49a34 100644
--- a/bin/ps/fmt.c
+++ b/bin/ps/fmt.c
@@ -29,15 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static char sccsid[] = "@(#)fmt.c 8.4 (Berkeley) 4/15/94";
-#endif
-#endif
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
diff --git a/bin/ps/keyword.c b/bin/ps/keyword.c
index e78cf984e455..f05e5245f695 100644
--- a/bin/ps/keyword.c
+++ b/bin/ps/keyword.c
@@ -3,6 +3,11 @@
*
* Copyright (c) 1990, 1993, 1994
* The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Olivier Certner
+ * <olce@FreeBSD.org> at Kumacom SARL under sponsorship from the FreeBSD
+ * Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -29,14 +34,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static char sccsid[] = "@(#)keyword.c 8.5 (Berkeley) 4/2/94";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/time.h>
#include <sys/resource.h>
@@ -44,16 +41,17 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <sys/user.h>
-#include <err.h>
+#include <assert.h>
+#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+
#include <libxo/xo.h>
#include "ps.h"
-static VAR *findvar(char *, int, char **header);
static int vcmp(const void *, const void *);
/* Compute offset in common structures. */
@@ -66,189 +64,318 @@ static int vcmp(const void *, const void *);
#define PIDFMT "d"
/* PLEASE KEEP THE TABLE BELOW SORTED ALPHABETICALLY!!! */
-static VAR var[] = {
- {"%cpu", "%CPU", NULL, "percent-cpu", 0, pcpu, 0, CHAR, NULL, 0},
- {"%mem", "%MEM", NULL, "percent-memory", 0, pmem, 0, CHAR, NULL, 0},
- {"acflag", "ACFLG", NULL, "accounting-flag", 0, kvar, KOFF(ki_acflag),
- USHORT, "x", 0},
- {"acflg", "", "acflag", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"args", "COMMAND", NULL, "arguments", COMM|LJUST|USER, arguments, 0,
- CHAR, NULL, 0},
- {"blocked", "", "sigmask", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"caught", "", "sigcatch", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"class", "CLASS", NULL, "login-class", LJUST, loginclass, 0, CHAR,
- NULL, 0},
- {"comm", "COMMAND", NULL, "command", LJUST, ucomm, 0, CHAR, NULL, 0},
- {"command", "COMMAND", NULL, "command", COMM|LJUST|USER, command, 0,
- CHAR, NULL, 0},
- {"cow", "COW", NULL, "copy-on-write-faults", 0, kvar, KOFF(ki_cow),
- UINT, "u", 0},
- {"cpu", "C", NULL, "on-cpu", 0, cpunum, 0, CHAR, NULL, 0},
- {"cputime", "", "time", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"dsiz", "DSIZ", NULL, "data-size", 0, kvar, KOFF(ki_dsize), PGTOK,
- "ld", 0},
- {"egid", "", "gid", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"egroup", "", "group", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"emul", "EMUL", NULL, "emulation-envirnment", LJUST, emulname, 0,
- CHAR, NULL, 0},
- {"etime", "ELAPSED", NULL, "elapsed-time", USER, elapsed, 0, CHAR,
- NULL, 0},
- {"etimes", "ELAPSED", NULL, "elapsed-times", USER, elapseds, 0, CHAR,
- NULL, 0},
- {"euid", "", "uid", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"f", "F", NULL, "flags", 0, kvar, KOFF(ki_flag), LONG, "lx", 0},
- {"f2", "F2", NULL, "flags2", 0, kvar, KOFF(ki_flag2), INT, "08x", 0},
- {"fib", "FIB", NULL, "fib", 0, kvar, KOFF(ki_fibnum), INT, "d", 0},
- {"flags", "", "f", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"flags2", "", "f2", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"gid", "GID", NULL, "gid", 0, kvar, KOFF(ki_groups), UINT, UIDFMT, 0},
- {"group", "GROUP", NULL, "group", LJUST, egroupname, 0, CHAR, NULL, 0},
- {"ignored", "", "sigignore", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"inblk", "INBLK", NULL, "read-blocks", USER, rvar, ROFF(ru_inblock),
- LONG, "ld", 0},
- {"inblock", "", "inblk", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"jail", "JAIL", NULL, "jail-name", LJUST, jailname, 0, CHAR, NULL, 0},
- {"jid", "JID", NULL, "jail-id", 0, kvar, KOFF(ki_jid), INT, "d", 0},
- {"jobc", "JOBC", NULL, "job-control-count", 0, kvar, KOFF(ki_jobc),
- SHORT, "d", 0},
- {"ktrace", "KTRACE", NULL, "ktrace", 0, kvar, KOFF(ki_traceflag), INT,
- "x", 0},
- {"label", "LABEL", NULL, "label", LJUST, label, 0, CHAR, NULL, 0},
- {"lim", "LIM", NULL, "memory-limit", 0, maxrss, 0, CHAR, NULL, 0},
- {"lockname", "LOCK", NULL, "lock-name", LJUST, lockname, 0, CHAR, NULL,
- 0},
- {"login", "LOGIN", NULL, "login-name", LJUST, logname, 0, CHAR, NULL,
- 0},
- {"logname", "", "login", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"lstart", "STARTED", NULL, "start-time", LJUST|USER, lstarted, 0,
- CHAR, NULL, 0},
- {"lwp", "LWP", NULL, "thread-id", 0, kvar, KOFF(ki_tid), UINT,
- LWPFMT, 0},
- {"majflt", "MAJFLT", NULL, "major-faults", USER, rvar, ROFF(ru_majflt),
- LONG, "ld", 0},
- {"minflt", "MINFLT", NULL, "minor-faults", USER, rvar, ROFF(ru_minflt),
- LONG, "ld", 0},
- {"msgrcv", "MSGRCV", NULL, "received-messages", USER, rvar,
- ROFF(ru_msgrcv), LONG, "ld", 0},
- {"msgsnd", "MSGSND", NULL, "sent-messages", USER, rvar,
- ROFF(ru_msgsnd), LONG, "ld", 0},
- {"mwchan", "MWCHAN", NULL, "wait-channel", LJUST, mwchan, 0, CHAR,
- NULL, 0},
- {"ni", "", "nice", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"nice", "NI", NULL, "nice", 0, kvar, KOFF(ki_nice), CHAR, "d", 0},
- {"nivcsw", "NIVCSW", NULL, "involuntary-context-switches", USER, rvar,
- ROFF(ru_nivcsw), LONG, "ld", 0},
- {"nlwp", "NLWP", NULL, "threads", 0, kvar, KOFF(ki_numthreads), UINT,
- NLWPFMT, 0},
- {"nsignals", "", "nsigs", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"nsigs", "NSIGS", NULL, "signals-taken", USER, rvar,
- ROFF(ru_nsignals), LONG, "ld", 0},
- {"nswap", "NSWAP", NULL, "swaps", USER, rvar, ROFF(ru_nswap), LONG,
- "ld", 0},
- {"nvcsw", "NVCSW", NULL, "voluntary-context-switches", USER, rvar,
- ROFF(ru_nvcsw), LONG, "ld", 0},
- {"nwchan", "NWCHAN", NULL, "wait-channel-address", LJUST, nwchan, 0,
- CHAR, NULL, 0},
- {"oublk", "OUBLK", NULL, "written-blocks", USER, rvar,
- ROFF(ru_oublock), LONG, "ld", 0},
- {"oublock", "", "oublk", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"paddr", "PADDR", NULL, "process-address", 0, kvar, KOFF(ki_paddr),
- KPTR, "lx", 0},
- {"pagein", "PAGEIN", NULL, "pageins", USER, pagein, 0, CHAR, NULL, 0},
- {"pcpu", "", "%cpu", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"pending", "", "sig", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"pgid", "PGID", NULL, "process-group", 0, kvar, KOFF(ki_pgid), UINT,
- PIDFMT, 0},
- {"pid", "PID", NULL, "pid", 0, kvar, KOFF(ki_pid), UINT, PIDFMT, 0},
- {"pmem", "", "%mem", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"ppid", "PPID", NULL, "ppid", 0, kvar, KOFF(ki_ppid), UINT, PIDFMT, 0},
- {"pri", "PRI", NULL, "priority", 0, pri, 0, CHAR, NULL, 0},
- {"re", "RE", NULL, "residency-time", INF127, kvar, KOFF(ki_swtime),
- UINT, "d", 0},
- {"rgid", "RGID", NULL, "real-gid", 0, kvar, KOFF(ki_rgid), UINT,
- UIDFMT, 0},
- {"rgroup", "RGROUP", NULL, "real-group", LJUST, rgroupname, 0, CHAR,
- NULL, 0},
- {"rss", "RSS", NULL, "rss", 0, kvar, KOFF(ki_rssize), PGTOK, "ld", 0},
- {"rtprio", "RTPRIO", NULL, "realtime-priority", 0, priorityr,
- KOFF(ki_pri), CHAR, NULL, 0},
- {"ruid", "RUID", NULL, "real-uid", 0, kvar, KOFF(ki_ruid), UINT,
- UIDFMT, 0},
- {"ruser", "RUSER", NULL, "real-user", LJUST, runame, 0, CHAR, NULL, 0},
- {"sid", "SID", NULL, "sid", 0, kvar, KOFF(ki_sid), UINT, PIDFMT, 0},
- {"sig", "PENDING", NULL, "signals-pending", 0, kvar, KOFF(ki_siglist),
- INT, "x", 0},
- {"sigcatch", "CAUGHT", NULL, "signals-caught", 0, kvar,
- KOFF(ki_sigcatch), UINT, "x", 0},
- {"sigignore", "IGNORED", NULL, "signals-ignored", 0, kvar,
- KOFF(ki_sigignore), UINT, "x", 0},
- {"sigmask", "BLOCKED", NULL, "signal-mask", 0, kvar, KOFF(ki_sigmask),
- UINT, "x", 0},
- {"sl", "SL", NULL, "sleep-time", INF127, kvar, KOFF(ki_slptime), UINT,
- "d", 0},
- {"ssiz", "SSIZ", NULL, "stack-size", 0, kvar, KOFF(ki_ssize), PGTOK,
- "ld", 0},
- {"start", "STARTED", NULL, "start-time", LJUST|USER, started, 0, CHAR,
- NULL, 0},
- {"stat", "", "state", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"state", "STAT", NULL, "state", LJUST, state, 0, CHAR, NULL, 0},
- {"svgid", "SVGID", NULL, "saved-gid", 0, kvar, KOFF(ki_svgid), UINT,
- UIDFMT, 0},
- {"svuid", "SVUID", NULL, "saved-uid", 0, kvar, KOFF(ki_svuid), UINT,
- UIDFMT, 0},
- {"systime", "SYSTIME", NULL, "system-time", USER, systime, 0, CHAR,
- NULL, 0},
- {"tdaddr", "TDADDR", NULL, "thread-address", 0, kvar, KOFF(ki_tdaddr),
- KPTR, "lx", 0},
- {"tdev", "TDEV", NULL, "terminal-device", 0, tdev, 0, CHAR, NULL, 0},
- {"tdnam", "", "tdname", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"tdname", "TDNAME", NULL, "thread-name", LJUST, tdnam, 0, CHAR,
- NULL, 0},
- {"tid", "", "lwp", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"time", "TIME", NULL, "cpu-time", USER, cputime, 0, CHAR, NULL, 0},
- {"tpgid", "TPGID", NULL, "terminal-process-gid", 0, kvar,
- KOFF(ki_tpgid), UINT, PIDFMT, 0},
- {"tracer", "TRACER", NULL, "tracer", 0, kvar, KOFF(ki_tracer), UINT,
- PIDFMT, 0},
- {"tsid", "TSID", NULL, "terminal-sid", 0, kvar, KOFF(ki_tsid), UINT,
- PIDFMT, 0},
- {"tsiz", "TSIZ", NULL, "text-size", 0, kvar, KOFF(ki_tsize), PGTOK,
- "ld", 0},
- {"tt", "TT ", NULL, "terminal-name", 0, tname, 0, CHAR, NULL, 0},
- {"tty", "TTY", NULL, "tty", LJUST, longtname, 0, CHAR, NULL, 0},
- {"ucomm", "UCOMM", NULL, "accounting-name", LJUST, ucomm, 0, CHAR,
- NULL, 0},
- {"uid", "UID", NULL, "uid", 0, kvar, KOFF(ki_uid), UINT, UIDFMT, 0},
- {"upr", "UPR", NULL, "user-priority", 0, upr, 0, CHAR, NULL, 0},
- {"uprocp", "UPROCP", NULL, "process-address", 0, kvar, KOFF(ki_paddr),
- KPTR, "lx", 0},
- {"user", "USER", NULL, "user", LJUST, username, 0, CHAR, NULL, 0},
- {"usertime", "USERTIME", NULL, "user-time", USER, usertime, 0, CHAR,
- NULL, 0},
- {"usrpri", "", "upr", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"vmaddr", "VMADDR", NULL, "vmspace-address", 0, kvar, KOFF(ki_vmspace),
- KPTR, "lx", 0},
- {"vsize", "", "vsz", NULL, 0, NULL, 0, CHAR, NULL, 0},
- {"vsz", "VSZ", NULL, "virtual-size", 0, vsize, 0, CHAR, NULL, 0},
- {"wchan", "WCHAN", NULL, "wait-channel", LJUST, wchan, 0, CHAR, NULL,
- 0},
- {"xstat", "XSTAT", NULL, "exit-status", 0, kvar, KOFF(ki_xstat),
- USHORT, "x", 0},
- {"", NULL, NULL, NULL, 0, NULL, 0, CHAR, NULL, 0},
+static VAR keywords[] = {
+ {"%cpu", {NULL}, "%CPU", "percent-cpu", 0, pcpu, 0, UNSPEC, NULL},
+ {"%mem", {NULL}, "%MEM", "percent-memory", 0, pmem, 0, UNSPEC, NULL},
+ {"acflag", {NULL}, "ACFLG", "accounting-flag", 0, kvar, KOFF(ki_acflag),
+ USHORT, "x"},
+ {"acflg", {"acflag"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"args", {NULL}, "COMMAND", "arguments", COMM|LJUST|USER, arguments, 0,
+ UNSPEC, NULL},
+ {"blocked", {"sigmask"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"caught", {"sigcatch"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"class", {NULL}, "CLASS", "login-class", LJUST, loginclass, 0,
+ UNSPEC, NULL},
+ {"comm", {NULL}, "COMMAND", "command", LJUST, ucomm, 0, UNSPEC, NULL},
+ {"command", {NULL}, "COMMAND", "command", COMM|LJUST|USER, command, 0,
+ UNSPEC, NULL},
+ {"cow", {NULL}, "COW", "copy-on-write-faults", 0, kvar, KOFF(ki_cow),
+ UINT, "u"},
+ {"cpu", {NULL}, "C", "on-cpu", 0, cpunum, 0, UNSPEC, NULL},
+ {"cputime", {"time"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"dsiz", {NULL}, "DSIZ", "data-size", 0, kvar, KOFF(ki_dsize),
+ PGTOK, "ld"},
+ {"egid", {"gid"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"egroup", {"group"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"emul", {NULL}, "EMUL", "emulation-envirnment", LJUST, emulname, 0,
+ UNSPEC, NULL},
+ {"etime", {NULL}, "ELAPSED", "elapsed-time", USER, elapsed, 0,
+ UNSPEC, NULL},
+ {"etimes", {NULL}, "ELAPSED", "elapsed-times", USER, elapseds, 0,
+ UNSPEC, NULL},
+ {"euid", {"uid"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"f", {NULL}, "F", "flags", 0, kvar, KOFF(ki_flag), LONG, "lx"},
+ {"f2", {NULL}, "F2", "flags2", 0, kvar, KOFF(ki_flag2), INT, "08x"},
+ {"fib", {NULL}, "FIB", "fib", 0, kvar, KOFF(ki_fibnum), INT, "d"},
+ {"flags", {"f"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"flags2", {"f2"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"gid", {NULL}, "GID", "gid", 0, kvar, KOFF(ki_groups), UINT, UIDFMT},
+ {"group", {NULL}, "GROUP", "group", LJUST, egroupname, 0, UNSPEC, NULL},
+ {"ignored", {"sigignore"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"inblk", {NULL}, "INBLK", "read-blocks", USER, rvar, ROFF(ru_inblock),
+ LONG, "ld"},
+ {"inblock", {"inblk"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"jail", {NULL}, "JAIL", "jail-name", LJUST, jailname, 0, UNSPEC, NULL},
+ {"jid", {NULL}, "JID", "jail-id", 0, kvar, KOFF(ki_jid), INT, "d"},
+ {"jobc", {NULL}, "JOBC", "job-control-count", 0, kvar, KOFF(ki_jobc),
+ SHORT, "d"},
+ {"ktrace", {NULL}, "KTRACE", "ktrace", 0, kvar, KOFF(ki_traceflag),
+ INT, "x"},
+ {"label", {NULL}, "LABEL", "label", LJUST, label, 0, UNSPEC, NULL},
+ {"lim", {NULL}, "LIM", "memory-limit", 0, maxrss, 0, UNSPEC, NULL},
+ {"lockname", {NULL}, "LOCK", "lock-name", LJUST, lockname, 0,
+ UNSPEC, NULL},
+ {"login", {NULL}, "LOGIN", "login-name", LJUST, logname, 0,
+ UNSPEC, NULL},
+ {"logname", {"login"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"lstart", {NULL}, "STARTED", "start-time", LJUST|USER, lstarted, 0,
+ UNSPEC, NULL},
+ {"lwp", {NULL}, "LWP", "thread-id", 0, kvar, KOFF(ki_tid),
+ UINT, LWPFMT},
+ {"majflt", {NULL}, "MAJFLT", "major-faults", USER, rvar, ROFF(ru_majflt),
+ LONG, "ld"},
+ {"minflt", {NULL}, "MINFLT", "minor-faults", USER, rvar, ROFF(ru_minflt),
+ LONG, "ld"},
+ {"msgrcv", {NULL}, "MSGRCV", "received-messages", USER, rvar,
+ ROFF(ru_msgrcv), LONG, "ld"},
+ {"msgsnd", {NULL}, "MSGSND", "sent-messages", USER, rvar,
+ ROFF(ru_msgsnd), LONG, "ld"},
+ {"mwchan", {NULL}, "MWCHAN", "wait-channel", LJUST, mwchan, 0,
+ UNSPEC, NULL},
+ {"ni", {"nice"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"nice", {NULL}, "NI", "nice", 0, kvar, KOFF(ki_nice), CHAR, "d"},
+ {"nivcsw", {NULL}, "NIVCSW", "involuntary-context-switches", USER, rvar,
+ ROFF(ru_nivcsw), LONG, "ld"},
+ {"nlwp", {NULL}, "NLWP", "threads", 0, kvar, KOFF(ki_numthreads),
+ UINT, NLWPFMT},
+ {"nsignals", {"nsigs"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"nsigs", {NULL}, "NSIGS", "signals-taken", USER, rvar,
+ ROFF(ru_nsignals), LONG, "ld"},
+ {"nswap", {NULL}, "NSWAP", "swaps", USER, rvar, ROFF(ru_nswap),
+ LONG, "ld"},
+ {"nvcsw", {NULL}, "NVCSW", "voluntary-context-switches", USER, rvar,
+ ROFF(ru_nvcsw), LONG, "ld"},
+ {"nwchan", {NULL}, "NWCHAN", "wait-channel-address", LJUST, nwchan, 0,
+ UNSPEC, NULL},
+ {"oublk", {NULL}, "OUBLK", "written-blocks", USER, rvar,
+ ROFF(ru_oublock), LONG, "ld"},
+ {"oublock", {"oublk"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"paddr", {NULL}, "PADDR", "process-address", 0, kvar, KOFF(ki_paddr),
+ KPTR, "lx"},
+ {"pagein", {NULL}, "PAGEIN", "pageins", USER, pagein, 0, UNSPEC, NULL},
+ {"pcpu", {"%cpu"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"pending", {"sig"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"pgid", {NULL}, "PGID", "process-group", 0, kvar, KOFF(ki_pgid),
+ UINT, PIDFMT},
+ {"pid", {NULL}, "PID", "pid", 0, kvar, KOFF(ki_pid), UINT, PIDFMT},
+ {"pmem", {"%mem"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"ppid", {NULL}, "PPID", "ppid", 0, kvar, KOFF(ki_ppid), UINT, PIDFMT},
+ {"pri", {NULL}, "PRI", "priority", 0, pri, 0, UNSPEC, NULL},
+ {"re", {NULL}, "RE", "residency-time", INF127, kvar, KOFF(ki_swtime),
+ UINT, "d"},
+ {"rgid", {NULL}, "RGID", "real-gid", 0, kvar, KOFF(ki_rgid),
+ UINT, UIDFMT},
+ {"rgroup", {NULL}, "RGROUP", "real-group", LJUST, rgroupname, 0,
+ UNSPEC, NULL},
+ {"rss", {NULL}, "RSS", "rss", 0, kvar, KOFF(ki_rssize), PGTOK, "ld"},
+ {"rtprio", {NULL}, "RTPRIO", "realtime-priority", 0, priorityr,
+ KOFF(ki_pri), UNSPEC, NULL},
+ {"ruid", {NULL}, "RUID", "real-uid", 0, kvar, KOFF(ki_ruid),
+ UINT, UIDFMT},
+ {"ruser", {NULL}, "RUSER", "real-user", LJUST, runame, 0, UNSPEC, NULL},
+ {"sid", {NULL}, "SID", "sid", 0, kvar, KOFF(ki_sid), UINT, PIDFMT},
+ {"sig", {NULL}, "PENDING", "signals-pending", 0, kvar, KOFF(ki_siglist),
+ INT, "x"},
+ {"sigcatch", {NULL}, "CAUGHT", "signals-caught", 0, kvar,
+ KOFF(ki_sigcatch), UINT, "x"},
+ {"sigignore", {NULL}, "IGNORED", "signals-ignored", 0, kvar,
+ KOFF(ki_sigignore), UINT, "x"},
+ {"sigmask", {NULL}, "BLOCKED", "signal-mask", 0, kvar, KOFF(ki_sigmask),
+ UINT, "x"},
+ {"sl", {NULL}, "SL", "sleep-time", INF127, kvar, KOFF(ki_slptime),
+ UINT, "d"},
+ {"ssiz", {NULL}, "SSIZ", "stack-size", 0, kvar, KOFF(ki_ssize),
+ PGTOK, "ld"},
+ {"start", {NULL}, "STARTED", "start-time", LJUST|USER, started, 0,
+ UNSPEC, NULL},
+ {"stat", {"state"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"state", {NULL}, "STAT", "state", LJUST, state, 0, UNSPEC, NULL},
+ {"svgid", {NULL}, "SVGID", "saved-gid", 0, kvar, KOFF(ki_svgid),
+ UINT, UIDFMT},
+ {"svuid", {NULL}, "SVUID", "saved-uid", 0, kvar, KOFF(ki_svuid),
+ UINT, UIDFMT},
+ {"systime", {NULL}, "SYSTIME", "system-time", USER, systime, 0,
+ UNSPEC, NULL},
+ {"tdaddr", {NULL}, "TDADDR", "thread-address", 0, kvar, KOFF(ki_tdaddr),
+ KPTR, "lx"},
+ {"tdev", {NULL}, "TDEV", "terminal-device", 0, tdev, 0, UNSPEC, NULL},
+ {"tdnam", {"tdname"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"tdname", {NULL}, "TDNAME", "thread-name", LJUST, tdnam, 0,
+ UNSPEC, NULL},
+ {"tid", {"lwp"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"time", {NULL}, "TIME", "cpu-time", USER, cputime, 0, UNSPEC, NULL},
+ {"tpgid", {NULL}, "TPGID", "terminal-process-gid", 0, kvar,
+ KOFF(ki_tpgid), UINT, PIDFMT},
+ {"tracer", {NULL}, "TRACER", "tracer", 0, kvar, KOFF(ki_tracer),
+ UINT, PIDFMT},
+ {"tsid", {NULL}, "TSID", "terminal-sid", 0, kvar, KOFF(ki_tsid),
+ UINT, PIDFMT},
+ {"tsiz", {NULL}, "TSIZ", "text-size", 0, kvar, KOFF(ki_tsize),
+ PGTOK, "ld"},
+ {"tt", {NULL}, "TT ", "terminal-name", 0, tname, 0, UNSPEC, NULL},
+ {"tty", {NULL}, "TTY", "tty", LJUST, longtname, 0, UNSPEC, NULL},
+ {"ucomm", {NULL}, "UCOMM", "accounting-name", LJUST, ucomm, 0,
+ UNSPEC, NULL},
+ {"uid", {NULL}, "UID", "uid", 0, kvar, KOFF(ki_uid), UINT, UIDFMT},
+ {"upr", {NULL}, "UPR", "user-priority", 0, upr, 0, UNSPEC, NULL},
+ {"uprocp", {NULL}, "UPROCP", "process-address", 0, kvar, KOFF(ki_paddr),
+ KPTR, "lx"},
+ {"user", {NULL}, "USER", "user", LJUST, username, 0, UNSPEC, NULL},
+ {"usertime", {NULL}, "USERTIME", "user-time", USER, usertime, 0,
+ UNSPEC, NULL},
+ {"usrpri", {"upr"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"vmaddr", {NULL}, "VMADDR", "vmspace-address", 0, kvar,
+ KOFF(ki_vmspace), KPTR, "lx"},
+ {"vsize", {"vsz"}, NULL, NULL, 0, NULL, 0, UNSPEC, NULL},
+ {"vsz", {NULL}, "VSZ", "virtual-size", 0, vsize, 0, UNSPEC, NULL},
+ {"wchan", {NULL}, "WCHAN", "wait-channel", LJUST, wchan, 0,
+ UNSPEC, NULL},
+ {"xstat", {NULL}, "XSTAT", "exit-status", 0, kvar, KOFF(ki_xstat),
+ USHORT, "x"},
};
+const size_t known_keywords_nb = nitems(keywords);
+
+size_t
+aliased_keyword_index(const VAR *const v)
+{
+ const VAR *const fv = (v->flag & RESOLVED_ALIAS) == 0 ?
+ v : v->final_kw;
+ const size_t idx = fv - keywords;
+
+ assert(idx < known_keywords_nb);
+ return (idx);
+}
+
+/*
+ * Sanity checks on declared keywords.
+ *
+ * Checks specific to aliases are done in resolve_alias() instead.
+ *
+ * Currently, only checks that keywords are alphabetically ordered by their
+ * names. More checks could be added, such as the absence of type (UNSPEC),
+ * 'fmt' (NULL) when the output routine is not kval()/rval().
+ *
+ * Called from main() on PS_CHECK_KEYWORDS, else available when debugging.
+ */
+void
+check_keywords(void)
+{
+ const VAR *k, *next_k;
+ bool order_violated = false;
+
+ k = &keywords[0];
+ for (size_t i = 1; i < known_keywords_nb; ++i) {
+ next_k = &keywords[i];
+ if (vcmp(k, next_k) >= 0) {
+ xo_warnx("keywords bad order: '%s' followed by '%s'",
+ k->name, next_k->name);
+ order_violated = true;
+ }
+ k = next_k;
+ }
+ if (order_violated)
+ /* Must be the case as we rely on bsearch() + vcmp(). */
+ xo_errx(2, "keywords are not in ascending order "
+ "(internal error)");
+}
+
+static void
+alias_errx(const char *const name, const char *const what)
+{
+ xo_errx(2, "alias keyword '%s' specifies %s (internal error)",
+ name, what);
+}
+
+static void
+merge_alias(VAR *const k, VAR *const tgt)
+{
+ if ((tgt->flag & RESOLVED_ALIAS) != 0)
+ k->final_kw = tgt->final_kw;
+ else {
+ k->final_kw = tgt;
+ assert(tgt->aliased == NULL);
+ }
+
+#define MERGE_IF_SENTINEL(field, sentinel) do { \
+ if (k->field == sentinel) \
+ k->field = tgt->field; \
+} while (0)
+
+ MERGE_IF_SENTINEL(header, NULL);
+ MERGE_IF_SENTINEL(field, NULL);
+ /* If NOINHERIT is present, no merge occurs. */
+ MERGE_IF_SENTINEL(flag, 0);
+
+#undef MERGE_IF_SENTINEL
+
+ /* We also check that aliases don't specify things they should not. */
+#define MERGE_CHECK_SENTINEL(field, sentinel, field_descr) do { \
+ if (k->field != sentinel) \
+ alias_errx(k->name, field_descr); \
+ k->field = tgt->field; \
+} while (0);
+
+ MERGE_CHECK_SENTINEL(oproc, NULL, "an output routine");
+ MERGE_CHECK_SENTINEL(off, 0, "a structure offset");
+ MERGE_CHECK_SENTINEL(type, UNSPEC, "a different type than UNSPEC");
+ MERGE_CHECK_SENTINEL(fmt, NULL, "a printf format");
+
+#undef MERGE_CHECK_SENTINEL
+}
+
+static void
+resolve_alias(VAR *const k)
+{
+ VAR *t, key;
+
+ if ((k->flag & RESOLVED_ALIAS) != 0 || k->aliased == NULL)
+ return;
+
+ if ((k->flag & RESOLVING_ALIAS) != 0)
+ xo_errx(2, "cycle when resolving alias keyword '%s'", k->name);
+ k->flag |= RESOLVING_ALIAS;
+
+ key.name = k->aliased;
+ t = bsearch(&key, keywords, known_keywords_nb, sizeof(VAR), vcmp);
+ if (t == NULL)
+ xo_errx(2, "unknown target '%s' for keyword alias '%s'",
+ k->aliased, k->name);
+
+ resolve_alias(t);
+ merge_alias(k, t);
+
+ k->flag &= ~RESOLVING_ALIAS;
+ k->flag |= RESOLVED_ALIAS;
+}
+
+/*
+ * Resolve all aliases immediately.
+ *
+ * Called from main() on PS_CHECK_KEYWORDS, else available when debugging.
+ */
+void
+resolve_aliases(void)
+{
+ for (size_t i = 0; i < known_keywords_nb; ++i)
+ resolve_alias(&keywords[i]);
+}
+
void
showkey(void)
{
- VAR *v;
+ const VAR *v;
+ const VAR *const end = keywords + known_keywords_nb;
+ const char *sep;
int i;
- const char *p, *sep;
i = 0;
sep = "";
xo_open_list("key");
- for (v = var; *(p = v->name); ++v) {
- int len = strlen(p);
+ for (v = keywords; v < end; ++v) {
+ const char *const p = v->name;
+ const int len = strlen(p);
+
if (termwidth && (i += len + 1) > termwidth) {
i = len;
sep = "\n";
@@ -258,115 +385,95 @@ showkey(void)
}
xo_emit("\n");
xo_close_list("key");
- xo_finish();
+ if (xo_finish() < 0)
+ xo_err(1, "stdout");
}
void
-parsefmt(const char *p, int user)
+parsefmt(const char *p, struct velisthead *const var_list,
+ const int user)
{
- char *tempstr, *tempstr1;
+ char *copy, *cp;
+ char *hdr_p, sep;
+ size_t sep_idx;
+ VAR *v, key;
+ struct varent *vent;
-#define FMTSEP " \t,\n"
- tempstr1 = tempstr = strdup(p);
- while (tempstr && *tempstr) {
- char *cp, *hp;
- VAR *v;
- struct varent *vent;
+ cp = copy = strdup(p);
+ if (copy == NULL)
+ xo_err(1, "strdup");
+
+ sep = cp[0]; /* We only care if it's 0 or not here. */
+ sep_idx = -1;
+ while (sep != '\0') {
+ cp += sep_idx + 1;
/*
* If an item contains an equals sign, it specifies a column
* header, may contain embedded separator characters and
- * is always the last item.
+ * is always the last item.
*/
- if (tempstr[strcspn(tempstr, "="FMTSEP)] != '=')
- while ((cp = strsep(&tempstr, FMTSEP)) != NULL &&
- *cp == '\0')
- /* void */;
- else {
- cp = tempstr;
- tempstr = NULL;
- }
- if (cp == NULL || !(v = findvar(cp, user, &hp)))
- continue;
- if (!user) {
+ sep_idx = strcspn(cp, "= \t,\n");
+ sep = cp[sep_idx];
+ cp[sep_idx] = 0;
+ if (sep == '=') {
+ hdr_p = cp + sep_idx + 1;
+ sep = '\0'; /* No more keywords. */
+ } else
+ hdr_p = NULL;
+
+ /* At this point, '*cp' is '\0' iff 'sep_idx' is 0. */
+ if (*cp == '\0') {
/*
- * If the user is NOT adding this field manually,
- * get on with our lives if this VAR is already
- * represented in the list.
+ * Empty keyword. Skip it, and silently unless some
+ * header has been specified.
*/
- vent = find_varentry(v);
- if (vent != NULL)
- continue;
- }
- if ((vent = malloc(sizeof(struct varent))) == NULL)
- errx(1, "malloc failed");
- vent->header = v->header;
- if (hp) {
- hp = strdup(hp);
- if (hp)
- vent->header = hp;
+ if (hdr_p != NULL)
+ xo_warnx("empty keyword with header '%s'",
+ hdr_p);
+ continue;
}
- vent->var = malloc(sizeof(*vent->var));
- if (vent->var == NULL)
- errx(1, "malloc failed");
- memcpy(vent->var, v, sizeof(*vent->var));
- STAILQ_INSERT_TAIL(&varlist, vent, next_ve);
- }
- free(tempstr1);
- if (STAILQ_EMPTY(&varlist)) {
- warnx("no valid keywords; valid keywords:");
- showkey();
- exit(1);
- }
-}
-
-static VAR *
-findvar(char *p, int user, char **header)
-{
- size_t rflen;
- VAR *v, key;
- char *hp, *realfmt;
-
- hp = strchr(p, '=');
- if (hp)
- *hp++ = '\0';
- key.name = p;
- v = bsearch(&key, var, sizeof(var)/sizeof(VAR) - 1, sizeof(VAR), vcmp);
+ /* Find the keyword. */
+ key.name = cp;
+ v = bsearch(&key, keywords,
+ known_keywords_nb, sizeof(VAR), vcmp);
+ if (v == NULL) {
+ xo_warnx("%s: keyword not found", cp);
+ eval = 1;
+ continue;
+ }
- if (v && v->alias) {
+#ifndef PS_CHECK_KEYWORDS
/*
- * If the user specified an alternate-header for this
- * (aliased) format-name, then we need to copy that
- * alternate-header when making the recursive call to
- * process the alias.
+ * On PS_CHECK_KEYWORDS, this is not necessary as all aliases
+ * are resolved at startup in main() by calling
+ * resolve_aliases().
*/
- if (hp == NULL)
- parsefmt(v->alias, user);
- else {
- /*
- * XXX - This processing will not be correct for
- * any alias which expands into a list of format
- * keywords. Presently there are no aliases
- * which do that.
- */
- rflen = strlen(v->alias) + strlen(hp) + 2;
- realfmt = malloc(rflen);
- if (realfmt == NULL)
- errx(1, "malloc failed");
- snprintf(realfmt, rflen, "%s=%s", v->alias, hp);
- parsefmt(realfmt, user);
- free(realfmt);
+ resolve_alias(v);
+#endif
+
+ if ((vent = malloc(sizeof(struct varent))) == NULL)
+ xo_errx(1, "malloc failed");
+ vent->header = v->header;
+ if (hdr_p) {
+ hdr_p = strdup(hdr_p);
+ if (hdr_p)
+ vent->header = hdr_p;
}
- return ((VAR *)NULL);
+ vent->width = strlen(vent->header);
+ vent->var = v;
+ vent->flags = user ? VE_KEEP : 0;
+ STAILQ_INSERT_TAIL(var_list, vent, next_ve);
}
- if (!v) {
- warnx("%s: keyword not found", p);
- eval = 1;
+
+ free(copy);
+
+ if (STAILQ_EMPTY(var_list)) {
+ xo_warnx("no valid keywords; valid keywords:");
+ showkey();
+ exit(1);
}
- if (header)
- *header = hp;
- return (v);
}
static int
diff --git a/bin/ps/nlist.c b/bin/ps/nlist.c
index 5ef56c9dfabc..8c6a4814e4d5 100644
--- a/bin/ps/nlist.c
+++ b/bin/ps/nlist.c
@@ -29,15 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static char sccsid[] = "@(#)nlist.c 8.4 (Berkeley) 4/2/94";
-#endif /* not lint */
-#endif
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/types.h>
#include <sys/sysctl.h>
diff --git a/bin/ps/print.c b/bin/ps/print.c
index d4dbd9624011..f59e843effc1 100644
--- a/bin/ps/print.c
+++ b/bin/ps/print.c
@@ -29,15 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static char sccsid[] = "@(#)print.c 8.6 (Berkeley) 4/16/94";
-#endif /* not lint */
-#endif
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/time.h>
#include <sys/resource.h>
@@ -49,7 +40,6 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <sys/vmmeter.h>
-#include <err.h>
#include <grp.h>
#include <jail.h>
#include <langinfo.h>
@@ -76,7 +66,7 @@ __FBSDID("$FreeBSD$");
void
printheader(void)
{
- VAR *v;
+ const VAR *v;
struct varent *vent;
STAILQ_FOREACH(vent, &varlist, next_ve)
@@ -91,9 +81,9 @@ printheader(void)
if (STAILQ_NEXT(vent, next_ve) == NULL) /* last one */
xo_emit("{T:/%hs}", vent->header);
else
- xo_emit("{T:/%-*hs}", v->width, vent->header);
+ xo_emit("{T:/%-*hs}", vent->width, vent->header);
} else
- xo_emit("{T:/%*hs}", v->width, vent->header);
+ xo_emit("{T:/%*hs}", vent->width, vent->header);
if (STAILQ_NEXT(vent, next_ve) != NULL)
xo_emit("{P: }");
}
@@ -263,8 +253,6 @@ state(KINFO *k, VARENT *ve __unused)
*cp = '?';
}
cp++;
- if (!(flag & P_INMEM))
- *cp++ = 'W';
if (k->ki_p->ki_nice < NZERO || k->ki_p->ki_pri.pri_class == PRI_REALTIME)
*cp++ = '<';
else if (k->ki_p->ki_nice > NZERO || k->ki_p->ki_pri.pri_class == PRI_IDLE)
@@ -277,7 +265,7 @@ state(KINFO *k, VARENT *ve __unused)
*cp++ = 'V';
if ((flag & P_SYSTEM) || k->ki_p->ki_lock > 0)
*cp++ = 'L';
- if ((k->ki_p->ki_cr_flags & CRED_FLAG_CAPMODE) != 0)
+ if ((k->ki_p->ki_cr_flags & KI_CRF_CAPABILITY_MODE) != 0)
*cp++ = 'C';
if (k->ki_p->ki_kiflag & KI_SLEADER)
*cp++ = 's';
@@ -289,7 +277,7 @@ state(KINFO *k, VARENT *ve __unused)
return (buf);
}
-#define scalepri(x) ((x) - PZERO)
+#define scalepri(x) ((x) - PUSER)
char *
pri(KINFO *k, VARENT *ve __unused)
@@ -643,7 +631,7 @@ getpcpu(const KINFO *k)
#define fxtofl(fixpt) ((double)(fixpt) / fscale)
/* XXX - I don't like this */
- if (k->ki_p->ki_swtime == 0 || (k->ki_p->ki_flag & P_INMEM) == 0)
+ if (k->ki_p->ki_swtime == 0)
return (0.0);
if (rawcpu)
return (100.0 * fxtofl(k->ki_p->ki_pctcpu));
@@ -671,8 +659,6 @@ getpmem(KINFO *k)
if (failure)
return (0.0);
- if ((k->ki_p->ki_flag & P_INMEM) == 0)
- return (0.0);
/* XXX want pmap ptpages, segtab, etc. (per architecture) */
/* XXX don't have info about shared */
fracmem = ((double)k->ki_p->ki_rssize) / mempages;
@@ -748,7 +734,7 @@ priorityr(KINFO *k, VARENT *ve __unused)
* structures.
*/
static char *
-printval(void *bp, VAR *v)
+printval(void *bp, const VAR *v)
{
static char ofmt[32] = "%";
const char *fcp;
@@ -761,6 +747,10 @@ printval(void *bp, VAR *v)
#define CHKINF127(n) (((n) > 127) && (v->flag & INF127) ? 127 : (n))
switch (v->type) {
+ case UNSPEC:
+ xo_errx(1, "cannot print value of unspecified type "
+ "(internal error)");
+ break;
case CHAR:
(void)asprintf(&str, ofmt, *(char *)bp);
break;
@@ -791,6 +781,9 @@ printval(void *bp, VAR *v)
case PGTOK:
(void)asprintf(&str, ofmt, ps_pgtok(*(u_long *)bp));
break;
+ default:
+ xo_errx(1, "unknown type (internal error)");
+ break;
}
return (str);
@@ -799,7 +792,7 @@ printval(void *bp, VAR *v)
char *
kvar(KINFO *k, VARENT *ve)
{
- VAR *v;
+ const VAR *v;
v = ve->var;
return (printval((char *)((char *)k->ki_p + v->off), v));
@@ -808,7 +801,7 @@ kvar(KINFO *k, VARENT *ve)
char *
rvar(KINFO *k, VARENT *ve)
{
- VAR *v;
+ const VAR *v;
v = ve->var;
if (!k->ki_valid)
diff --git a/bin/ps/ps.1 b/bin/ps/ps.1
index 25373adb7d73..542004453658 100644
--- a/bin/ps/ps.1
+++ b/bin/ps/ps.1
@@ -1,6 +1,13 @@
.\"-
+.\" SPDX-License-Identifier: BSD-3-Clause
+.\"
.\" Copyright (c) 1980, 1990, 1991, 1993, 1994
.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 2025 The FreeBSD Foundation
+.\"
+.\" Portions of this documentation were written by Olivier Certner
+.\" <olce@FreeBSD.org> at Kumacom SARL under sponsorship from the FreeBSD
+ \" Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
@@ -26,10 +33,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" @(#)ps.1 8.3 (Berkeley) 4/18/94
-.\" $FreeBSD$
-.\"
-.Dd June 27, 2020
+.Dd July 16, 2025
.Dt PS 1
.Os
.Sh NAME
@@ -38,8 +42,10 @@
.Sh SYNOPSIS
.Nm
.Op Fl -libxo
-.Op Fl aCcdefHhjlmrSTuvwXxZ
-.Op Fl O Ar fmt | Fl o Ar fmt
+.Op Fl AaCcdefHhjlmrSTuvwXxZ
+.Op Fl O Ar fmt
+.Op Fl o Ar fmt
+.Op Fl D Ar up | down | both
.Op Fl G Ar gid Ns Op , Ns Ar gid Ns Ar ...
.Op Fl J Ar jid Ns Op , Ns Ar jid Ns Ar ...
.Op Fl M Ar core
@@ -49,40 +55,106 @@
.Op Fl U Ar user Ns Op , Ns Ar user Ns Ar ...
.Nm
.Op Fl -libxo
-.Op Fl L
+.Fl L
.Sh DESCRIPTION
The
.Nm
-utility
-displays a header line, followed by lines containing information about
-all of your
-processes that have controlling terminals.
-If the
+utility displays information about a selection of processes.
+Its traditional text style output consists of a header line followed by one line
+of information per selected process, or possibly multiple ones if using
+.Fl H
+.Pq one per lightweight-process .
+Other output styles can be requested via
+.Fl -libxo .
+.Pp
+By default, only the processes of the calling user, determined by matching their
+effective user ID with that of the
+.Nm
+process, that have controlling terminals are shown.
+A different set of processes can be selected for display by using combinations
+of the
+.Fl A , a , D , G , J , p , T , t , U , X ,
+and
.Fl x
-options is specified,
+options.
+Except for options
+.Fl X
+and
+.Fl x ,
+as soon as one of them appears, it inhibits the default process selection, i.e.,
+the calling user's processes are shown only on request.
+If more than one of these
+.Pq with same exceptions
+appear,
.Nm
-will also display processes that do not have controlling terminals.
+will select processes as soon as they are matched by at least one of them
+.Pq inclusive OR .
+The
+.Fl X
+option can be independently used to further filter the listed processes to only
+those that have a controlling terminal
+.Po
+except for those selected by
+.Fl p
+.Pc .
+Its opposite,
+.Fl x ,
+forcefully removes that filter.
+If none of
+.Fl X
+and
+.Fl x
+is specified, the implied default behavior is that of
+.Fl X
+unless using another option whose description explicitly says that
+.Fl x
+is implied.
.Pp
-A different set of processes can be selected for display by using any
-combination of the
-.Fl a , G , J , p , T , t ,
+For each selected process, the default displayed information consists of the
+process' ID, controlling terminal, state, CPU time
+.Pq including both user and system time
+and associated command
+.Po
+see the documentation for the
+.Cm command
+keyword below
+.Pc .
+This information can be tweaked using two groups of options which can be
+combined as needed.
+First, options
+.Fl o
+and
+.Fl O
+add columns with data corresponding to the explicitly passed keywords.
+Available keywords are documented in the
+.Sx KEYWORDS
+section below.
+They can be listed using option
+.Fl L .
+Second, options
+.Fl j , l , u ,
and
-.Fl U
+.Fl v
+designate specific predefined groups of columns, also called canned displays.
+Appearance of any of these options inhibits the default display, replacing it
+all with the requested columns, and in the order options are passed.
+The individual columns requested via a canned display option that have the same
+keyword or an alias to that of some column added by an earlier canned display
+option, or by an explicit
+.Fl O
+or
+.Fl o
+option anywhere on the command line, are suppressed.
+This automatic removal of duplicate data in canned displays is useful for
+slightly tweaking these displays and/or combining multiple ones without having
+to rebuild variants from scratch, e.g., using only
+.Fl o
options.
-If more than one of these options are given, then
-.Nm
-will select all processes which are matched by at least one of the
-given options.
.Pp
-For the processes which have been selected for display,
-.Nm
-will usually display one line per process.
-The
+Output information lines are by default sorted first by controlling terminal,
+then by process ID, and then, if
.Fl H
-option may result in multiple output lines (one line per thread) for
-some processes.
-By default all of these output lines are sorted first by controlling
-terminal, then by process ID.
+has been specified, by lightweight-process (thread) ID.
The
.Fl m , r , u ,
and
@@ -91,23 +163,29 @@ options will change the sort order.
If more than one sorting option was given, then the selected processes
will be sorted by the last sorting option which was specified.
.Pp
-For the processes which have been selected for display, the information
-to display is selected based on a set of keywords (see the
-.Fl L , O ,
-and
-.Fl o
-options).
-The default output format includes, for each process, the process' ID,
-controlling terminal, state, CPU time (including both user and system time)
-and associated command.
-.Pp
-If the
+If the traditional text output (the default) is used, the default output width is that requested by the
+.Ev COLUMNS
+environment variable if present, else the line width of the terminal associated
+to the
.Nm
-process is associated with a terminal, the default output width is that of the
-terminal; otherwise the output width is unlimited.
+process, if any.
+In all other situations, the output width is unlimited.
See also the
.Fl w
+option and the
+.Sx BUGS
+section.
+.Pp
+For backwards compatibility,
+.Nm
+attempts to interpret any positional argument as a process ID, as if specified
+by the
+.Fl p
option.
+Failure to do so will trigger an error.
+.Nm
+also accepts the old-style BSD options, whose format and effect are left
+undocumented on purpose.
.Pp
The options are as follows:
.Bl -tag -width indent
@@ -116,18 +194,30 @@ Generate output via
.Xr libxo 3
in a selection of different human and machine readable formats.
See
-.Xr xo_parse_args 3
+.Xr xo_options 7
for details on command line arguments.
+The default is the traditional text style output.
+.It Fl A
+Display information about all processes in the system.
+Using this option is strictly equivalent to specifying both
+.Fl a
+and
+.Fl x .
+Please see their description for more information.
.It Fl a
-Display information about other users' processes as well as your own.
+Display information about all users' processes.
+It does not, however, list all processes
+.Po
+see
+.Fl A
+and
+.Fl x
+.Pc .
If the
.Va security.bsd.see_other_uids
-sysctl is set to zero, this option is honored only if the UID of the user is 0.
-.It Fl c
-Change the
-.Dq command
-column output to just contain the executable name,
-rather than the full command line.
+sysctl is set to zero, this option is honored only if the real user ID of the
+.Nm
+process is 0.
.It Fl C
Change the way the CPU percentage is calculated by using a
.Dq raw
@@ -135,6 +225,23 @@ CPU calculation that ignores
.Dq resident
time (this normally has
no effect).
+.It Fl c
+Change the
+.Dq command
+column output to just contain the executable name,
+rather than the full command line.
+.It Fl D
+Expand the list of selected processes based on the process tree.
+.Dq UP
+will add the ancestor processes,
+.Dq DOWN
+will add the descendant processes, and
+.Dq BOTH
+will add both the ancestor and the descendant processes.
+.Fl D
+does not imply
+.Fl d ,
+but works well with it.
.It Fl d
Arrange processes into descendancy order and prefix each command with
indentation text showing sibling and parent/child relationships as a tree.
@@ -144,27 +251,34 @@ and
.Fl r
options are also used, they control how sibling processes are sorted
relative to each other.
-Note that this option has no effect if the
-.Dq command
-column is not the last column displayed.
+Note that this option has no effect if the last column does not have
+.Cm comm ,
+.Cm command
+or
+.Cm ucomm
+as its keyword.
.It Fl e
Display the environment as well.
.It Fl f
-Show command-line and environment information about swapped out processes.
-This option is honored only if the UID of the user is 0.
+Indicates to print the full command and arguments in
+.Cm command
+columns.
+This is the default behavior on
+.Fx .
+See
+.Fl c
+to turn it off.
.It Fl G
-Display information about processes which are running with the specified
-real group IDs.
+Display information about processes whose real group ID matches the specified
+group IDs or names.
+Implies
+.Fl x
+by default.
.It Fl H
Show all of the threads associated with each process.
.It Fl h
Repeat the information header as often as necessary to guarantee one
header per page of information.
-.It Fl j
-Print information associated with the following keywords:
-.Cm user , pid , ppid , pgid , sid , jobc , state , tt , time ,
-and
-.Cm command .
.It Fl J
Display information about processes which match the specified jail IDs.
This may be either the
@@ -175,10 +289,15 @@ of the jail.
Use
.Fl J
.Sy 0
-to display only host processes.
-This flag implies
+to request display of host processes.
+Implies
.Fl x
by default.
+.It Fl j
+Print information associated with the following keywords:
+.Cm user , pid , ppid , pgid , sid , jobc , state , tt , time ,
+and
+.Cm command .
.It Fl L
List the set of keywords available for the
.Fl O
@@ -201,21 +320,26 @@ terminal and process ID.
Extract the name list from the specified system instead of the default,
which is the kernel image the system has booted from.
.It Fl O
-Add the information associated with the space or comma separated list
-of keywords specified, after the process ID,
-in the default information
-display.
-Keywords may be appended with an equals
+Save passed columns in a separate list that in the end is grafted just after the
+display's first occurence of the process ID column as specified by other
+options, or the default display if there is none.
+If the display prepared by other options does not include a process ID column,
+the list is inserted at start of the display.
+Further occurences of
+.Fl O
+append to the to-be-grafted list of columns.
+This option takes a space- or comma-separated list of keywords.
+The last keyword in the list may be appended with an equals sign
.Pq Ql =
-sign and a string.
-This causes the printed header to use the specified string instead of
-the standard header.
+as explained for option
+.Fl o
+and with the same effect.
.It Fl o
-Display information associated with the space or comma separated
-list of keywords specified.
-The last keyword in the list may be appended with an equals
+Display information associated with the space- or comma-separated list of
+keywords specified.
+The last keyword in the list may be appended with an equals sign
.Pq Ql =
-sign and a string that spans the rest of the argument, and can contain
+and a string that spans the rest of the argument, and can contain
space and comma characters.
This causes the printed header to use the specified string instead of
the standard header.
@@ -226,6 +350,8 @@ So the header texts for multiple keywords can be changed.
If all keywords have empty header texts, no header line is written.
.It Fl p
Display information about processes which match the specified process IDs.
+Processes selected by this option are not subject to being filtered by
+.Fl X .
.It Fl r
Sort by current CPU usage, instead of the combination of controlling
terminal and process ID.
@@ -241,8 +367,15 @@ devices.
Full pathnames, as well as abbreviations (see explanation of the
.Cm tt
keyword) can be specified.
+Implies
+.Fl x
+by default.
.It Fl U
-Display the processes belonging to the specified usernames.
+Display information about processes whose real user ID matches the specified
+user IDs or names.
+Implies
+.Fl x
+by default.
.It Fl u
Display information associated with the following keywords:
.Cm user , pid , %cpu , %mem , vsz , rss , tt , state , start , time ,
@@ -265,35 +398,40 @@ option implies the
.Fl m
option.
.It Fl w
-Use at least 132 columns to display information, instead of the default which
-is the window size if
-.Nm
-is associated with a terminal.
-If the
+Use at least 131 columns to display information.
+If
.Fl w
-option is specified more than once,
+is specified more than once,
.Nm
-will use as many columns as necessary without regard for the window size.
-Note that this option has no effect if the
-.Dq command
-column is not the last column displayed.
+will use as many columns as necessary.
+Please see the preamble of this manual page for how the output width is
+initially determined.
+In particular, if the initial output width is unlimited, specifying
+.Fl w
+has no effect.
+Please also consult the
+.Sx BUGS
+section.
.It Fl X
-When displaying processes matched by other options, skip any processes
-which do not have a controlling terminal.
-This is the default behaviour.
+When displaying processes selected by other options, skip any processes which do
+not have a controlling terminal, except for those selected through
+.Fl p .
+This is the default behaviour, unless using another option whose description
+explicitly says that
+.Fl x
+is implied.
.It Fl x
-When displaying processes matched by other options, include processes
-which do not have a controlling terminal.
-This is the opposite of the
-.Fl X
-option.
+When displaying processes selected by other options, include processes which do
+not have a controlling terminal.
+This option has the opposite behavior to that of
+.Fl X .
If both
.Fl X
and
.Fl x
-are specified in the same command, then
+are specified,
.Nm
-will use the one which was specified last.
+will obey the last occurence.
.It Fl Z
Add
.Xr mac 4
@@ -301,228 +439,10 @@ label to the list of keywords for which
.Nm
will display information.
.El
-.Pp
-A complete list of the available keywords are listed below.
-Some of these keywords are further specified as follows:
-.Bl -tag -width lockname
-.It Cm %cpu
-The CPU utilization of the process; this is a decaying average over up to
-a minute of previous (real) time.
-Since the time base over which this is computed varies (since processes may
-be very young) it is possible for the sum of all
-.Cm %cpu
-fields to exceed 100%.
-.It Cm %mem
-The percentage of real memory used by this process.
-.It Cm class
-Login class associated with the process.
-.It Cm flags
-The flags associated with the process as in
-the include file
-.In sys/proc.h :
-.Bl -column P_SINGLE_BOUNDARY 0x40000000
-.It Dv "P_ADVLOCK" Ta No "0x00001" Ta "Process may hold a POSIX advisory lock"
-.It Dv "P_CONTROLT" Ta No "0x00002" Ta "Has a controlling terminal"
-.It Dv "P_KPROC" Ta No "0x00004" Ta "Kernel process"
-.It Dv "P_PPWAIT" Ta No "0x00010" Ta "Parent is waiting for child to exec/exit"
-.It Dv "P_PROFIL" Ta No "0x00020" Ta "Has started profiling"
-.It Dv "P_STOPPROF" Ta No "0x00040" Ta "Has thread in requesting to stop prof"
-.It Dv "P_HADTHREADS" Ta No "0x00080" Ta "Has had threads (no cleanup shortcuts)"
-.It Dv "P_SUGID" Ta No "0x00100" Ta "Had set id privileges since last exec"
-.It Dv "P_SYSTEM" Ta No "0x00200" Ta "System proc: no sigs, stats or swapping"
-.It Dv "P_SINGLE_EXIT" Ta No "0x00400" Ta "Threads suspending should exit, not wait"
-.It Dv "P_TRACED" Ta No "0x00800" Ta "Debugged process being traced"
-.It Dv "P_WAITED" Ta No "0x01000" Ta "Someone is waiting for us"
-.It Dv "P_WEXIT" Ta No "0x02000" Ta "Working on exiting"
-.It Dv "P_EXEC" Ta No "0x04000" Ta "Process called exec"
-.It Dv "P_WKILLED" Ta No "0x08000" Ta "Killed, shall go to kernel/user boundary ASAP"
-.It Dv "P_CONTINUED" Ta No "0x10000" Ta "Proc has continued from a stopped state"
-.It Dv "P_STOPPED_SIG" Ta No "0x20000" Ta "Stopped due to SIGSTOP/SIGTSTP"
-.It Dv "P_STOPPED_TRACE" Ta No "0x40000" Ta "Stopped because of tracing"
-.It Dv "P_STOPPED_SINGLE" Ta No "0x80000" Ta "Only one thread can continue"
-.It Dv "P_PROTECTED" Ta No "0x100000" Ta "Do not kill on memory overcommit"
-.It Dv "P_SIGEVENT" Ta No "0x200000" Ta "Process pending signals changed"
-.It Dv "P_SINGLE_BOUNDARY" Ta No "0x400000" Ta "Threads should suspend at user boundary"
-.It Dv "P_HWPMC" Ta No "0x800000" Ta "Process is using HWPMCs"
-.It Dv "P_JAILED" Ta No "0x1000000" Ta "Process is in jail"
-.It Dv "P_TOTAL_STOP" Ta No "0x2000000" Ta "Stopped for system suspend"
-.It Dv "P_INEXEC" Ta No "0x4000000" Ta Process is in Xr execve 2
-.It Dv "P_STATCHILD" Ta No "0x8000000" Ta "Child process stopped or exited"
-.It Dv "P_INMEM" Ta No "0x10000000" Ta "Loaded into memory"
-.It Dv "P_SWAPPINGOUT" Ta No "0x20000000" Ta "Process is being swapped out"
-.It Dv "P_SWAPPINGIN" Ta No "0x40000000" Ta "Process is being swapped in"
-.It Dv "P_PPTRACE" Ta No "0x80000000" Ta "Vforked child issued ptrace(PT_TRACEME)"
-.El
-.It Cm flags2
-The flags kept in
-.Va p_flag2
-associated with the process as in
-the include file
-.In sys/proc.h :
-.Bl -column P2_INHERIT_PROTECTED 0x00000001
-.It Dv "P2_INHERIT_PROTECTED" Ta No "0x00000001" Ta "New children get P_PROTECTED"
-.It Dv "P2_NOTRACE" Ta No "0x00000002" Ta "No" Xr ptrace 2 attach or coredumps
-.It Dv "P2_NOTRACE_EXEC" Ta No "0x00000004" Ta Keep P2_NOPTRACE on Xr execve 2
-.It Dv "P2_AST_SU" Ta No "0x00000008" Ta "Handles SU ast for kthreads"
-.It Dv "P2_PTRACE_FSTP" Ta No "0x00000010" Ta "SIGSTOP from PT_ATTACH not yet handled"
-.El
-.It Cm label
-The MAC label of the process.
-.It Cm lim
-The soft limit on memory used, specified via a call to
-.Xr setrlimit 2 .
-.It Cm lstart
-The exact time the command started, using the
-.Ql %c
-format described in
-.Xr strftime 3 .
-.It Cm lockname
-The name of the lock that the process is currently blocked on.
-If the name is invalid or unknown, then
-.Dq ???\&
-is displayed.
-.It Cm logname
-The login name associated with the session the process is in (see
-.Xr getlogin 2 ) .
-.It Cm mwchan
-The event name if the process is blocked normally, or the lock name if
-the process is blocked on a lock.
-See the wchan and lockname keywords
-for details.
-.It Cm nice
-The process scheduling increment (see
-.Xr setpriority 2 ) .
-.It Cm rss
-the real memory (resident set) size of the process (in 1024 byte units).
-.It Cm start
-The time the command started.
-If the command started less than 24 hours ago, the start time is
-displayed using the
-.Dq Li %H:%M
-format described in
-.Xr strftime 3 .
-If the command started less than 7 days ago, the start time is
-displayed using the
-.Dq Li %a%H
-format.
-Otherwise, the start time is displayed using the
-.Dq Li %e%b%y
-format.
-.It Cm state
-The state is given by a sequence of characters, for example,
-.Dq Li RWNA .
-The first character indicates the run state of the process:
-.Pp
-.Bl -tag -width indent -compact
-.It Li D
-Marks a process in disk (or other short term, uninterruptible) wait.
-.It Li I
-Marks a process that is idle (sleeping for longer than about 20 seconds).
-.It Li L
-Marks a process that is waiting to acquire a lock.
-.It Li R
-Marks a runnable process.
-.It Li S
-Marks a process that is sleeping for less than about 20 seconds.
-.It Li T
-Marks a stopped process.
-.It Li W
-Marks an idle interrupt thread.
-.It Li Z
-Marks a dead process (a
-.Dq zombie ) .
-.El
-.Pp
-Additional characters after these, if any, indicate additional state
-information:
-.Pp
-.Bl -tag -width indent -compact
-.It Li +
-The process is in the foreground process group of its control terminal.
-.It Li <
-The process has raised CPU scheduling priority.
-.It Li C
-The process is in
-.Xr capsicum 4
-capability mode.
-.It Li E
-The process is trying to exit.
-.It Li J
-Marks a process which is in
-.Xr jail 2 .
-The hostname of the prison can be found in
-.Pa /proc/ Ns Ao Ar pid Ac Ns Pa /status .
-.It Li L
-The process has pages locked in core (for example, for raw I/O).
-.It Li N
-The process has reduced CPU scheduling priority (see
-.Xr setpriority 2 ) .
-.It Li s
-The process is a session leader.
-.It Li V
-The process' parent is suspended during a
-.Xr vfork 2 ,
-waiting for the process to exec or exit.
-.It Li W
-The process is swapped out.
-.It Li X
-The process is being traced or debugged.
-.El
-.It Cm tt
-An abbreviation for the pathname of the controlling terminal, if any.
-The abbreviation consists of the three letters following
-.Pa /dev/tty ,
-or, for pseudo-terminals, the corresponding entry in
-.Pa /dev/pts .
-This is followed by a
-.Ql -
-if the process can no longer reach that
-controlling terminal (i.e., it has been revoked).
-A
-.Ql -
-without a preceding two letter abbreviation or pseudo-terminal device number
-indicates a process which never had a controlling terminal.
-The full pathname of the controlling terminal is available via the
-.Cm tty
-keyword.
-.It Cm wchan
-The event (an address in the system) on which a process waits.
-When printed numerically, the initial part of the address is
-trimmed off and the result is printed in hex, for example, 0x80324000 prints
-as 324000.
-.El
-.Pp
-When printing using the command keyword, a process that has exited and
-has a parent that has not yet waited for the process (in other words, a zombie)
-is listed as
-.Dq Li <defunct> ,
-and a process which is blocked while trying
-to exit is listed as
-.Dq Li <exiting> .
-If the arguments cannot be located (usually because it has not been set, as is
-the case of system processes and/or kernel threads) the command name is printed
-within square brackets.
-The
-.Nm
-utility first tries to obtain the arguments cached by the kernel (if they were
-shorter than the value of the
-.Va kern.ps_arg_cache_limit
-sysctl).
-The process can change the arguments shown with
-.Xr setproctitle 3 .
-Otherwise,
-.Nm
-makes an educated guess as to the file name and arguments given when the
-process was created by examining memory or the swap area.
-The method is inherently somewhat unreliable and in any event a process
-is entitled to destroy this information.
-The ucomm (accounting) keyword can, however, be depended on.
-If the arguments are unavailable or do not agree with the ucomm keyword,
-the value for the ucomm keyword is appended to the arguments in parentheses.
.Sh KEYWORDS
-The following is a complete list of the available keywords and their
-meanings.
+The following is a complete list of the available keywords and their meanings.
Several of them have aliases (keywords which are synonyms).
+Detailed descriptions for some of them can be found after this list.
.Pp
.Bl -tag -width ".Cm sigignore" -compact
.It Cm %cpu
@@ -548,7 +468,7 @@ number of copy-on-write faults
The processor number on which the process is executing (visible only on SMP
systems).
.It Cm dsiz
-data size (in Kbytes)
+data size in KiB
.It Cm emul
system-call emulation environment (ABI)
.It Cm etime
@@ -647,7 +567,7 @@ real group ID
.It Cm rgroup
group name (from rgid)
.It Cm rss
-resident set size
+resident set size in KiB
.It Cm rtprio
realtime priority (see
.Xr rtprio 1)
@@ -672,7 +592,7 @@ blocked signals (alias
.It Cm sl
sleep time (in seconds; 127 = infinity)
.It Cm ssiz
-stack size (in Kbytes)
+stack size in KiB
.It Cm start
time started
.It Cm state
@@ -698,17 +618,17 @@ control terminal process group ID
.It Cm tracer
tracer process ID
.\".It Cm trss
-.\"text resident set size (in Kbytes)
+.\"text resident set size in KiB
.It Cm tsid
control terminal session ID
.It Cm tsiz
-text size (in Kbytes)
+text size in KiB
.It Cm tt
control terminal name (two letter abbreviation)
.It Cm tty
full name of control terminal
.It Cm ucomm
-name to be used for accounting
+process name used for accounting
.It Cm uid
effective user ID (alias
.Cm euid )
@@ -724,7 +644,7 @@ accumulated user CPU time
.It Cm vmaddr
vmspace pointer
.It Cm vsz
-virtual size in Kbytes (alias
+virtual size in KiB (alias
.Cm vsize )
.It Cm wchan
wait channel (as a symbolic name)
@@ -732,21 +652,258 @@ wait channel (as a symbolic name)
exit or stop status (valid only for stopped or zombie process)
.El
.Pp
-Note that the
-.Cm pending
-column displays bitmask of signals pending in the process queue when
+Some of these keywords are further specified as follows:
+.Bl -tag -width lockname
+.It Cm %cpu
+The CPU utilization of the process; this is a decaying average over up to
+a minute of previous (real) time.
+Since the time base over which this is computed varies (since processes may
+be very young) it is possible for the sum of all
+.Cm %cpu
+fields to exceed 100%.
+.It Cm %mem
+The percentage of real memory used by this process.
+.It Cm class
+Login class associated with the process.
+.It Cm command
+The printed command and arguments are determined as follows.
+A process that has exited and has a parent that has not yet waited for the
+process (in other words, a zombie) is listed as
+.Dq Li <defunct>.
+If the arguments cannot be located
+.Po
+usually because they have not been set, as is the case for system processes
+and/or kernel threads
+.Pc ,
+the command name is printed within square brackets.
+The
+.Nm
+utility first tries to obtain the arguments cached by the kernel
+.Po
+if they were shorter than the value of the
+.Va kern.ps_arg_cache_limit
+sysctl
+.Pc .
+The process can change the arguments shown with
+.Xr setproctitle 3 .
+Otherwise,
+.Nm
+makes an educated guess as to the file name and arguments given when the
+process was created by examining memory or the swap area.
+The method is inherently somewhat unreliable and in any event a process
+is entitled to destroy this information.
+The
+.Cm ucomm
+keyword
+.Pq accounting
+can, however, be depended on.
+If the arguments are unavailable or do not agree with the
+.Cm ucomm
+keyword, the value for the
+.Cm ucomm
+keyword is appended to the arguments in parentheses.
+.It Cm flags
+The flags associated with the process as in
+the include file
+.In sys/proc.h :
+.Bl -column P_SINGLE_BOUNDARY 0x40000000
+.It Dv "P_ADVLOCK" Ta No "0x00000001" Ta "Process may hold a POSIX advisory lock"
+.It Dv "P_CONTROLT" Ta No "0x00000002" Ta "Has a controlling terminal"
+.It Dv "P_KPROC" Ta No "0x00000004" Ta "Kernel process"
+.It Dv "P_PPWAIT" Ta No "0x00000010" Ta "Parent is waiting for child to exec/exit"
+.It Dv "P_PROFIL" Ta No "0x00000020" Ta "Has started profiling"
+.It Dv "P_STOPPROF" Ta No "0x00000040" Ta "Has thread in requesting to stop prof"
+.It Dv "P_HADTHREADS" Ta No "0x00000080" Ta "Has had threads (no cleanup shortcuts)"
+.It Dv "P_SUGID" Ta No "0x00000100" Ta "Had set id privileges since last exec"
+.It Dv "P_SYSTEM" Ta No "0x00000200" Ta "System proc: no sigs, stats or swapping"
+.It Dv "P_SINGLE_EXIT" Ta No "0x00000400" Ta "Threads suspending should exit, not wait"
+.It Dv "P_TRACED" Ta No "0x00000800" Ta "Debugged process being traced"
+.It Dv "P_WAITED" Ta No "0x00001000" Ta "Someone is waiting for us"
+.It Dv "P_WEXIT" Ta No "0x00002000" Ta "Working on exiting"
+.It Dv "P_EXEC" Ta No "0x00004000" Ta "Process called exec"
+.It Dv "P_WKILLED" Ta No "0x00008000" Ta "Killed, shall go to kernel/user boundary ASAP"
+.It Dv "P_CONTINUED" Ta No "0x00010000" Ta "Proc has continued from a stopped state"
+.It Dv "P_STOPPED_SIG" Ta No "0x00020000" Ta "Stopped due to SIGSTOP/SIGTSTP"
+.It Dv "P_STOPPED_TRACE" Ta No "0x00040000" Ta "Stopped because of tracing"
+.It Dv "P_STOPPED_SINGLE" Ta No "0x00080000" Ta "Only one thread can continue"
+.It Dv "P_PROTECTED" Ta No "0x00100000" Ta "Do not kill on memory overcommit"
+.It Dv "P_SIGEVENT" Ta No "0x00200000" Ta "Process pending signals changed"
+.It Dv "P_SINGLE_BOUNDARY" Ta No "0x00400000" Ta "Threads should suspend at user boundary"
+.It Dv "P_HWPMC" Ta No "0x00800000" Ta "Process is using HWPMCs"
+.It Dv "P_JAILED" Ta No "0x01000000" Ta "Process is in jail"
+.It Dv "P_TOTAL_STOP" Ta No "0x02000000" Ta "Stopped for system suspend"
+.It Dv "P_INEXEC" Ta No "0x04000000" Ta Process is in Xr execve 2
+.It Dv "P_STATCHILD" Ta No "0x08000000" Ta "Child process stopped or exited"
+.It Dv "P_INMEM" Ta No "0x10000000" Ta "Always set, unused"
+.It Dv "P_PPTRACE" Ta No "0x80000000" Ta "Vforked child issued ptrace(PT_TRACEME)"
+.El
+.It Cm flags2
+The flags kept in
+.Va p_flag2
+associated with the process as in
+the include file
+.In sys/proc.h :
+.Bl -column P2_INHERIT_PROTECTED 0x00000001
+.It Dv "P2_INHERIT_PROTECTED" Ta No "0x00000001" Ta "New children get P_PROTECTED"
+.It Dv "P2_NOTRACE" Ta No "0x00000002" Ta "No" Xr ptrace 2 attach or coredumps
+.It Dv "P2_NOTRACE_EXEC" Ta No "0x00000004" Ta Keep P2_NOPTRACE on Xr execve 2
+.It Dv "P2_AST_SU" Ta No "0x00000008" Ta "Handles SU ast for kthreads"
+.It Dv "P2_PTRACE_FSTP" Ta No "0x00000010" Ta "SIGSTOP from PT_ATTACH not yet handled"
+.It Dv "P2_TRAPCAP" Ta No "0x00000020" Ta "SIGTRAP on ENOTCAPABLE"
+.It Dv "P2_ASLR_ENABLE" Ta No "0x00000040" Ta "Force enable ASLR"
+.It Dv "P2_ASLR_DISABLE" Ta No "0x00000080" Ta "Force disable ASLR"
+.It Dv "P2_ASLR_IGNSTART" Ta No "0x00000100" Ta "Enable ASLR to consume sbrk area"
+.It Dv "P2_PROTMAX_ENABLE" Ta No "0x00000200" Ta "Force enable implied PROT_MAX"
+.It Dv "P2_PROTMAX_DISABLE" Ta No "0x00000400" Ta "Force disable implied PROT_MAX"
+.It Dv "P2_STKGAP_DISABLE" Ta No "0x00000800" Ta "Disable stack gap for MAP_STACK"
+.It Dv "P2_STKGAP_DISABLE_EXEC" Ta No "0x00001000" Ta "Stack gap disabled after exec"
+.It Dv "P2_ITSTOPPED" Ta No "0x00002000" Ta "itimers stopped (as part of process stop)"
+.It Dv "P2_PTRACEREQ" Ta No "0x00004000" Ta "Active ptrace req"
+.It Dv "P2_NO_NEW_PRIVS" Ta No "0x00008000" Ta "Ignore setuid on exec"
+.It Dv "P2_WXORX_DISABLE" Ta No "0x00010000" Ta "WX mappings enabled"
+.It Dv "P2_WXORX_ENABLE_EXEC" Ta No "0x00020000" Ta "WxorX enabled after exec"
+.It Dv "P2_WEXIT" Ta No "0x00040000" Ta "Internal exit early state"
+.It Dv "P2_REAPKILLED" Ta No "0x00080000" Ta "REAP_KILL pass handled the process"
+.It Dv "P2_MEMBAR_PRIVE" Ta No "0x00100000" Ta "membarrier private expedited registered"
+.It Dv "P2_MEMBAR_PRIVE_SYNCORE" Ta No "0x00200000" Ta "membarrier private expedited sync core registered"
+.It Dv "P2_MEMBAR_GLOBE" Ta No "0x00400000" Ta "membar global expedited registered"
+.El
+.It Cm label
+The MAC label of the process.
+.It Cm lim
+The soft limit on memory used, specified via a call to
+.Xr setrlimit 2 .
+.It Cm lstart
+The exact time the command started, using the
+.Ql %c
+format described in
+.Xr strftime 3 .
+.It Cm lockname
+The name of the lock that the process is currently blocked on.
+If the name is invalid or unknown, then
+.Dq ???\&
+is displayed.
+.It Cm logname
+The login name associated with the session the process is in (see
+.Xr getlogin 2 ) .
+.It Cm mwchan
+The event name if the process is blocked normally, or the lock name if
+the process is blocked on a lock.
+See the wchan and lockname keywords
+for details.
+.It Cm nice
+The process scheduling increment (see
+.Xr setpriority 2 ) .
+.It Cm rss
+the real memory (resident set) size of the process in KiB.
+.It Cm start
+The time the command started.
+If the command started less than 24 hours ago, the start time is
+displayed using the
+.Dq Li %H:%M
+format described in
+.Xr strftime 3 .
+If the command started less than 7 days ago, the start time is
+displayed using the
+.Dq Li %a%H
+format.
+Otherwise, the start time is displayed using the
+.Dq Li %e%b%y
+format.
+.It Cm sig
+The bitmask of signals pending in the process queue if the
.Fl H
-option is not specified, otherwise the per-thread queue of pending signals
-is shown.
+option has not been specified, else the per-thread queue of pending signals.
+.It Cm state
+The state is given by a sequence of characters, for example,
+.Dq Li RWNA .
+The first character indicates the run state of the process:
+.Pp
+.Bl -tag -width indent -compact
+.It Li D
+Marks a process in disk (or other short term, uninterruptible) wait.
+.It Li I
+Marks a process that is idle (sleeping for longer than about 20 seconds).
+.It Li L
+Marks a process that is waiting to acquire a lock.
+.It Li R
+Marks a runnable process.
+.It Li S
+Marks a process that is sleeping for less than about 20 seconds.
+.It Li T
+Marks a stopped process.
+.It Li W
+Marks an idle interrupt thread.
+.It Li Z
+Marks a dead process (a
+.Dq zombie ) .
+.El
+.Pp
+Additional characters after these, if any, indicate additional state
+information:
+.Pp
+.Bl -tag -width indent -compact
+.It Li +
+The process is in the foreground process group of its control terminal.
+.It Li <
+The process has raised CPU scheduling priority.
+.It Li C
+The process is in
+.Xr capsicum 4
+capability mode.
+.It Li E
+The process is trying to exit.
+.It Li J
+Marks a process which is in
+.Xr jail 2 .
+The hostname of the prison can be found in
+.Pa /proc/ Ns Ao Ar pid Ac Ns Pa /status .
+.It Li L
+The process has pages locked in core (for example, for raw I/O).
+.It Li N
+The process has reduced CPU scheduling priority (see
+.Xr setpriority 2 ) .
+.It Li s
+The process is a session leader.
+.It Li V
+The process' parent is suspended during a
+.Xr vfork 2 ,
+waiting for the process to exec or exit.
+.It Li X
+The process is being traced or debugged.
+.El
+.It Cm tt
+An abbreviation for the pathname of the controlling terminal, if any.
+The abbreviation consists of the three letters following
+.Pa /dev/tty ,
+or, for pseudo-terminals, the corresponding entry in
+.Pa /dev/pts .
+This is followed by a
+.Ql -
+if the process can no longer reach that
+controlling terminal (i.e., it has been revoked).
+A
+.Ql -
+without a preceding two letter abbreviation or pseudo-terminal device number
+indicates a process which never had a controlling terminal.
+The full pathname of the controlling terminal is available via the
+.Cm tty
+keyword.
+.It Cm wchan
+The event (an address in the system) on which a process waits.
+When printed numerically, the initial part of the address is
+trimmed off and the result is printed in hex, for example, 0x80324000 prints
+as 324000.
+.El
.Sh ENVIRONMENT
The following environment variables affect the execution of
.Nm :
.Bl -tag -width ".Ev COLUMNS"
.It Ev COLUMNS
If set, specifies the user's preferred output width in column positions.
-By default,
-.Nm
-attempts to automatically determine the terminal width.
+Only affects the traditional text style output.
+Please see the preamble of this manual page on how the final output width is
+determined.
.El
.Sh FILES
.Bl -tag -width ".Pa /boot/kernel/kernel" -compact
@@ -768,9 +925,9 @@ Display information on all system processes:
.Xr kvm 3 ,
.Xr libxo 3 ,
.Xr strftime 3 ,
-.Xr xo_parse_args 3 ,
+.Xr xo_options 7 ,
.Xr mac 4 ,
-.Xr procfs 5 ,
+.Xr procfs 4 ,
.Xr pstat 8 ,
.Xr sysctl 8 ,
.Xr mutex 9
@@ -780,10 +937,45 @@ For historical reasons, the
utility under
.Fx
supports a different set of options from what is described by
-.St -p1003.2 ,
+.St -p1003.1-2024
and what is supported on
.No non- Ns Bx
operating systems.
+.Pp
+In particular, and contrary to this implementation, POSIX specifies that option
+.Fl d
+should serve to select all processes except session leaders, option
+.Fl e
+to select all processes
+.Po
+equivalently to
+.Fl A
+.Pc ,
+and option
+.Fl u
+to select processes by effective user ID.
+.Pp
+However, options
+.Fl A , a , G , l , o , p , U ,
+and
+.Fl t
+behave as prescribed by
+.St -p1003.1-2024 .
+Options
+.Fl f
+and
+.Fl w
+currently do not, but may be changed to in the future.
+.Pp
+POSIX's option
+.Fl g ,
+to select processes having the specified processes as their session leader, is
+not implemented.
+However, other UNIX systems that provide this functionality do so via option
+.Fl s
+instead, reserving
+.Fl g
+to query by group leaders.
.Sh HISTORY
The
.Nm
@@ -796,6 +988,31 @@ Since
cannot run faster than the system and is run as any other scheduled
process, the information it displays can never be exact.
.Pp
+.Nm ps
+currently does not correctly limit the ouput width, and in most cases does not
+limit it at all when it should.
+Regardless of the target width, requested columns are always all printed and
+with widths allowing to entirely print their longest values, except for columns
+with keyword
+.Cm command
+or
+.Cm args
+that are not last in the display
+.Pq they are truncated to 16 bytes ,
+and for the last column in the display if its keyword requests textual
+information of variable length, such as the
+.Cm command , jail ,
+and
+.Cm user
+keywords do.
+This considerably limits the effects and usefulness of the terminal width on the
+output, and consequently that of the
+.Ev COLUMNS
+environment variable and the
+.Fl w
+option
+.Pq if specified only once .
+.Pp
The
.Nm
utility does not correctly display argument lists containing multibyte
diff --git a/bin/ps/ps.c b/bin/ps/ps.c
index 89d90b78f2b0..bb5102729957 100644
--- a/bin/ps/ps.c
+++ b/bin/ps/ps.c
@@ -3,6 +3,11 @@
*
* Copyright (c) 1990, 1993, 1994
* The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Olivier Certner
+ * <olce@FreeBSD.org> at Kumacom SARL under sponsorship from the FreeBSD
+ * Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -36,21 +41,6 @@
* ------+---------+---------+-------- + --------+---------+---------+---------*
*/
-#ifndef lint
-static const char copyright[] =
-"@(#) Copyright (c) 1990, 1993, 1994\n\
- The Regents of the University of California. All rights reserved.\n";
-#endif /* not lint */
-
-#if 0
-#ifndef lint
-static char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94";
-#endif /* not lint */
-#endif
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/jail.h>
#include <sys/proc.h>
@@ -61,7 +51,6 @@ __FBSDID("$FreeBSD$");
#include <sys/mount.h>
#include <ctype.h>
-#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
@@ -84,14 +73,6 @@ __FBSDID("$FreeBSD$");
#define W_SEP " \t" /* "Whitespace" list separators */
#define T_SEP "," /* "Terminate-element" list separators */
-#ifdef LAZY_PS
-#define DEF_UREAD 0
-#define OPT_LAZY_f "f"
-#else
-#define DEF_UREAD 1 /* Always do the more-expensive read. */
-#define OPT_LAZY_f /* I.e., the `-f' option is not added. */
-#endif
-
/*
* isdigit takes an `int', but expects values in the range of unsigned char.
* This wrapper ensures that values from a 'char' end up in the correct range.
@@ -106,15 +87,28 @@ int sumrusage; /* -S */
int termwidth; /* Width of the screen (0 == infinity). */
int showthreads; /* will threads be shown? */
+struct keyword_info {
+ /*
+ * Whether there is (at least) one column referencing this keyword that
+ * must be kept.
+ */
+#define KWI_HAS_MUST_KEEP_COLUMN (1 << 0)
+ /*
+ * Whether a column with such a keyword has been seen.
+ */
+#define KWI_SEEN (1 << 1)
+ u_int flags;
+};
+
struct velisthead varlist = STAILQ_HEAD_INITIALIZER(varlist);
+static struct velisthead Ovarlist = STAILQ_HEAD_INITIALIZER(Ovarlist);
-static int forceuread = DEF_UREAD; /* Do extra work to get u-area. */
static kvm_t *kd;
static int needcomm; /* -o "command" */
static int needenv; /* -e */
static int needuser; /* -o "user" */
static int optfatal; /* Fatal error parsing some list-option. */
-static int pid_max; /* kern.max_pid */
+static int pid_max; /* kern.pid_max */
static enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
@@ -154,23 +148,21 @@ static void init_list(struct listinfo *, addelem_rtn, int, const char *);
static char *kludge_oldps_options(const char *, char *, const char *);
static int pscomp(const void *, const void *);
static void saveuser(KINFO *);
-static void scanvars(void);
-static void sizevars(void);
+static void scan_vars(struct keyword_info *);
+static void remove_redundant_columns(struct keyword_info *);
static void pidmax_init(void);
static void usage(void);
-static char dfmt[] = "pid,tt,state,time,command";
-static char jfmt[] = "user,pid,ppid,pgid,sid,jobc,state,tt,time,command";
-static char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state,"
- "tt,time,command";
-static char o1[] = "pid";
-static char o2[] = "tt,state,time,command";
-static char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command";
-static char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz,"
- "%cpu,%mem,command";
-static char Zfmt[] = "label";
+static const char dfmt[] = "pid,tt,state,time,command";
+static const char jfmt[] = "user,pid,ppid,pgid,sid,jobc,state,tt,time,command";
+static const char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state,"
+ "tt,time,command";
+static const char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command";
+static const char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz,"
+ "%cpu,%mem,command";
+static const char Zfmt[] = "label";
-#define PS_ARGS "AaCcde" OPT_LAZY_f "G:gHhjJ:LlM:mN:O:o:p:rSTt:U:uvwXxZ"
+#define PS_ARGS "AaCcD:defG:gHhjJ:LlM:mN:O:o:p:rSTt:U:uvwXxZ"
int
main(int argc, char *argv[])
@@ -190,6 +182,9 @@ main(int argc, char *argv[])
int fwidthmin, fwidthmax;
char errbuf[_POSIX2_LINE_MAX];
char fmtbuf[_POSIX2_LINE_MAX];
+ enum { NONE = 0, UP = 1, DOWN = 2, BOTH = 1 | 2 } directions = NONE;
+ struct { int traversed; int initial; } pid_count;
+ struct keyword_info *keywords_info;
(void) setlocale(LC_ALL, "");
time(&now); /* Used by routines in print.c. */
@@ -227,6 +222,13 @@ main(int argc, char *argv[])
pidmax_init();
+#ifdef PS_CHECK_KEYWORDS
+ /* Check for obvious problems in the keywords array. */
+ check_keywords();
+ /* Resolve all aliases at start to spot errors. */
+ resolve_aliases();
+#endif
+
all = descendancy = _fmt = nselectors = optfatal = 0;
prtheader = showthreads = wflag = xkeep_implied = 0;
xkeep = -1; /* Neither -x nor -X. */
@@ -248,11 +250,6 @@ main(int argc, char *argv[])
while ((ch = getopt(argc, argv, PS_ARGS)) != -1)
switch (ch) {
case 'A':
- /*
- * Exactly the same as `-ax'. This has been
- * added for compatibility with SUSv3, but for
- * now it will not be described in the man page.
- */
all = xkeep = 1;
break;
case 'a':
@@ -264,18 +261,31 @@ main(int argc, char *argv[])
case 'c':
cflag = 1;
break;
+ case 'D': {
+ size_t len = strlen(optarg);
+
+ if (len <= 2 &&
+ strncasecmp(optarg, "up", len) == 0)
+ directions |= UP;
+ else if (len <= 4 &&
+ strncasecmp(optarg, "down", len) == 0)
+ directions |= DOWN;
+ else if (len <= 4 &&
+ strncasecmp(optarg, "both", len) == 0)
+ directions |= BOTH;
+ else
+ usage();
+ break;
+ }
case 'd':
descendancy = 1;
break;
case 'e': /* XXX set ufmt */
needenv = 1;
break;
-#ifdef LAZY_PS
case 'f':
- if (getuid() == 0 || getgid() == 0)
- forceuread = 1;
+ /* compat */
break;
-#endif
case 'G':
add_list(&gidlist, optarg);
xkeep_implied = 1;
@@ -283,17 +293,24 @@ main(int argc, char *argv[])
break;
case 'g':
#if 0
- /*-
- * XXX - This SUSv3 behavior is still under debate
- * since it conflicts with the (undocumented)
- * `-g' option. So we skip it for now.
+ /*
+ * XXX - This behavior is still under debate since it
+ * conflicts with the (undocumented) `-g' option
+ * and is non-standard. However, it is the
+ * behavior of most UNIX systems except
+ * SunOS/Solaris/illumos (see next comment; see
+ * also comment for '-s' below).
*/
add_list(&pgrplist, optarg);
xkeep_implied = 1;
nselectors++;
break;
#else
- /* The historical BSD-ish (from SunOS) behavior. */
+ /*
+ * The historical BSD-ish (from SunOS) behavior: Also
+ * display process group leaders (but we do not filter
+ * them out).
+ */
break; /* no-op */
#endif
case 'H':
@@ -308,17 +325,15 @@ main(int argc, char *argv[])
nselectors++;
break;
case 'j':
- parsefmt(jfmt, 0);
+ parsefmt(jfmt, &varlist, 0);
_fmt = 1;
- jfmt[0] = '\0';
break;
case 'L':
showkey();
exit(0);
case 'l':
- parsefmt(lfmt, 0);
+ parsefmt(lfmt, &varlist, 0);
_fmt = 1;
- lfmt[0] = '\0';
break;
case 'M':
memf = optarg;
@@ -330,14 +345,10 @@ main(int argc, char *argv[])
nlistf = optarg;
break;
case 'O':
- parsefmt(o1, 1);
- parsefmt(optarg, 1);
- parsefmt(o2, 1);
- o1[0] = o2[0] = '\0';
- _fmt = 1;
+ parsefmt(optarg, &Ovarlist, 1);
break;
case 'o':
- parsefmt(optarg, 1);
+ parsefmt(optarg, &varlist, 1);
_fmt = 1;
break;
case 'p':
@@ -350,20 +361,6 @@ main(int argc, char *argv[])
*/
nselectors++;
break;
-#if 0
- case 'R':
- /*-
- * XXX - This un-standard option is still under
- * debate. This is what SUSv3 defines as
- * the `-U' option, and while it would be
- * nice to have, it could cause even more
- * confusion to implement it as `-R'.
- */
- add_list(&ruidlist, optarg);
- xkeep_implied = 1;
- nselectors++;
- break;
-#endif
case 'r':
sortby = SORTCPU;
break;
@@ -372,11 +369,13 @@ main(int argc, char *argv[])
break;
#if 0
case 's':
- /*-
- * XXX - This non-standard option is still under
- * debate. This *is* supported on Solaris,
- * Linux, and IRIX, but conflicts with `-s'
- * on NetBSD and maybe some older BSD's.
+ /*
+ * XXX - This non-standard option is still under debate.
+ * It is supported on Solaris, Linux, IRIX, and
+ * OpenBSD but conflicts with '-s' on NetBSD. This
+ * is the same functionality as POSIX option '-g',
+ * but the cited systems do not provide it under
+ * '-g', only under '-s'.
*/
add_list(&sesslist, optarg);
xkeep_implied = 1;
@@ -393,22 +392,38 @@ main(int argc, char *argv[])
nselectors++;
break;
case 'U':
- /* This is what SUSv3 defines as the `-u' option. */
- add_list(&uidlist, optarg);
+ add_list(&ruidlist, optarg);
xkeep_implied = 1;
nselectors++;
break;
case 'u':
- parsefmt(ufmt, 0);
+#if 0
+ /*
+ * POSIX's '-u' behavior.
+ *
+ * This has not been activated because:
+ * 1. Option '-U' is a substitute for most users, and
+ * those that care seem more likely to want to match
+ * on the real user ID to display all processes
+ * launched by some users.
+ * 2. '-u' has been a canned display on the BSDs for
+ * a very long time (POLA).
+ */
+ add_list(&uidlist, optarg);
+ xkeep_implied = 1;
+ nselectors++;
+ break;
+#else
+ /* Historical BSD's '-u'. */
+ parsefmt(ufmt, &varlist, 0);
sortby = SORTCPU;
_fmt = 1;
- ufmt[0] = '\0';
break;
+#endif
case 'v':
- parsefmt(vfmt, 0);
+ parsefmt(vfmt, &varlist, 0);
sortby = SORTMEM;
_fmt = 1;
- vfmt[0] = '\0';
break;
case 'w':
if (wflag)
@@ -435,8 +450,7 @@ main(int argc, char *argv[])
xkeep = 1;
break;
case 'Z':
- parsefmt(Zfmt, 0);
- Zfmt[0] = '\0';
+ parsefmt(Zfmt, &varlist, 0);
break;
case '?':
default:
@@ -469,22 +483,60 @@ main(int argc, char *argv[])
xo_errx(1, "%s", errbuf);
if (!_fmt)
- parsefmt(dfmt, 0);
+ parsefmt(dfmt, &varlist, 0);
- if (!all && nselectors == 0) {
- uidlist.l.ptr = malloc(sizeof(uid_t));
- if (uidlist.l.ptr == NULL)
- xo_errx(1, "malloc failed");
- nselectors = 1;
- uidlist.count = uidlist.maxcount = 1;
- *uidlist.l.uids = getuid();
+ if (!STAILQ_EMPTY(&Ovarlist)) {
+ VARENT *const pid_entry = find_varentry("pid");
+
+ /*
+ * We insert the keywords passed by '-O' after the process ID if
+ * specified, else at start.
+ */
+ if (pid_entry != NULL) {
+ struct velisthead rest;
+
+ STAILQ_SPLIT_AFTER(&varlist, pid_entry, &rest, next_ve);
+ STAILQ_CONCAT(&varlist, &Ovarlist);
+ STAILQ_CONCAT(&varlist, &rest);
+ }
+ else {
+ STAILQ_SWAP(&varlist, &Ovarlist, varent);
+ STAILQ_CONCAT(&varlist, &Ovarlist);
+ }
}
+ keywords_info = calloc(known_keywords_nb, sizeof(struct keyword_info));
+ if (keywords_info == NULL)
+ xo_errx(1, "malloc failed");
/*
- * scan requested variables, noting what structures are needed,
- * and adjusting header widths as appropriate.
+ * Scan requested variables, noting which structures are needed and
+ * which keywords are specified.
*/
- scanvars();
+ scan_vars(keywords_info);
+ /*
+ * Remove redundant columns from "canned" displays (see the callee's
+ * herald comment for more details).
+ */
+ remove_redundant_columns(keywords_info);
+ free(keywords_info);
+ keywords_info = NULL;
+
+ if (all)
+ /*
+ * We have to display all processes, regardless of other
+ * options.
+ */
+ nselectors = 0;
+ else if (nselectors == 0) {
+ /*
+ * Default is to request our processes only. As per POSIX, we
+ * match processes by their effective user IDs and we use our
+ * effective user ID as our own identity.
+ */
+ expand_list(&uidlist);
+ uidlist.l.uids[uidlist.count++] = geteuid();
+ nselectors = 1;
+ }
/*
* Get process list. If the user requested just one selector-
@@ -504,7 +556,7 @@ main(int argc, char *argv[])
what = KERN_PROC_PGRP | showthreads;
flag = *pgrplist.l.pids;
nselectors = 0;
- } else if (pidlist.count == 1 && !descendancy) {
+ } else if (pidlist.count == 1 && directions == NONE) {
what = KERN_PROC_PID | showthreads;
flag = *pidlist.l.pids;
nselectors = 0;
@@ -539,14 +591,33 @@ main(int argc, char *argv[])
if ((kp == NULL && errno != ESRCH) || (kp != NULL && nentries < 0))
xo_errx(1, "%s", kvm_geterr(kd));
nkept = 0;
- if (descendancy)
+ pid_count.initial = pidlist.count;
+ if (directions & DOWN)
for (elem = 0; elem < pidlist.count; elem++)
- for (i = 0; i < nentries; i++)
+ for (i = 0; i < nentries; i++) {
+ if (kp[i].ki_ppid == kp[i].ki_pid)
+ continue;
if (kp[i].ki_ppid == pidlist.l.pids[elem]) {
if (pidlist.count >= pidlist.maxcount)
expand_list(&pidlist);
pidlist.l.pids[pidlist.count++] = kp[i].ki_pid;
}
+ }
+ pid_count.traversed = pidlist.count;
+ if (directions & UP)
+ for (elem = 0; elem < pidlist.count; elem++) {
+ if (elem >= pid_count.initial && elem < pid_count.traversed)
+ continue;
+ for (i = 0; i < nentries; i++) {
+ if (kp[i].ki_ppid == kp[i].ki_pid)
+ continue;
+ if (kp[i].ki_pid == pidlist.l.pids[elem]) {
+ if (pidlist.count >= pidlist.maxcount)
+ expand_list(&pidlist);
+ pidlist.l.pids[pidlist.count++] = kp[i].ki_ppid;
+ }
+ }
+ }
if (nentries > 0) {
if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
xo_errx(1, "malloc failed");
@@ -631,11 +702,10 @@ main(int argc, char *argv[])
}
}
- sizevars();
-
if (nkept == 0) {
printheader();
- xo_finish();
+ if (xo_finish() < 0)
+ xo_err(1, "stdout");
exit(1);
}
@@ -690,7 +760,7 @@ main(int argc, char *argv[])
/* No padding for the last column, if it's LJUST. */
fwidthmin = (xo_get_style(NULL) != XO_STYLE_TEXT ||
(STAILQ_NEXT(vent, next_ve) == NULL &&
- (vent->var->flag & LJUST))) ? 0 : vent->var->width;
+ (vent->var->flag & LJUST))) ? 0 : vent->width;
snprintf(fmtbuf, sizeof(fmtbuf), "{:%s/%%%s%d..%dhs}",
vent->var->field ? vent->var->field : vent->var->name,
(vent->var->flag & LJUST) ? "-" : "",
@@ -720,7 +790,8 @@ main(int argc, char *argv[])
}
xo_close_list("process");
xo_close_container("process-information");
- xo_finish();
+ if (xo_finish() < 0)
+ xo_err(1, "stdout");
free_list(&gidlist);
free_list(&jidlist);
@@ -790,14 +861,14 @@ addelem_jid(struct listinfo *inf, const char *elem)
int tempid;
if (*elem == '\0') {
- warnx("Invalid (zero-length) jail id");
+ xo_warnx("Invalid (zero-length) jail id");
optfatal = 1;
return (0); /* Do not add this value. */
}
tempid = jail_getid(elem);
if (tempid < 0) {
- warnx("Invalid %s: %s", inf->lname, elem);
+ xo_warnx("Invalid %s: %s", inf->lname, elem);
optfatal = 1;
return (0);
}
@@ -839,7 +910,7 @@ addelem_pid(struct listinfo *inf, const char *elem)
return (1);
}
-/*-
+/*
* The user can specify a device via one of three formats:
* 1) fully qualified, e.g.: /dev/ttyp0 /dev/console /dev/pts/0
* 2) missing "/dev", e.g.: ttyp0 console pts/0
@@ -1174,22 +1245,22 @@ init_list(struct listinfo *inf, addelem_rtn artn, int elemsize,
}
VARENT *
-find_varentry(VAR *v)
+find_varentry(const char *name)
{
struct varent *vent;
STAILQ_FOREACH(vent, &varlist, next_ve) {
- if (strcmp(vent->var->name, v->name) == 0)
+ if (strcmp(vent->var->name, name) == 0)
return vent;
}
return NULL;
}
static void
-scanvars(void)
+scan_vars(struct keyword_info *const keywords_info)
{
struct varent *vent;
- VAR *v;
+ const VAR *v;
STAILQ_FOREACH(vent, &varlist, next_ve) {
v = vent->var;
@@ -1197,6 +1268,47 @@ scanvars(void)
needuser = 1;
if (v->flag & COMM)
needcomm = 1;
+ if ((vent->flags & VE_KEEP) != 0)
+ keywords_info[aliased_keyword_index(v)].flags |=
+ KWI_HAS_MUST_KEEP_COLUMN;
+ }
+}
+
+/*
+ * For each explicitly requested keyword, remove all the same keywords
+ * from "canned" displays. If the same keyword appears multiple times
+ * only in "canned displays", then keep the first (leftmost) occurence
+ * only (with the reasoning that columns requested first are the most
+ * important as their positions catch the eye more).
+ */
+static void
+remove_redundant_columns(struct keyword_info *const keywords_info)
+{
+ struct varent *prev_vent, *vent, *next_vent;
+
+ prev_vent = NULL;
+ STAILQ_FOREACH_SAFE(vent, &varlist, next_ve, next_vent) {
+ const VAR *const v = vent->var;
+ struct keyword_info *const kwi =
+ &keywords_info[aliased_keyword_index(v)];
+
+ /*
+ * If the current column is not marked as to absolutely keep,
+ * and we have either already output one with the same keyword
+ * or know we will output one later, remove it.
+ */
+ if ((vent->flags & VE_KEEP) == 0 &&
+ (kwi->flags & (KWI_HAS_MUST_KEEP_COLUMN | KWI_SEEN)) != 0) {
+ if (prev_vent == NULL)
+ STAILQ_REMOVE_HEAD(&varlist, next_ve);
+ else
+ STAILQ_REMOVE_AFTER(&varlist, prev_vent,
+ next_ve);
+ } else
+ prev_vent = vent;
+
+
+ kwi->flags |= KWI_SEEN;
}
}
@@ -1204,10 +1316,10 @@ static void
format_output(KINFO *ki)
{
struct varent *vent;
- VAR *v;
+ const VAR *v;
KINFO_STR *ks;
char *str;
- int len;
+ u_int len;
STAILQ_INIT(&ki->ki_ks);
STAILQ_FOREACH(vent, &varlist, next_ve) {
@@ -1222,23 +1334,8 @@ format_output(KINFO *ki)
len = strlen(str);
} else
len = 1; /* "-" */
- if (v->width < len)
- v->width = len;
- }
-}
-
-static void
-sizevars(void)
-{
- struct varent *vent;
- VAR *v;
- int i;
-
- STAILQ_FOREACH(vent, &varlist, next_ve) {
- v = vent->var;
- i = strlen(vent->header);
- if (v->width < i)
- v->width = i;
+ if (vent->width < len)
+ vent->width = len;
}
}
@@ -1253,38 +1350,24 @@ fmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki,
return (s);
}
-#define UREADOK(ki) (forceuread || (ki->ki_p->ki_flag & P_INMEM))
-
static void
saveuser(KINFO *ki)
{
char tdname[COMMLEN + 1];
- char *argsp;
- if (ki->ki_p->ki_flag & P_INMEM) {
- /*
- * The u-area might be swapped out, and we can't get
- * at it because we have a crashdump and no swap.
- * If it's here fill in these fields, otherwise, just
- * leave them 0.
- */
- ki->ki_valid = 1;
- } else
- ki->ki_valid = 0;
+ ki->ki_valid = 1;
+
/*
* save arguments if needed
*/
if (needcomm) {
if (ki->ki_p->ki_stat == SZOMB) {
ki->ki_args = strdup("<defunct>");
- } else if (UREADOK(ki) || (ki->ki_p->ki_args != NULL)) {
+ } else {
(void)snprintf(tdname, sizeof(tdname), "%s%s",
ki->ki_p->ki_tdname, ki->ki_p->ki_moretdname);
ki->ki_args = fmt(kvm_getargv, ki,
ki->ki_p->ki_comm, tdname, COMMLEN * 2 + 1);
- } else {
- asprintf(&argsp, "(%s)", ki->ki_p->ki_comm);
- ki->ki_args = argsp;
}
if (ki->ki_args == NULL)
xo_errx(1, "malloc failed");
@@ -1292,11 +1375,8 @@ saveuser(KINFO *ki)
ki->ki_args = NULL;
}
if (needenv) {
- if (UREADOK(ki))
- ki->ki_env = fmt(kvm_getenvv, ki,
- (char *)NULL, (char *)NULL, 0);
- else
- ki->ki_env = strdup("()");
+ ki->ki_env = fmt(kvm_getenvv, ki, (char *)NULL,
+ (char *)NULL, 0);
if (ki->ki_env == NULL)
xo_errx(1, "malloc failed");
} else {
@@ -1307,7 +1387,7 @@ saveuser(KINFO *ki)
/* A macro used to improve the readability of pscomp(). */
#define DIFF_RETURN(a, b, field) do { \
if ((a)->field != (b)->field) \
- return (((a)->field < (b)->field) ? -1 : 1); \
+ return (((a)->field < (b)->field) ? -1 : 1); \
} while (0)
static int
@@ -1456,12 +1536,13 @@ pidmax_init(void)
static void __dead2
usage(void)
{
-#define SINGLE_OPTS "[-aCcde" OPT_LAZY_f "HhjlmrSTuvwXxZ]"
+#define SINGLE_OPTS "[-aCcdeHhjlmrSTuvwXxZ]"
- (void)xo_error("%s\n%s\n%s\n%s\n",
- "usage: ps " SINGLE_OPTS " [-O fmt | -o fmt] [-G gid[,gid...]]",
- " [-J jid[,jid...]] [-M core] [-N system]",
+ xo_error("%s\n%s\n%s\n%s\n%s\n",
+ "usage: ps [--libxo] " SINGLE_OPTS " [-O fmt | -o fmt]",
+ " [-G gid[,gid...]] [-J jid[,jid...]] [-M core] [-N system]",
" [-p pid[,pid...]] [-t tty[,tty...]] [-U user[,user...]]",
- " ps [-L]");
+ " [-D up | down | both]",
+ " ps [--libxo] -L");
exit(1);
}
diff --git a/bin/ps/ps.h b/bin/ps/ps.h
index 03e38af9d517..065a4c1f1c54 100644
--- a/bin/ps/ps.h
+++ b/bin/ps/ps.h
@@ -27,15 +27,13 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
- *
- * @(#)ps.h 8.1 (Berkeley) 5/31/93
- * $FreeBSD$
*/
#include <sys/queue.h>
#define UNLIMITED 0 /* unlimited terminal width */
-enum type { CHAR, UCHAR, SHORT, USHORT, INT, UINT, LONG, ULONG, KPTR, PGTOK };
+enum type { UNSPEC, /* For output routines that don't care and aliases. */
+ CHAR, UCHAR, SHORT, USHORT, INT, UINT, LONG, ULONG, KPTR, PGTOK };
typedef struct kinfo_str {
STAILQ_ENTRY(kinfo_str) ks_next;
@@ -56,25 +54,39 @@ typedef struct kinfo {
STAILQ_HEAD(, kinfo_str) ki_ks;
} KINFO;
-/* Variables. */
+/* Keywords/variables to be printed. */
typedef struct varent {
- STAILQ_ENTRY(varent) next_ve;
- const char *header;
- struct var *var;
+ STAILQ_ENTRY(varent) next_ve;
+ const char *header;
+ const struct var *var;
+ u_int width;
+#define VE_KEEP (1 << 0)
+ uint16_t flags;
} VARENT;
+STAILQ_HEAD(velisthead, varent);
-typedef struct var {
+struct var;
+typedef struct var VAR;
+/* Structure representing one available keyword. */
+struct var {
const char *name; /* name(s) of variable */
+ union {
+ /* Valid field depends on RESOLVED_ALIAS' presence. */
+ const char *aliased; /* keyword this one is an alias to */
+ const VAR *final_kw; /* final aliased keyword */
+ };
const char *header; /* default header */
- const char *alias; /* aliases */
const char *field; /* xo field name */
-#define COMM 0x01 /* needs exec arguments and environment (XXX) */
-#define LJUST 0x02 /* left adjust on output (trailing blanks) */
-#define USER 0x04 /* needs user structure */
-#define INF127 0x10 /* values >127 displayed as 127 */
+#define COMM 0x01 /* needs exec arguments and environment (XXX) */
+#define LJUST 0x02 /* left adjust on output (trailing blanks) */
+#define USER 0x04 /* needs user structure */
+#define INF127 0x10 /* values >127 displayed as 127 */
+#define NOINHERIT 0x1000 /* Don't inherit flags from aliased keyword. */
+#define RESOLVING_ALIAS 0x10000 /* Used transiently to resolve aliases. */
+#define RESOLVED_ALIAS 0x20000 /* Mark that an alias has been resolved. */
u_int flag;
- /* output routine */
- char *(*oproc)(struct kinfo *, struct varent *);
+ /* output routine */
+ char *(*oproc)(struct kinfo *, struct varent *);
/*
* The following (optional) elements are hooks for passing information
* to the generic output routine pvar (which prints simple elements
@@ -82,9 +94,7 @@ typedef struct var {
*/
size_t off; /* offset in structure */
enum type type; /* type of element */
- const char *fmt; /* printf format */
-
- short width; /* calculated width */
-} VAR;
+ const char *fmt; /* printf format (depends on output routine) */
+};
#include "extern.h"