diff options
Diffstat (limited to 'bin/ps')
-rw-r--r-- | bin/ps/Makefile | 13 | ||||
-rw-r--r-- | bin/ps/Makefile.depend | 6 | ||||
-rw-r--r-- | bin/ps/extern.h | 13 | ||||
-rw-r--r-- | bin/ps/fmt.c | 9 | ||||
-rw-r--r-- | bin/ps/keyword.c | 651 | ||||
-rw-r--r-- | bin/ps/nlist.c | 9 | ||||
-rw-r--r-- | bin/ps/print.c | 39 | ||||
-rw-r--r-- | bin/ps/ps.1 | 889 | ||||
-rw-r--r-- | bin/ps/ps.c | 407 | ||||
-rw-r--r-- | bin/ps/ps.h | 50 |
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" |