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