From: David van Moolenbroek Date: Wed, 28 Oct 2015 22:51:59 +0000 (+0000) Subject: Import NetBSD ps(1) X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/Bv9ARM.ch05.html?a=commitdiff_plain;h=97189037b1c31a92749a06453951cb811bee1eb0;p=minix.git Import NetBSD ps(1) No changes except for one cosmetic adjustment: NetBSD has chosen to rename the standard TT column to TTY and not shorten tty names; we undo those changes, making ps(1) behave more in accordance with the specification and its manual page, and, most importantly for us, not use an incredibly wide TTY column to print "console". Change-Id: I3b3c198762f3eacf1b8e500557a803c1fedf2a61 --- diff --git a/bin/Makefile b/bin/Makefile index bff3add7c..8dee2cf16 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -4,7 +4,7 @@ #__MINIX: chio mt ps SUBDIR= cat chmod cp csh date dd df domainname echo ed expr hostname \ - kill ksh ln ls mkdir mv pax pwd rcp rcmd rm rmdir sh \ + kill ksh ln ls mkdir mv pax ps pwd rcp rcmd rm rmdir sh \ sleep stty sync test .include diff --git a/bin/ps/Makefile b/bin/ps/Makefile new file mode 100644 index 000000000..0919b105e --- /dev/null +++ b/bin/ps/Makefile @@ -0,0 +1,12 @@ +# $NetBSD: Makefile,v 1.29 2011/08/14 10:53:17 christos Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/2/93 + +PROG= ps +SRCS= fmt.c keyword.c nlist.c print.c ps.c +DPADD= ${LIBM} ${LIBKVM} +LDADD= -lm -lkvm + +CWARNFLAGS+= -Wno-format-y2k +COPTS.print.c = -Wno-format-nonliteral + +.include diff --git a/bin/ps/keyword.c b/bin/ps/keyword.c new file mode 100644 index 000000000..d9833f494 --- /dev/null +++ b/bin/ps/keyword.c @@ -0,0 +1,419 @@ +/* $NetBSD: keyword.c,v 1.54 2014/01/15 08:07:53 mlelstv 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.54 2014/01/15 08:07:53 mlelstv 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, uname), + 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), +#ifndef __minix + VAR6("tt", "TTY", LJUST, tname, POFF(p_tdev), INT32), +#else /* __minix */ + VAR6("tt", "TT", LJUST, tname, POFF(p_tdev), INT32), +#endif /* __minix */ + 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, uname), + 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(1, 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(1, "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(1, NULL); + if ((newheader = strdup(hp)) == NULL) + err(1, 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 000000000..013785575 --- /dev/null +++ b/bin/ps/nlist.c @@ -0,0 +1,207 @@ +/* $NetBSD: nlist.c,v 1.26 2008/04/28 20:22:51 martin 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.26 2008/04/28 20:22:51 martin 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 ccpu; /* 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 */ + +#define kread(x, v) \ + kvm_read(kd, psnl[x].n_value, (char *)&v, sizeof v) != sizeof(v) + +int +donlist(void) +{ + int rval; + fixpt_t xccpu; + + rval = 0; + nlistread = 1; + if (kvm_nlist(kd, psnl)) { + nlisterr(psnl); + eval = 1; + return (1); + } + if (kread(X_FSCALE, fscale)) { + warnx("fscale: %s", kvm_geterr(kd)); + eval = rval = 1; + } + if (kread(X_PHYSMEM, mempages)) { + warnx("avail_start: %s", kvm_geterr(kd)); + eval = rval = 1; + } + if (kread(X_CCPU, xccpu)) { + warnx("ccpu: %s", kvm_geterr(kd)); + eval = rval = 1; + } + if (kread(X_MAXSLP, maxslp)) { + warnx("maxslp: %s", kvm_geterr(kd)); + eval = rval = 1; + } + ccpu = (double)xccpu / fscale; + return (rval); +} + +int +donlist_sysctl(void) +{ + int mib[2]; + size_t size; + fixpt_t xccpu; + uint64_t memsize; + + nlistread = 1; + mib[0] = CTL_HW; + mib[1] = HW_PHYSMEM64; + size = sizeof(memsize); + if (sysctl(mib, 2, &memsize, &size, NULL, 0) == 0) + mempages = memsize / getpagesize(); + else + mempages = 0; + + mib[0] = CTL_KERN; + mib[1] = KERN_FSCALE; + size = sizeof(fscale); + if (sysctl(mib, 2, &fscale, &size, NULL, 0) == -1) + fscale = (1 << 8); /* XXX Hopefully reasonable default */ + + mib[0] = CTL_KERN; + mib[1] = KERN_CCPU; + size = sizeof(xccpu); + if (sysctl(mib, 2, &xccpu, &size, NULL, 0) == -1) + ccpu = exp(-1.0 / 20.0); /* XXX Hopefully reasonable default */ + else + ccpu = (double)xccpu / fscale; + + mib[0] = CTL_VM; + mib[1] = VM_MAXSLP; + size = sizeof(maxslp); + if (sysctl(mib, 2, &maxslp, &size, NULL, 0) == -1) +#ifdef MAXSLP + maxslp = MAXSLP; +#else + maxslp = 20; /* XXX Hopefully reasonable default */ +#endif + + mib[0] = CTL_VM; + mib[1] = VM_USPACE; + size = sizeof(uspace); + if (sysctl(mib, 2, &uspace, &size, NULL, 0) == -1) +#ifdef USPACE + uspace = USPACE; +#else + uspace = getpagesize(); /* XXX Hopefully reasonable default */ +#endif + + return 0; +} + +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 000000000..48c4ea1e1 --- /dev/null +++ b/bin/ps/print.c @@ -0,0 +1,1483 @@ +/* $NetBSD: print.c,v 1.123 2014/11/15 01:58:34 joerg 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.123 2014/11/15 01:58:34 joerg 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *ki; + VAR *v; + int left; + char **argv, **p, *name; + + if (mode == WIDTHMODE) + return; + + ki = arg; + 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) { + 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *ki; + VAR *v; + int left, i; + char buf[16], *p; + + if (mode == WIDTHMODE) + return; + + ki = arg; + 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *ki; + VAR *v; + int left, i; + const char *p; + + if (mode == WIDTHMODE) + return; + + ki = arg; + 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + strprintorsetwidth(v, k->p_comm, mode); +} + +void +emul(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + strprintorsetwidth(v, k->p_ename, mode); +} + +void +logname(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + strprintorsetwidth(v, k->p_login, mode); +} + +void +state(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + int flag, is_zombie; + char *cp; + VAR *v; + char buf[16]; + + k = arg; + 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_lwp *k; + int flag; + char *cp; + VAR *v; + char buf[16]; + + k = arg; + 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + intprintorsetwidth(v, k->p_nice - NZERO, mode); +} + +void +pri(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_lwp *l; + VAR *v; + + l = arg; + v = ve->var; + intprintorsetwidth(v, l->l_priority, mode); +} + +void +uname(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + strprintorsetwidth(v, user_from_uid(k->p_uid, 0), mode); +} + +void +runame(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + strprintorsetwidth(v, user_from_uid(k->p_ruid, 0), mode); +} + +void +svuname(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + strprintorsetwidth(v, user_from_uid(k->p_svuid, 0), mode); +} + +void +gname(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + strprintorsetwidth(v, group_from_gid(k->p_gid, 0), mode); +} + +void +rgname(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + strprintorsetwidth(v, group_from_gid(k->p_rgid, 0), mode); +} + +void +svgname(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + strprintorsetwidth(v, group_from_gid(k->p_svgid, 0), mode); +} + +void +tdev(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + dev_t dev; + char buff[16]; + + k = arg; + 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + dev_t dev; + const char *ttname; + int noctty; + + k = arg; + 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 { +#ifdef __minix + /* Actually shorten TTY names. "console" is *really* long. */ + if (strcmp(ttname, "console") == 0) + ttname = "co"; + else if (strncmp(ttname, "tty", 3) == 0 && ttname[3] != '\0') + ttname += 3; + else if (strncmp(ttname, "pts/", 4) == 0 && ttname[4] != '\0') + ttname += 4; /* this is what FreeBSD does */ +#endif /* __minix */ + 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + dev_t dev; + const char *ttname; + + k = arg; + 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + time_t startt; + struct tm *tp; + char buf[100], *cp; + + k = arg; + 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + time_t startt; + char buf[100]; + + k = arg; + 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + int32_t origseconds, secs, mins, hours, days; + int fmtlen, printed_something; + + k = arg; + 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_lwp *l; + VAR *v; + char *buf; + + l = arg; + v = ve->var; + if (l->l_wchan) { + if (l->l_wmesg[0]) { + strprintorsetwidth(v, l->l_wmesg, mode); + v->width = min(v->width, KI_WMESGLEN); + } else { + (void)asprintf(&buf, "%-*" PRIx64, v->width, + l->l_wchan); + if (buf == NULL) + err(1, "%s", ""); + strprintorsetwidth(v, buf, mode); + v->width = min(v->width, KI_WMESGLEN); + free(buf); + } + } else { + if (mode == PRINTMODE) + (void)printf("%-*s", v->width, "-"); + } +} + +#define pgtok(a) (((a)*(size_t)getpagesize())/1024) + +void +vsize(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + intprintorsetwidth(v, pgtok(k->p_vm_msize), mode); +} + +void +rssize(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + /* XXX don't have info about shared */ + intprintorsetwidth(v, pgtok(k->p_vm_rssize), mode); +} + +void +p_rssize(void *arg, VARENT *ve, enum mode mode) /* doesn't account for text */ +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + intprintorsetwidth(v, pgtok(k->p_vm_rssize), mode); +} + +void +cpuid(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_lwp *l; + VAR *v; + + l = arg; + 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + int32_t secs; + int32_t psecs; /* "parts" of a second. first micro, then centi */ + + k = arg; + 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_lwp *l; + VAR *v; + int32_t secs; + int32_t psecs; /* "parts" of a second. first micro, then centi */ + + l = arg; + v = ve->var; + + secs = l->l_rtime_sec; + psecs = l->l_rtime_usec; + + cputime1(secs, psecs, v, mode); +} + +double +getpcpu(const struct kinfo_proc2 *k) +{ + static int failure; + + if (!nlistread) + failure = (kd) ? donlist() : 1; + if (failure) + return (0.0); + +#define fxtofl(fixpt) ((double)(fixpt) / fscale) + + if (k->p_swtime == 0 || k->p_realstat == SZOMB) + return (0.0); + if (rawcpu) + return (100.0 * fxtofl(k->p_pctcpu)); + return (100.0 * fxtofl(k->p_pctcpu) / + (1.0 - exp(k->p_swtime * log(ccpu)))); +} + +void +pcpu(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + double dbl; + + k = arg; + v = ve->var; + dbl = getpcpu(k); + doubleprintorsetwidth(v, dbl, (dbl >= 99.95) ? 0 : 1, mode); +} + +double +getpmem(const struct kinfo_proc2 *k) +{ + static int failure; + double fracmem; + int szptudot; + + if (!nlistread) + failure = (kd) ? donlist() : 1; + if (failure) + return (0.0); + + /* 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + doubleprintorsetwidth(v, getpmem(k), 1, mode); +} + +void +pagein(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + v = ve->var; + intprintorsetwidth(v, k->p_uvalid ? k->p_uru_majflt : 0, mode); +} + +void +maxrss(void *arg, 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_proc2 *k; + VAR *v; + + k = arg; + 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(1, "unknown type %d", v->type); + } +#undef GET +#undef CHK_INF127 +} + +void +pvar(void *arg, VARENT *ve, enum mode mode) +{ + VAR *v; + + v = ve->var; + if (v->flag & UAREA && !((struct kinfo_proc2 *)arg)->p_uvalid) { + if (mode == PRINTMODE) + (void)printf("%*s", v->width, "-"); + return; + } + + (void)printval((char *)arg + v->off, v, mode); +} + +void +putimeval(void *arg, VARENT *ve, enum mode mode) +{ + VAR *v = ve->var; + struct kinfo_proc2 *k = arg; + ulong secs = *(uint32_t *)((char *)arg + v->off); + ulong usec = *(uint32_t *)((char *)arg + 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(void *arg, VARENT *ve, enum mode mode) +{ + struct kinfo_lwp *l; + VAR *v; + + l = arg; + 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 000000000..46a6dbfed --- /dev/null +++ b/bin/ps/ps.1 @@ -0,0 +1,708 @@ +.\" $NetBSD: ps.1,v 1.103 2014/01/15 09:24:31 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 January 15, 2014 +.Dt PS 1 +.Os +.Sh NAME +.Nm ps +.Nd process status +.Sh SYNOPSIS +.Nm +.Op Fl AaCcehjlmrSsTuvwx +.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 +.Tn 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' +.Tn 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 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 +.Tn 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 +.Tn %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_FSTRACE" Ta No "0x00010000 tracing via file system" +.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 Tn 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 \*[Lt] +The process has raised +.Tn 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 +.Tn 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 +.Tn "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 +.Tn "struct proc" +belonging to the process. +.It Ar pagein +pageins (same as majflt) +.It Ar pgid +process group number +.It Ar pid +process +.Tn ID +.It Ar ppid +parent process +.Tn ID +.It Ar pri +scheduling priority +.It Ar re +core residency time (in seconds; 127 = infinity) +.It Ar rgid +real group +.Tn 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 +.Tn ID +.It Ar ruser +user name (from ruid) +.It Ar sess +session pointer +.It Ar sid +session +.Tn 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 +.Tn 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 +.Tn "struct user" +belonging to the LWP. +.It Ar ucomm +name to be used for accounting +.It Ar uid +effective user +.Tn 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 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 000000000..1e243ae02 --- /dev/null +++ b/bin/ps/ps.c @@ -0,0 +1,783 @@ +/* $NetBSD: ps.c,v 1.83 2015/06/16 22:31:08 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.83 2015/06/16 22:31:08 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 "ps.h" + +/* + * ARGOPTS must contain all option characters that take arguments + * (except for 't'!) - it is used in kludge_oldps_options() + */ +#define GETOPTSTR "aAcCeghjk:LlM:mN:O:o:p:rSsTt:U:uvW:wx" +#define ARGOPTS "kMNOopUW" + +struct kinfo_proc2 *kinfo; +struct varlist displaylist = SIMPLEQ_HEAD_INITIALIZER(displaylist); +struct varlist sortlist = SIMPLEQ_HEAD_INITIALIZER(sortlist); + +int eval; /* exit value */ +int rawcpu; /* -C */ +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 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 *); +int main(int, char *[]); + +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(1, "%s", ttypath); + } + if (!S_ISCHR(sb.st_mode)) + errx(1, "%s: not a terminal", ttypath); + return sb.st_rdev; +} + +int +main(int argc, char *argv[]) +{ + struct varent *vent; + struct winsize ws; + struct kinfo_lwp *kl, *l; + int ch, i, j, fmt, lineno, nentries, nlwps; + long long flag; + int prtheader, wflag, what, xflg, 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]); + + fmt = prtheader = wflag = xflg = 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 '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(); + exit(0); + /* NOTREACHED */ + 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(1, "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 == 0) + errx(1, "%s", errbuf); + + if (!fmt) + parsefmt(default_fmt); + + /* Add default sort criteria */ + parsesort("tdev,pid"); + 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); + } + + /* + * scan requested variables, noting what structures are needed. + */ + scanvars(); + + /* + * select procs + */ + if (!(kinfo = getkinfo_kvm(kd, what, flag, &nentries))) + err(1, "%s", kvm_geterr(kd)); + if (nentries == 0) { + printheader(); + exit(1); + } + /* + * sort proc list + */ + qsort(kinfo, nentries, sizeof(struct kinfo_proc2), pscomp); + /* + * 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 kinfo_proc2 *ki = &kinfo[i]; + + 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, ki, l, 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, ki, &kl[j], 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 kinfo_proc2 *ki = &kinfo[i]; + + 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, ki, l, 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, ki, &kl[j], PRINTMODE); + if (SIMPLEQ_NEXT(vent, next) != NULL) + (void)putchar(' '); + } + (void)putchar('\n'); + if (prtheader && lineno++ == prtheader - 4) { + (void)putchar('\n'); + printheader(); + lineno = 0; + } + } + } + } + exit(eval); + /* NOTREACHED */ +} + +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 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 kinfo_proc2 *ka = (const struct kinfo_proc2 *)a; + const struct kinfo_proc2 *kb = (const struct kinfo_proc2 *)b; + + 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 *)a + v->off); + sb = (const void *)((const char *)b + 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 = getpcpu(kb) - getpcpu(ka); + 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(1, 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(1, "Invalid %s: `%s'", msg, str); + + if (ul > INT_MAX) + errx(1, "Out of range %s: `%s'", msg, str); + + return (int)ul; +} + +static void +usage(void) +{ + + (void)fprintf(stderr, + "usage:\t%s\n\t %s\n\t%s\n", + "ps [-AaCcehjlmrSsTuvwx] [-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/distrib/sets/lists/minix-base/mi b/distrib/sets/lists/minix-base/mi index e3eb91afb..58fb5e913 100644 --- a/distrib/sets/lists/minix-base/mi +++ b/distrib/sets/lists/minix-base/mi @@ -38,6 +38,7 @@ ./bin/pax minix-base ./bin/printconfig minix-base ./bin/printroot minix-base +./bin/ps minix-base ./bin/pwd minix-base ./bin/rcmd minix-base ./bin/rcp minix-base @@ -437,7 +438,7 @@ ./usr/bin/printf minix-base ./usr/bin/profile minix-base ./usr/bin/progressbar minix-base -./usr/bin/ps minix-base +./usr/bin/ps minix-base obsolete ./usr/bin/pwhash minix-base ./usr/bin/ramdisk minix-base ./usr/bin/rarpd minix-base diff --git a/minix/commands/Makefile b/minix/commands/Makefile index f5833713c..b0c37e757 100644 --- a/minix/commands/Makefile +++ b/minix/commands/Makefile @@ -18,7 +18,7 @@ SUBDIR= add_route arp at backup \ mount mt netconf \ nonamed \ postinstall prep printroot \ - profile progressbar pr_routes ps pwdauth \ + profile progressbar pr_routes pwdauth \ ramdisk rarpd rawspeed readclock \ remsync rget rlogin \ rotate service setup \ diff --git a/minix/commands/ps/Makefile b/minix/commands/ps/Makefile deleted file mode 100644 index 96f376b31..000000000 --- a/minix/commands/ps/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# Makefile for the process status utility. -# - -PROG= ps - -.include diff --git a/minix/commands/ps/ps.1 b/minix/commands/ps/ps.1 deleted file mode 100644 index 5ed7fac4e..000000000 --- a/minix/commands/ps/ps.1 +++ /dev/null @@ -1,88 +0,0 @@ -.TH PS 1 -.SH NAME -ps \- process status -.SH SYNOPSIS -\fBps \fR[\fR[\fB\-\fR]\fBalxE\fR] -.br -.de FL -.TP -\\fB\\$1\\fR -\\$2 -.. -.de EX -.TP 20 -\\fB\\$1\\fR -# \\$2 -.. -.SH OPTIONS -.TP 5 -.B \-a -# Print all processes with controlling terminals -.TP 5 -.B \-l -# Give long listing -.TP 5 -.B \-x -# Include processes without a terminal -.TP 5 -.B \-E -# Print kernel endpoint numbers where pids are normally printed -.SH EXAMPLES -.TP 20 -.B ps -# Show user's own processes in short format -.TP 20 -.B ps \-axlE -# Print all processes and tasks in long format -.TP 20 -.B ps \axlE -# Same -- the '\-' is optional -.SH DESCRIPTION -.PP -\fIPs\fR prints the status of active processes. Normally only the caller's own -processes are listed in short format (the PID, TTY, TIME and CMD fields as -explained below). The long listing contains: -.PP - ST - State: - R: runnable - W: waiting (on a message) - S: sleeping (i.e.,suspended on PM or VFS) - Z: zombie - T: stopped -.PP - UID, PID, PPID, PGRP - The user, process, parent process and process group ID's. -.PP - SZ - Size of the process in kilobytes. -.PP - RECV - Process/task on which a receiving process is waiting or sleeping. -.PP - TTY - Controlling tty for the process. -.PP - TIME - Process' cumulative (user + system) execution time. -.PP - CMD Command line arguments of the process. -.PP -.PP -The \fI/proc\fR file system is used to obtain the process information. -Terminal names in \fI/dev\fR are used to generate the -mnemonic names in the TTY column, so \fIps\fR is independent of terminal naming -conventions. -.SH NOTES -The '\-' option prefix is not required. -For marginal compatibility with System V usage, the hidden option -.B \-e -means the same as -.BR \-ax , -and -.B \-f -is the same as -.BR \-l . - -.\" edited by ASW 2004-12-14 - diff --git a/minix/commands/ps/ps.c b/minix/commands/ps/ps.c deleted file mode 100644 index dd6664675..000000000 --- a/minix/commands/ps/ps.c +++ /dev/null @@ -1,524 +0,0 @@ -/* ps - print status Author: Peter Valkenburg */ -/* Modified for ProcFS by Alen Stojanov and David van Moolenbroek */ - -/* Ps.c, Peter Valkenburg (valke@psy.vu.nl), january 1990. - * - * This is a V7 ps(1) look-alike for MINIX >= 1.5.0. - * It does not support the 'k' option (i.e. cannot read memory from core file). - * If you want to compile this for non-IBM PC architectures, the header files - * require that you have your CHIP, MACHINE etc. defined. - * Full syntax: - * ps [-][aeflx] - * Option `a' gives all processes, `l' for detailed info, `x' includes even - * processes without a terminal. - * The `f' and `e' options were added by Kees Bot for the convenience of - * Solaris users accustomed to these options. The `e' option is equivalent to - * `a' and `f' is equivalent to -l. These do not appear in the usage message. - */ - -/* Some technical comments on this implementation: - * - * Most fields are similar to V7 ps(1), except for CPU, NICE, PRI which are - * absent, RECV which replaces WCHAN, and PGRP that is an extra. - * The info is obtained from the following fields of proc, mproc and fproc: - * ST - kernel status field, p_rts_flags; pm status field, mp_flags (R if - * p_rts_flags is 0; Z if mp_flags == ZOMBIE; T if mp_flags == STOPPED; - * else W). - * UID - pm eff uid field, mp_effuid - * PID - pm pid field, mp_pid - * PPID - pm parent process index field, mp_parent (used as index in proc). - * PGRP - pm process group field, mp_procgrp - * SZ - memory size, including common and shared memory - * RECV - kernel process index field for message receiving, p_getfrom - * If sleeping, pm's mp_flags, or fs's fp_task are used for more info. - * TTY - fs controlling tty device field, fp_tty. - * TIME - kernel user + system times fields, user_time + sys_time - * CMD - system process index (converted to mnemonic name by using the p_name - * field), or user process argument list (obtained by reading the stack - * frame; the resulting address is used to get the argument vector from - * user space and converted into a concatenated argument list). - */ - -#define _MINIX_SYSTEM 1 - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -/*----- ps's local stuff below this line ------*/ - -/* Structure for tty name info. */ -typedef struct { - char tty_name[NAME_MAX + 1]; /* file name in /dev */ - dev_t tty_dev; /* major/minor pair */ -} ttyinfo_t; - -ttyinfo_t *ttyinfo; /* ttyinfo holds actual tty info */ -size_t n_ttyinfo; /* Number of tty info slots */ - -u32_t system_hz; /* system clock frequency */ -unsigned int nr_procs; /* maximum number of processes */ -unsigned int nr_tasks; /* maximum number of tasks */ - -struct pstat *ptable; /* table with process information */ - -/* Macro to convert endpoints to slots into ptable */ -#define SLOT_NR(e) (_ENDPOINT_P(e) + nr_tasks) - -/* Macro to convert memory offsets to rounded kilo-units */ -#define off_to_k(off) ((unsigned) (((off) + 512) / 1024)) - - -/* Short and long listing formats: - * - * PID TTY TIME CMD - * ppppp tttmmm:ss cccccccccc... - * - * ST UID PID PPID PGRP SZ RECV TTY TIME CMD - * s uuu ppppp ppppp ppppp ssss rrrrrrrrrr tttmmm:ss cccccccc... - */ -#define S_HEADER " PID TTY TIME CMD\n" -#define S_FORMAT "%5s %3s %s %s\n" -#define L_HEADER "ST UID PID PPID PGRP SZ RECV TTY TIME CMD\n" -#define L_FORMAT " %c %3d %5s %5d %5d %6d %12s %3s %s %s\n" - - -struct pstat { /* structure filled by pstat() */ - struct pstat *ps_next; /* next in process list */ - int ps_task; /* is this process a task or not? */ - int ps_endpt; /* process endpoint (NONE means unused slot) */ - dev_t ps_dev; /* major/minor of controlling tty */ - uid_t ps_ruid; /* real uid */ - uid_t ps_euid; /* effective uid */ - pid_t ps_pid; /* process id */ - pid_t ps_ppid; /* parent process id */ - int ps_pgrp; /* process group id */ - char ps_state; /* process state */ - char ps_pstate; /* sleep state */ - char ps_fstate; /* VFS block state */ - int ps_ftask; /* VFS suspend task (endpoint) */ - vir_bytes ps_memory; /* memory usage */ - int ps_recv; /* process number to receive from (endpoint) */ - unsigned int ps_utime; /* accumulated user time */ - unsigned int ps_stime; /* accumulated system time */ - char ps_name[PROC_NAME_LEN+1];/* process name */ - char *ps_args; /* concatenated argument string */ -}; - -int main(int argc, char *argv []); -void plist(void); -int addrread(int fd, phys_clicks base, vir_bytes addr, char *buf, int - nbytes ); -void usage(const char *pname ); -void err(const char *s ); -int gettynames(void); - - -/* - * Tname returns mnemonic string for dev_nr. This is "?" for maj/min pairs that - * are not found. It uses the ttyinfo array (prepared by gettynames). - * Tname assumes that the first three letters of the tty's name can be omitted - * and returns the rest (except for the console, which yields "co"). - */ -static char *tname(dev_t dev_nr) -{ - unsigned int i; - - if (major(dev_nr) == TTY_MAJOR && minor(dev_nr) == 0) return "co"; - - for (i = 0; i < n_ttyinfo && ttyinfo[i].tty_name[0] != '\0'; i++) - if (ttyinfo[i].tty_dev == dev_nr) - return ttyinfo[i].tty_name + 3; - - return "?"; -} - -/* Find a task by its endpoint. */ -static struct pstat *findtask(endpoint_t endpt) -{ - struct pstat *ps; - unsigned int slot; - - slot = SLOT_NR(endpt); - - if (slot >= nr_tasks + nr_procs) - return NULL; - - ps = &ptable[slot]; - - if (ps != NULL && ps->ps_endpt == (int) endpt) - return ps; - - return NULL; -} - -/* Return canonical task name of the given endpoint. */ -static char *taskname(endpoint_t endpt) -{ - struct pstat *ps; - - ps = findtask(endpt); - - return ps ? ps->ps_name : "???"; -} - -/* Prrecv prints the RECV field for process with pstat buffer pointer ps. - * This is either "ANY", "taskname", or "(blockreason) taskname". - */ -static char *prrecv(struct pstat *ps) -{ - char *blkstr, *task; /* reason for blocking and task */ - static char recvstr[20]; - - if (ps->ps_recv == ANY) return "ANY"; - - task = taskname(ps->ps_recv); - if (ps->ps_state != STATE_SLEEP) return task; - - blkstr = "?"; - if (ps->ps_recv == PM_PROC_NR) { - switch (ps->ps_pstate) { - case PSTATE_WAITING: blkstr = "wait"; break; - case PSTATE_SIGSUSP: blkstr = "sigsusp"; break; - } - } else if (ps->ps_recv == VFS_PROC_NR) { - switch (ps->ps_fstate) { - case FSTATE_PIPE: blkstr = "pipe"; break; - case FSTATE_LOCK: blkstr = "flock"; break; - case FSTATE_POPEN: blkstr = "popen"; break; - case FSTATE_SELECT: blkstr = "select"; break; - case FSTATE_TASK: blkstr = taskname(ps->ps_ftask); break; - default: blkstr = "??"; break; - } - } - (void) sprintf(recvstr, "(%s) %s", blkstr, task); - return recvstr; -} - -static void getkinfo(void) -{ - FILE *fp; - - if ((fp = fopen("kinfo", "r")) == NULL) - err("Unable to open " _PATH_PROC "kinfo"); - - if (fscanf(fp, "%u %u", &nr_procs, &nr_tasks) != 2) - err("Unable to read from " _PATH_PROC "kinfo"); - - fclose(fp); -} - -/* Main interprets arguments, gathers information, and prints a process list. - */ -int main(argc, argv) -int argc; -char *argv[]; -{ - int i; - unsigned int n; - struct pstat *ps; - int uid = getuid(); /* real uid of caller */ - char *opt; - int opt_all = FALSE; /* -a */ - int opt_long = FALSE; /* -l */ - int opt_notty = FALSE; /* -x */ - int opt_endpoint = FALSE; /* -E */ - char pid[2 + sizeof(pid_t) * 3]; - unsigned long ustime; - char cpu[sizeof(clock_t) * 3 + 1 + 2]; - - /* Parse arguments; a '-' need not be present (V7/BSD compatability) */ - for (i = 1; i < argc; i++) { - opt = argv[i]; - if (opt[0] == '-') opt++; - while (*opt != 0) switch (*opt++) { - case 'a': opt_all = TRUE; break; - case 'E': opt_endpoint = TRUE; break; - case 'e': opt_all = opt_notty = TRUE; break; - case 'f': - case 'l': opt_long = TRUE; break; - case 'x': opt_notty = TRUE; break; - default: usage(argv[0]); - } - } - - if (gettynames() == -1) err("Can't get tty names"); - - if (chdir(_PATH_PROC) != 0) err("Can't chdir to /proc"); - - /* Get information from the proc file system */ - system_hz = (u32_t) sysconf(_SC_CLK_TCK); - - getkinfo(); - - plist(); - - /* Now loop through process table and handle each entry */ - printf("%s", opt_long ? L_HEADER : S_HEADER); - for (n = 0; n < nr_procs + nr_tasks; n++) { - ps = &ptable[n]; - if (ps->ps_endpt == NONE) - continue; - - if ((opt_all || ps->ps_euid == uid || ps->ps_ruid == uid) && - (opt_notty || major(ps->ps_dev) == TTY_MAJOR)) { - if (ps->ps_task) { - sprintf(pid, "(%d)", ps->ps_pid); - } else { - sprintf(pid, "%d", - opt_endpoint ? ps->ps_endpt : ps->ps_pid); - } - - ustime = (ps->ps_utime + ps->ps_stime) / system_hz; - if (ustime < 60 * 60) { - sprintf(cpu, "%2lu:%02lu", ustime / 60, ustime % 60); - } else - if (ustime < 100L * 60 * 60) { - ustime /= 60; - sprintf(cpu, "%2luh%02lu", ustime / 60, ustime % 60); - } else { - sprintf(cpu, "%4luh", ustime / 3600); - } - - if (opt_long) printf(L_FORMAT, - ps->ps_state, - ps->ps_euid, pid, ps->ps_ppid, - ps->ps_pgrp, - off_to_k(ps->ps_memory), - (ps->ps_recv != NONE ? prrecv(ps) : ""), - tname((dev_t) ps->ps_dev), - cpu, - ps->ps_args != NULL ? ps->ps_args : ps->ps_name - ); - else - printf(S_FORMAT, - pid, tname((dev_t) ps->ps_dev), - cpu, - ps->ps_args != NULL ? ps->ps_args : ps->ps_name - ); - } - } - return(0); -} - -/* Get_args obtains the command line of a process. */ -char *get_args(struct pstat *ps) -{ - char path[PATH_MAX], buf[4096]; - ssize_t i, n; - int fd; - - /* Get a reasonable subset of the contents of the 'cmdline' file from procfs. - * It contains all arguments, separated and terminated by null characters. - */ - sprintf(path, "%d/cmdline", ps->ps_pid); - - fd = open(path, O_RDONLY); - if (fd < 0) return NULL; - - n = read(fd, buf, sizeof(buf)); - if (n <= 0) { - close(fd); - - return NULL; - } - - close(fd); - - /* Replace all argument separating null characters with spaces. */ - for (i = 0; i < n-1; i++) - if (buf[i] == '\0') - buf[i] = ' '; - - /* The last character should already be null, except if it got cut off. */ - buf[n-1] = '\0'; - - return strdup(buf); -} - -/* Pstat obtains the actual information for the given process, and stores it - * in the pstat structure. The outside world may change while we are doing - * this, so nothing is reported in case any of the calls fail. - */ -int pstat(struct pstat *ps, pid_t pid) -{ - FILE *fp; - int version, ruid, euid, dev; - char type, path[PATH_MAX], name[256]; - - ps->ps_pid = pid; - ps->ps_next = NULL; - - sprintf(path, "%d/psinfo", pid); - - if ((fp = fopen(path, "r")) == NULL) - return -1; - - if (fscanf(fp, "%d", &version) != 1) { - fclose(fp); - return -1; - } - - /* The psinfo file's version must match what we expect. */ - if (version != PSINFO_VERSION) { - fputs("procfs version mismatch!\n", stderr); - exit(1); - } - - if (fscanf(fp, " %c %d %255s %c %d %*d %u %u %*u %*u", - &type, &ps->ps_endpt, name, &ps->ps_state, - &ps->ps_recv, &ps->ps_utime, &ps->ps_stime) != 7) { - - fclose(fp); - return -1; - } - - strncpy(ps->ps_name, name, sizeof(ps->ps_name)-1); - ps->ps_name[sizeof(ps->ps_name)-1] = 0; - - ps->ps_task = type == TYPE_TASK; - - if (!ps->ps_task) { - if (fscanf(fp, " %lu %*u %*u %c %d %u %u %u %*d %c %d %u", - &ps->ps_memory, &ps->ps_pstate, &ps->ps_ppid, - &ruid, &euid, &ps->ps_pgrp, &ps->ps_fstate, - &ps->ps_ftask, &dev) != 9) { - - fclose(fp); - return -1; - } - - ps->ps_ruid = ruid; - ps->ps_euid = euid; - ps->ps_dev = dev; - } else { - ps->ps_memory = 0L; - ps->ps_pstate = PSTATE_NONE; - ps->ps_ppid = 0; - ps->ps_ruid = 0; - ps->ps_euid = 0; - ps->ps_pgrp = 0; - ps->ps_fstate = FSTATE_NONE; - ps->ps_ftask = NONE; - ps->ps_dev = NO_DEV; - } - - fclose(fp); - - if (ps->ps_state == STATE_ZOMBIE) - ps->ps_args = ""; - else if (!ps->ps_task) - ps->ps_args = get_args(ps); - else - ps->ps_args = NULL; - - return 0; -} - -/* Plist creates a list of processes with status information. */ -void plist(void) -{ - DIR *p_dir; - struct dirent *p_ent; - struct pstat pbuf; - pid_t pid; - char *end; - unsigned int slot; - - /* Allocate a table for process information. Initialize all slots' endpoints - * to NONE, indicating those slots are not used. - */ - if ((ptable = malloc((nr_tasks + nr_procs) * sizeof(struct pstat))) == NULL) - err("Out of memory!"); - - for (slot = 0; slot < nr_tasks + nr_procs; slot++) - ptable[slot].ps_endpt = NONE; - - /* Fill in the table slots for all existing processes, by retrieving all PID - * entries from the /proc directory. - */ - p_dir = opendir("."); - - if (p_dir == NULL) err("Can't open " _PATH_PROC); - - p_ent = readdir(p_dir); - while (p_ent != NULL) { - pid = strtol(p_ent->d_name, &end, 10); - - if (!end[0] && pid != 0 && !pstat(&pbuf, pid)) { - slot = SLOT_NR(pbuf.ps_endpt); - - if (slot < nr_tasks + nr_procs) - memcpy(&ptable[slot], &pbuf, sizeof(pbuf)); - } - - p_ent = readdir(p_dir); - } - - closedir(p_dir); -} - -void usage(const char *pname) -{ - fprintf(stderr, "Usage: %s [-][aeflx]\n", pname); - exit(1); -} - -void err(const char *s) -{ - extern int errno; - - if (errno == 0) - fprintf(stderr, "ps: %s\n", s); - else - fprintf(stderr, "ps: %s: %s\n", s, strerror(errno)); - - exit(2); -} - -/* Fill ttyinfo by fstatting character specials in /dev. */ -int gettynames(void) -{ - static char dev_path[] = "/dev/"; - struct stat statbuf; - static char path[sizeof(dev_path) + NAME_MAX]; - unsigned int index; - struct ttyent *ttyp; - - index = 0; - while ((ttyp = getttyent()) != NULL) { - strcpy(path, dev_path); - strcat(path, ttyp->ty_name); - if (stat(path, &statbuf) == -1 || !S_ISCHR(statbuf.st_mode)) - continue; - if (index >= n_ttyinfo) { - n_ttyinfo= (index+16) * 2; - ttyinfo = realloc(ttyinfo, n_ttyinfo * sizeof(ttyinfo[0])); - if (ttyinfo == NULL) err("Out of memory"); - } - ttyinfo[index].tty_dev = statbuf.st_rdev; - strcpy(ttyinfo[index].tty_name, ttyp->ty_name); - index++; - } - endttyent(); - while (index < n_ttyinfo) ttyinfo[index++].tty_dev= 0; - - return 0; -}