In -f mode group & display parent/child process relationships using ASCII art.
Borrows heavily from Brian Somers' work on FreeBSD ps(1).
With input from deraadt@ and tb@
OK benno@ claudio@
-/* $OpenBSD: extern.h,v 1.23 2022/01/05 04:10:36 guenther Exp $ */
+/* $OpenBSD: extern.h,v 1.24 2022/09/01 21:15:54 job Exp $ */
/* $NetBSD: extern.h,v 1.10 1995/05/21 13:38:27 mycroft Exp $ */
/*-
extern VARENT *vhead;
__BEGIN_DECLS
-void command(const struct kinfo_proc *, VARENT *);
-void cputime(const struct kinfo_proc *, VARENT *);
+void command(const struct pinfo *, VARENT *);
+void cputime(const struct pinfo *, VARENT *);
int donlist(void);
-void elapsed(const struct kinfo_proc *, VARENT *);
+void elapsed(const struct pinfo *, VARENT *);
double getpcpu(const struct kinfo_proc *);
double getpmem(const struct kinfo_proc *);
-void gname(const struct kinfo_proc *, VARENT *);
-void supgid(const struct kinfo_proc *, VARENT *);
-void supgrp(const struct kinfo_proc *, VARENT *);
-void logname(const struct kinfo_proc *, VARENT *);
-void longtname(const struct kinfo_proc *, VARENT *);
-void lstarted(const struct kinfo_proc *, VARENT *);
-void maxrss(const struct kinfo_proc *, VARENT *);
+void gname(const struct pinfo *, VARENT *);
+void supgid(const struct pinfo *, VARENT *);
+void supgrp(const struct pinfo *, VARENT *);
+void logname(const struct pinfo *, VARENT *);
+void longtname(const struct pinfo *, VARENT *);
+void lstarted(const struct pinfo *, VARENT *);
+void maxrss(const struct pinfo *, VARENT *);
void nlisterr(struct nlist *);
-void p_rssize(const struct kinfo_proc *, VARENT *);
-void pagein(const struct kinfo_proc *, VARENT *);
+void p_rssize(const struct pinfo *, VARENT *);
+void pagein(const struct pinfo *, VARENT *);
void parsefmt(char *);
-void pcpu(const struct kinfo_proc *, VARENT *);
-void pmem(const struct kinfo_proc *, VARENT *);
-void pri(const struct kinfo_proc *, VARENT *);
+void pcpu(const struct pinfo *, VARENT *);
+void pmem(const struct pinfo *, VARENT *);
+void pri(const struct pinfo *, VARENT *);
void printheader(void);
-void pvar(const struct kinfo_proc *kp, VARENT *);
-void pnice(const struct kinfo_proc *kp, VARENT *);
-void rgname(const struct kinfo_proc *, VARENT *);
-void rssize(const struct kinfo_proc *, VARENT *);
-void runame(const struct kinfo_proc *, VARENT *);
+void pvar(const struct pinfo *, VARENT *);
+void pnice(const struct pinfo *, VARENT *);
+void rgname(const struct pinfo *, VARENT *);
+void rssize(const struct pinfo *, VARENT *);
+void runame(const struct pinfo *, VARENT *);
void showkey(void);
-void started(const struct kinfo_proc *, VARENT *);
-void printstate(const struct kinfo_proc *, VARENT *);
-void printpledge(const struct kinfo_proc *, VARENT *);
-void tdev(const struct kinfo_proc *, VARENT *);
-void tname(const struct kinfo_proc *, VARENT *);
-void tsize(const struct kinfo_proc *, VARENT *);
-void dsize(const struct kinfo_proc *, VARENT *);
-void ssize(const struct kinfo_proc *, VARENT *);
-void ucomm(const struct kinfo_proc *, VARENT *);
-void curwd(const struct kinfo_proc *, VARENT *);
-void euname(const struct kinfo_proc *, VARENT *);
-void vsize(const struct kinfo_proc *, VARENT *);
-void wchan(const struct kinfo_proc *, VARENT *);
+void started(const struct pinfo *, VARENT *);
+void printstate(const struct pinfo *, VARENT *);
+void printpledge(const struct pinfo *, VARENT *);
+void tdev(const struct pinfo *, VARENT *);
+void tname(const struct pinfo *, VARENT *);
+void tsize(const struct pinfo *, VARENT *);
+void dsize(const struct pinfo *, VARENT *);
+void ssize(const struct pinfo *, VARENT *);
+void ucomm(const struct pinfo *, VARENT *);
+void curwd(const struct pinfo *, VARENT *);
+void euname(const struct pinfo *, VARENT *);
+void vsize(const struct pinfo *, VARENT *);
+void wchan(const struct pinfo *, VARENT *);
__END_DECLS
-/* $OpenBSD: print.c,v 1.82 2022/02/15 23:16:00 rob Exp $ */
+/* $OpenBSD: print.c,v 1.83 2022/09/01 21:15:54 job Exp $ */
/* $NetBSD: print.c,v 1.27 1995/09/29 21:58:12 cgd Exp $ */
/*-
}
void
-command(const struct kinfo_proc *kp, VARENT *ve)
+command(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
int left, wantspace = 0;
char **p;
}
if (needcomm) {
+ if (pi->prefix)
+ mbswprint(pi->prefix, left, 0);
if (!commandonly) {
char **argv = NULL;
}
void
-ucomm(const struct kinfo_proc *kp, VARENT *ve)
+ucomm(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
mbswprint(kp->p_comm, ve->var->width, ve->next != NULL);
}
void
-curwd(const struct kinfo_proc *kp, VARENT *ve)
+curwd(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
int name[] = { CTL_KERN, KERN_PROC_CWD, kp->p_pid };
char path[PATH_MAX];
size_t pathlen = sizeof path;
}
void
-logname(const struct kinfo_proc *kp, VARENT *ve)
+logname(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
#define pgtok(a) (((unsigned long long)(a)*getpagesize())/1024)
void
-printstate(const struct kinfo_proc *kp, VARENT *ve)
+printstate(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
int flag;
char *cp, state = '\0';
VAR *v;
}
void
-printpledge(const struct kinfo_proc *kp, VARENT *ve)
+printpledge(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
int i;
VAR *v;
char buf[1024];
}
void
-pri(const struct kinfo_proc *kp, VARENT *ve)
+pri(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
}
void
-pnice(const struct kinfo_proc *kp, VARENT *ve)
+pnice(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
+
v = ve->var;
(void)printf("%*d", v->width, kp->p_nice - NZERO);
}
void
-euname(const struct kinfo_proc *kp, VARENT *ve)
+euname(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
+
mbswprint(user_from_uid(kp->p_uid, 0), ve->var->width,
ve->next != NULL);
}
void
-runame(const struct kinfo_proc *kp, VARENT *ve)
+runame(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
+
mbswprint(user_from_uid(kp->p_ruid, 0), ve->var->width,
ve->next != NULL);
}
void
-gname(const struct kinfo_proc *kp, VARENT *ve)
+gname(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
+
mbswprint(group_from_gid(kp->p_gid, 0), ve->var->width,
ve->next != NULL);
}
void
-rgname(const struct kinfo_proc *kp, VARENT *ve)
+rgname(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
+
mbswprint(group_from_gid(kp->p_rgid, 0), ve->var->width,
ve->next != NULL);
}
void
-supgid(const struct kinfo_proc *kp, VARENT *ve)
+supgid(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
char buf[1024];
char *p = buf;
ssize_t size = sizeof(buf);
}
void
-supgrp(const struct kinfo_proc *kp, VARENT *ve)
+supgrp(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
char buf[1024];
char *p = buf;
ssize_t size = sizeof(buf);
}
void
-tdev(const struct kinfo_proc *kp, VARENT *ve)
+tdev(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
dev_t dev;
}
void
-tname(const struct kinfo_proc *kp, VARENT *ve)
+tname(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
dev_t dev;
char *ttname;
}
void
-longtname(const struct kinfo_proc *kp, VARENT *ve)
+longtname(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
dev_t dev;
char *ttname;
}
void
-started(const struct kinfo_proc *kp, VARENT *ve)
+started(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
static time_t now;
time_t startt;
}
void
-lstarted(const struct kinfo_proc *kp, VARENT *ve)
+lstarted(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
time_t startt;
char buf[100];
(void)printf("%-*s", v->width, buf);
}
-void elapsed(const struct kinfo_proc *kp, VARENT *ve)
+void elapsed(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
static time_t now;
time_t secs;
}
void
-wchan(const struct kinfo_proc *kp, VARENT *ve)
+wchan(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
}
void
-vsize(const struct kinfo_proc *kp, VARENT *ve)
+vsize(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
}
void
-rssize(const struct kinfo_proc *kp, VARENT *ve)
+rssize(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
}
void
-p_rssize(const struct kinfo_proc *kp, VARENT *ve)
+p_rssize(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
}
void
-cputime(const struct kinfo_proc *kp, VARENT *ve)
+cputime(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
long secs;
long psecs; /* "parts" of a second. first micro, then centi */
}
void
-pcpu(const struct kinfo_proc *kp, VARENT *ve)
+pcpu(const struct pinfo *pi, VARENT *ve)
{
VAR *v;
v = ve->var;
- (void)printf("%*.1f", v->width, getpcpu(kp));
+ (void)printf("%*.1f", v->width, getpcpu(pi->ki));
}
double
}
void
-pmem(const struct kinfo_proc *kp, VARENT *ve)
+pmem(const struct pinfo *pi, VARENT *ve)
{
VAR *v;
v = ve->var;
- (void)printf("%*.1f", v->width, getpmem(kp));
+ (void)printf("%*.1f", v->width, getpmem(pi->ki));
}
void
-pagein(const struct kinfo_proc *kp, VARENT *ve)
+pagein(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
}
void
-maxrss(const struct kinfo_proc *kp, VARENT *ve)
+maxrss(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
}
void
-tsize(const struct kinfo_proc *kp, VARENT *ve)
+tsize(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
}
void
-dsize(const struct kinfo_proc *kp, VARENT *ve)
+dsize(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
}
void
-ssize(const struct kinfo_proc *kp, VARENT *ve)
+ssize(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
}
void
-pvar(const struct kinfo_proc *kp, VARENT *ve)
+pvar(const struct pinfo *pi, VARENT *ve)
{
+ const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
-.\" $OpenBSD: ps.1,v 1.126 2022/07/05 15:06:16 visa Exp $
+.\" $OpenBSD: ps.1,v 1.127 2022/09/01 21:15:54 job Exp $
.\" $NetBSD: ps.1,v 1.16 1996/03/21 01:36:28 jtc Exp $
.\"
.\" Copyright (c) 1980, 1990, 1991, 1993, 1994
.\"
.\" @(#)ps.1 8.3 (Berkeley) 4/18/94
.\"
-.Dd $Mdocdate: July 5 2022 $
+.Dd $Mdocdate: September 1 2022 $
.Dt PS 1
.Os
.Sh NAME
.Sh SYNOPSIS
.Nm ps
.Sm off
-.Op Oo Fl Oc Cm AaceHhjkLlmrSTuvwx
+.Op Oo Fl Oc Cm AacefHhjkLlmrSTuvwx
.Sm on
.Op Fl M Ar core
.Op Fl N Ar system
.Dq sh .
.It Fl e
Display the environment as well.
+.It Fl f
+Arrange processes into a tree, order and prefix each command with
+indentation text showing sibling and parent/child relationships.
+If either of the
+.Fl m
+and
+.Fl r
+options are also used, they control how sibling processes are sorted relative
+to each other.
.It Fl H
Also display information about kernel visible threads.
.It Fl h
.St -p1003.1-2008 .
.Pp
The flags
-.Op Fl cHhjkLMmNOrSTvWwx
+.Op Fl cfHhjkLMmNOrSTvWwx
are extensions to
.St -p1003.1-2008 .
.Pp
-/* $OpenBSD: ps.c,v 1.78 2021/12/01 18:21:23 deraadt Exp $ */
+/* $OpenBSD: ps.c,v 1.79 2022/09/01 21:15:54 job Exp $ */
/* $NetBSD: ps.c,v 1.15 1995/05/18 20:33:25 mycroft Exp $ */
/*-
static char *kludge_oldps_options(char *);
static int pscomp(const void *, const void *);
static void scanvars(void);
+static void forest_sort(struct pinfo *, int);
static void usage(void);
char dfmt[] = "pid tt state time command";
int
main(int argc, char *argv[])
{
- struct kinfo_proc *kp, **kinfo;
+ struct kinfo_proc *kp;
+ struct pinfo *pinfo;
struct varent *vent;
struct winsize ws;
dev_t ttydev;
uid_t uid;
int all, ch, flag, i, fmt, lineno, nentries;
int prtheader, showthreads, wflag, kflag, what, Uflag, xflg;
+ int forest;
char *nlistf, *memf, *swapf, *cols, errbuf[_POSIX2_LINE_MAX];
setlocale(LC_CTYPE, "");
all = fmt = prtheader = showthreads = wflag = kflag = Uflag = xflg = 0;
pid = -1;
uid = 0;
+ forest = 0;
ttydev = NODEV;
memf = nlistf = swapf = NULL;
while ((ch = getopt(argc, argv,
- "AaCcegHhjkLlM:mN:O:o:p:rSTt:U:uvW:wx")) != -1)
+ "AaCcefgHhjkLlM:mN:O:o:p:rSTt:U:uvW:wx")) != -1)
switch (ch) {
case 'A':
all = 1;
case 'e': /* XXX set ufmt */
needenv = 1;
break;
+ case 'f':
+ forest = 1;
+ break;
case 'g':
break; /* no-op */
case 'H':
printheader();
if (nentries == 0)
exit(1);
- /*
- * sort proc list, we convert from an array of structs to an array
- * of pointers to make the sort cheaper.
- */
- if ((kinfo = reallocarray(NULL, nentries, sizeof(*kinfo))) == NULL)
- err(1, "failed to allocate memory for proc pointers");
+
+ if ((pinfo = calloc(nentries, sizeof(struct pinfo))) == NULL)
+ err(1, NULL);
for (i = 0; i < nentries; i++)
- kinfo[i] = &kp[i];
- qsort(kinfo, nentries, sizeof(*kinfo), pscomp);
+ pinfo[i].ki = &kp[i];
+ qsort(pinfo, nentries, sizeof(struct pinfo), pscomp);
+
+ if (forest)
+ forest_sort(pinfo, nentries);
+
/*
* for each proc, call each variable output function.
*/
for (i = lineno = 0; i < nentries; i++) {
- if (xflg == 0 && ((int)kinfo[i]->p_tdev == NODEV ||
- (kinfo[i]->p_psflags & PS_CONTROLT ) == 0))
+ if (xflg == 0 && ((int)pinfo[i].ki->p_tdev == NODEV ||
+ (pinfo[i].ki->p_psflags & PS_CONTROLT ) == 0))
continue;
- if (showthreads && kinfo[i]->p_tid == -1)
+ if (showthreads && pinfo[i].ki->p_tid == -1)
continue;
for (vent = vhead; vent; vent = vent->next) {
- (vent->var->oproc)(kinfo[i], vent);
+ (vent->var->oproc)(&pinfo[i], vent);
if (vent->next != NULL)
(void)putchar(' ');
}
static int
pscomp(const void *v1, const void *v2)
{
- const struct kinfo_proc *kp1 = *(const struct kinfo_proc **)v1;
- const struct kinfo_proc *kp2 = *(const struct kinfo_proc **)v2;
+ const struct pinfo *p1 = (const struct pinfo *)v1;
+ const struct pinfo *p2 = (const struct pinfo *)v2;
+ const struct kinfo_proc *kp1 = p1->ki;
+ const struct kinfo_proc *kp2 = p2->ki;
int i;
#define VSIZE(k) ((k)->p_vm_dsize + (k)->p_vm_ssize + (k)->p_vm_tsize)
return (newopts);
}
+static void
+forest_sort(struct pinfo *ki, int items)
+{
+ int dst, lvl, maxlvl, n, ndst, nsrc, siblings, src;
+ unsigned char *path;
+ struct pinfo kn;
+
+ /*
+ * First, sort the entries by forest, tracking the forest
+ * depth in the level field.
+ */
+ src = 0;
+ maxlvl = 0;
+ while (src < items) {
+ if (ki[src].level) {
+ src++;
+ continue;
+ }
+ for (nsrc = 1; src + nsrc < items; nsrc++)
+ if (!ki[src + nsrc].level)
+ break;
+
+ for (dst = 0; dst < items; dst++) {
+ if (ki[dst].ki->p_pid == ki[src].ki->p_pid)
+ continue;
+ if (ki[dst].ki->p_pid == ki[src].ki->p_ppid)
+ break;
+ }
+
+ if (dst == items) {
+ src += nsrc;
+ continue;
+ }
+
+ for (ndst = 1; dst + ndst < items; ndst++)
+ if (ki[dst + ndst].level <= ki[dst].level)
+ break;
+
+ for (n = src; n < src + nsrc; n++) {
+ ki[n].level += ki[dst].level + 1;
+ if (maxlvl < ki[n].level)
+ maxlvl = ki[n].level;
+ }
+
+ while (nsrc) {
+ if (src < dst) {
+ kn = ki[src];
+ memmove(ki + src, ki + src + 1,
+ (dst - src + ndst - 1) * sizeof *ki);
+ ki[dst + ndst - 1] = kn;
+ nsrc--;
+ dst--;
+ ndst++;
+ } else if (src != dst + ndst) {
+ kn = ki[src];
+ memmove(ki + dst + ndst + 1, ki + dst + ndst,
+ (src - dst - ndst) * sizeof *ki);
+ ki[dst + ndst] = kn;
+ ndst++;
+ nsrc--;
+ src++;
+ } else {
+ ndst += nsrc;
+ src += nsrc;
+ nsrc = 0;
+ }
+ }
+ }
+ /*
+ * Now populate prefix (instead of level) with the command
+ * prefix used to show descendancies.
+ */
+ path = calloc(1, (maxlvl + 7) / 8);
+ if (path == NULL)
+ err(1, NULL);
+
+ for (src = 0; src < items; src++) {
+ if ((lvl = ki[src].level) == 0) {
+ ki[src].prefix = NULL;
+ continue;
+ }
+
+ if ((ki[src].prefix = malloc(lvl * 2 + 1)) == NULL)
+ err(1, NULL);
+
+ for (n = 0; n < lvl - 2; n++) {
+ ki[src].prefix[n * 2] =
+ path[n / 8] & 1 << (n % 8) ? '|' : ' ';
+ ki[src].prefix[n * 2 + 1] = ' ';
+
+ }
+ if (n == lvl - 2) {
+ /* Have I any more siblings? */
+ for (siblings = 0, dst = src + 1; dst < items; dst++) {
+ if (ki[dst].level > lvl)
+ continue;
+ if (ki[dst].level == lvl)
+ siblings = 1;
+ break;
+ }
+ if (siblings)
+ path[n / 8] |= 1 << (n % 8);
+ else
+ path[n / 8] &= ~(1 << (n % 8));
+ ki[src].prefix[n * 2] = siblings ? '|' : '`';
+ ki[src].prefix[n * 2 + 1] = '-';
+ n++;
+ }
+ strlcpy(ki[src].prefix + n * 2, "- ", (lvl - n) * 2 + 1);
+ }
+ free(path);
+}
+
static void
usage(void)
{
- (void)fprintf(stderr,
- "usage: %s [-AaceHhjkLlmrSTuvwx] [-M core] [-N system] [-O fmt] [-o fmt] [-p pid]\n",
- __progname);
- (void)fprintf(stderr,
- "%-*s[-t tty] [-U username] [-W swap]\n", (int)strlen(__progname) + 8, "");
+ fprintf(stderr, "usage: %s [-AacefHhjkLlmrSTuvwx] [-M core] [-N system]"
+ " [-O fmt] [-o fmt] [-p pid]\n", __progname);
+ fprintf(stderr, "%-*s[-t tty] [-U username] [-W swap]\n",
+ (int)strlen(__progname) + 8, "");
exit(1);
}
-/* $OpenBSD: ps.h,v 1.10 2015/05/03 06:23:28 guenther Exp $ */
+/* $OpenBSD: ps.h,v 1.11 2022/09/01 21:15:54 job Exp $ */
/* $NetBSD: ps.h,v 1.11 1995/09/29 21:57:03 cgd Exp $ */
/*-
} VARENT;
struct kinfo_proc;
+struct pinfo {
+ struct kinfo_proc *ki;
+ char *prefix;
+ int level;
+};
typedef struct var {
char *name; /* name(s) of variable */
char *header; /* default header */
#define NLIST 0x10 /* needs nlist info from kernel */
u_int flag;
/* output routine */
- void (*oproc)(const struct kinfo_proc *, struct varent *);
+ void (*oproc)(const struct pinfo *, struct varent *);
short width; /* printing width */
char parsed; /* have we been parsed yet? (avoid dupes) */
/*