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