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; -}