fix botch
[unix-history] / usr / src / bin / ps / ps.c
/*-
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* %sccs.include.redist.c%
*/
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1990 The Regents of the University of California.\n\
All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static char sccsid[] = "@(#)ps.c 5.28 (Berkeley) %G%";
#endif /* not lint */
#include <machine/pte.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/vm.h>
#include <sys/text.h>
#include <sys/stat.h>
#include <sys/mbuf.h>
#include <nlist.h>
#include <pwd.h>
#include <math.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
#include <kvm.h>
struct usave {
struct proc *u_procp;
struct timeval u_start;
struct rusage u_ru;
struct rusage u_cru;
short u_cmask;
char u_acflag;
};
/*
* to compute offset in common structures
*/
#define POFF(x) ((int)&((struct proc *)0)->x)
#define EOFF(x) ((int)&((struct eproc *)0)->x)
#define UOFF(x) ((int)&((struct usave *)0)->x)
#define ROFF(x) ((int)&((struct rusage *)0)->x)
enum type { CHAR, UCHAR, SHORT, USHORT, LONG, ULONG, KPTR };
#define UIDFMT "u"
#define UIDLEN 5
#define PIDFMT "d"
#define PIDLEN 5
#define USERLEN 8
int needuser, needcomm, neednlist;
int command(), ucomm(), logname(), pvar(), evar(), uvar(), rvar(), uname(),
runame(), state(), pri(), tdev(), tname(), longtname(), started(),
lstarted(), wchan(), vsize(), rssize(), p_rssize(), cputime(),
pmem(), pcpu(), pagein(), maxrss(), tsize(), trss();
/**
utime(), stime(), ixrss(), idrss(), isrss();
**/
struct usave *saveuser();
char *saveargs();
struct var {
char *name[8]; /* name(s) of variable */
char *header; /* default header */
int flag;
#define USER 0x01 /* requires user structure */
#define LJUST 0x02 /* right adjust on output */
#define COMM 0x04 /* requires exec arguments and environment (XXX) */
#define NLIST 0x08 /* requires nlist to get extra variables */
int (*oproc)(); /* output routine */
short width; /* printing width */
/*
* The following (optional) elements are hooks for passing information
* to the generic output routines: pvar, evar, uvar (those which print
* simple elements from well known structures: proc, eproc, usave)
*/
int off; /* offset in structure */
enum type type; /* type of element */
char *fmt; /* printf format */
/*
* glue to link selected fields together
*/
struct var *next;
} var[] = {
{{"command", "comm", "args"}, "COMMAND", USER|LJUST|COMM,
command, 16},
{{"ucomm"}, "COMMAND", LJUST, ucomm, MAXCOMLEN},
{{"logname"}, "LOGNAME", LJUST, logname, MAXLOGNAME},
{{"flag", "f"}, "F", 0, pvar, 7, POFF(p_flag), LONG, "x"},
{{"uid"}, "UID", 0, pvar, UIDLEN, POFF(p_uid),USHORT, UIDFMT},
{{"ruid"}, "RUID", 0, pvar, UIDLEN, POFF(p_ruid), USHORT, UIDFMT},
{{"svuid"}, "SVUID", 0, pvar, UIDLEN, POFF(p_svuid), USHORT, UIDFMT},
{{"rgid"}, "RGID", 0, pvar, UIDLEN, POFF(p_rgid), USHORT, UIDFMT},
{{"svgid"}, "SVGID", 0, pvar, UIDLEN, POFF(p_svgid), USHORT, UIDFMT},
{{"pid"}, "PID", 0, pvar, PIDLEN, POFF(p_pid),SHORT, PIDFMT},
{{"ppid"}, "PPID", 0, pvar, PIDLEN, POFF(p_ppid), SHORT, PIDFMT},
{{"cp", "cpu"}, "CP", 0, pvar, 3, POFF(p_cpu), UCHAR, "d"},
{{"xstat"}, "XSTAT", 0, pvar, 4, POFF(p_xstat), USHORT, "x"},
{{"poip"}, "POIP", 0, pvar, 4, POFF(p_poip), SHORT, "d"},
{{"nwchan"}, "WCHAN", 0, pvar, 6, POFF(p_wchan), KPTR, "x"},
{{"wchan"}, "WCHAN", LJUST, wchan, 6},
{{"rlink"}, "RLINK", 0, pvar, 8, POFF(p_rlink), KPTR, "x"},
{{"ktrace", "traceflag"}, "KTRACE",
0, pvar, 8, POFF(p_traceflag), LONG, "x"},
{{"ktracep", "tracep"}, "KTRACEP",
0, pvar, 8, POFF(p_tracep), LONG, "x"},
{{"sig", "pending"}, "PENDING",
0, pvar, 8, POFF(p_sig), LONG, "x"},
{{"sigmask", "blocked"}, "BLOCKED",
0, pvar, 8, POFF(p_sigmask), LONG, "x"},
{{"sigignore", "ignored"}, "IGNORED",
0, pvar, 8, POFF(p_sigignore), LONG, "x"},
{{"sigcatch", "caught"}, "CAUGHT",
0, pvar, 8, POFF(p_sigcatch), LONG, "x"},
{{"user", "uname"}, "USER", LJUST, uname, USERLEN},
{{"ruser", "runame"}, "RUSER", LJUST, runame, USERLEN},
{{"pgid"}, "PGID", 0, evar, PIDLEN, EOFF(e_pgid), USHORT, PIDFMT},
{{"jobc"}, "JOBC", 0, evar, 4, EOFF(e_jobc), SHORT, "d"},
{{"sess", "session"}, "SESS", 0, evar, 6, EOFF(e_sess), KPTR, "x"},
{{"tdev", "dev"}, "TDEV", 0, tdev, 4},
{{"tname", "tty", "tt"}, "TT", LJUST, tname, 3},
{{"longtname", "longtty"}, "TT", LJUST, longtname, 8},
{{"tpgid"}, "TPGID", 0, evar, 4, EOFF(e_tpgid), USHORT, PIDFMT},
{{"tsession", "tsess"}, "TSESS",
0, evar, 6, EOFF(e_tsess), KPTR, "x"},
{{"paddr", "procaddr"}, "PADDR",
0, evar, 6, EOFF(e_paddr), KPTR, "x"},
{{"state", "stat"}, "STAT", 0, state, 4},
{{"pri"}, "PRI", 0, pri, 3},
{{"usrpri"}, "UPR", 0, pvar, 3, POFF(p_usrpri), CHAR, "d"},
{{"nice", "ni"}, "NI", 0, pvar, 2, POFF(p_nice), CHAR, "d"},
{{"vsize", "vsz"}, "VSZ", 0, vsize, 5},
{{"rssize", "rsz"}, "RSZ", 0, rssize, 4},
{{"rss", "p_rss"}, "RSS", 0, p_rssize, 4},
{{"u_procp", "uprocp"}, "UPROCP",
USER, uvar, 6, UOFF(u_procp), KPTR, "x"},
{{"umask", "u_cmask"}, "UMASK",
USER, uvar, 3, UOFF(u_cmask), CHAR, "#o"},
{{"acflag", "acflg"}, "ACFLG",
USER, uvar, 3, UOFF(u_acflag), SHORT, "x"},
{{"start"}, "STARTED", USER|LJUST, started, 8},
{{"lstart"}, "STARTED", USER|LJUST, lstarted, 28},
{{"cputime", "time"}, "TIME", USER, cputime, 9},
{{"p_ru"}, "P_RU", 0, pvar, 6, POFF(p_ru), KPTR, "x"},
{{"pcpu", "%cpu"}, "%CPU", NLIST, pcpu, 4},
{{"pmem", "%mem"}, "%MEM", NLIST, pmem, 4},
{{"sl", "slp", "slptime"}, "SL",
0, pvar, 3, POFF(p_slptime), CHAR, "d"},
{{"re", "resident"}, "RE",
0, pvar, 3, POFF(p_time), CHAR, "d"},
{{"pagein", "majflt"}, "PAGEIN", USER, pagein, 6},
{{"lim", "maxrss"}, "LIM", 0, maxrss, 5},
{{"tsiz"}, "TSIZ", 0, tsize, 4},
{{"trs"}, "TRS", 0, trss, 3},
/***
{{"utime"}, "UTIME", USER, utime, 4},
{{"stime"}, "STIME", USER, stime, 4},
{{"ixrss"}, "IXRSS", USER, ixrss, 4},
{{"idrss"}, "IDRSS", USER, idrss, 4},
{{"isrss"}, "ISRSS", USER, isrss, 4},
***/
{{"minflt"}, "MINFLT",
USER, rvar, 4, ROFF(ru_minflt), LONG, "d"},
{{"majflt"}, "MAJFLT",
USER, rvar, 4, ROFF(ru_majflt), LONG, "d"},
{{"nswap"}, "NSWAP",
USER, rvar, 4, ROFF(ru_nswap), LONG, "d"},
{{"inblock", "inblk"}, "INBLK",
USER, rvar, 4, ROFF(ru_inblock), LONG, "d"},
{{"oublock", "oublk"}, "OUBLK",
USER, rvar, 4, ROFF(ru_oublock), LONG, "d"},
{{"msgsnd"}, "MSGSND",
USER, rvar, 4, ROFF(ru_msgsnd), LONG, "d"},
{{"msgrcv"}, "MSGRCV",
USER, rvar, 4, ROFF(ru_msgrcv), LONG, "d"},
{{"nsignals", "nsigs"}, "NSIGS",
USER, rvar, 4, ROFF(ru_nsignals), LONG, "d"},
{{"nvcsw", "vcsw"}, "VCSW",
USER, rvar, 5, ROFF(ru_nvcsw), LONG, "d"},
{{"nivcsw", "ivcsw"}, "IVCSW",
USER, rvar, 5, ROFF(ru_nivcsw), LONG, "d"},
NULL
};
/*
* combination variables
*/
struct combovar {
char *name;
char *replace;
} combovar[] = {
"RUSAGE", "minflt majflt nswap inblock oublock \
msgsnd msgrcv nsigs nvcsw nivcsw",
0, 0
};
#define DFMT "pid tname state cputime comm"
#define LFMT \
"uid pid ppid cp pri nice vsz rss wchan state tname cputime comm"
#define JFMT "user pid ppid pgid sess jobc state tname cputime comm"
#define SFMT "uid pid sig sigmask sigignore sigcatch stat tname comm"
#define VFMT \
"pid tt state time sl re pagein vsz rss lim tsiz trs %cpu %mem comm"
#define UFMT \
"uname pid %cpu %mem vsz rss tt state start time comm"
struct kinfo {
struct proc *ki_p; /* proc structure */
struct eproc *ki_e; /* extra stuff */
struct usave *ki_u; /* interesting parts of user */
char *ki_args; /* exec args (should be char **) */
char *ki_env; /* environment (should be char **) */
} *kinfo;
struct var *vhead, *vtail;
int termwidth; /* width of screen (0 == infinity) */
#define UNLIMITED 0
int totwidth; /* calculated width of requested variables */
int sumrusage;
int rawcpu;
int sortby;
#define SORTMEM 1
#define SORTCPU 2
int uid = -1;
dev_t ttydev = NODEV;
int pid = -1;
int all;
int xflg;
int prtheader;
int lineno;
/*
* variables retrieved via nlist
*/
struct nlist psnl[] = {
{"_ecmx"},
#define X_ECMX 0
{"_fscale"},
#define X_FSCALE 1
{"_ccpu"},
#define X_CCPU 2
{NULL}
};
int fscale;
int ecmx;
fixpt_t ccpu;
#define USAGE "ps [ -(o|O) fmt ] [ -wlvujnsaxSCLmcr ] [ -p pid ] [ -t tty ]"
main (argc, argv)
char *argv[];
{
extern char *optarg;
extern int optind;
int ch;
register i;
register struct var *v;
register struct proc *p;
struct winsize ws;
struct kinfo_proc *kprocs;
int nentries;
int fmt = 0;
int pscomp();
int what, flag;
char *kludge_oldps_options();
if ((ioctl(1, TIOCGWINSZ, &ws) == -1 &&
ioctl(2, TIOCGWINSZ, &ws) == -1 &&
ioctl(0, TIOCGWINSZ, &ws) == -1) ||
ws.ws_col == 0)
termwidth = 79;
else
termwidth = ws.ws_col - 1;
if (argc > 1)
argv[1] = kludge_oldps_options(argv[1]);
while ((ch = getopt(argc, argv, "o:O:wlvujnsaxt:p:SCLmrhTg")) != EOF)
switch((char)ch) {
case 'o':
parsefmt(optarg);
fmt++;
break;
case 'O':
parsefmt("pid");
parsefmt(optarg);
parsefmt("state tt time command");
fmt++;
break;
case 'w':
if (termwidth < 131)
termwidth = 131;
else
termwidth = UNLIMITED;
break;
case 'l':
parsefmt(LFMT);
fmt++;
break;
case 'v':
parsefmt(VFMT);
sortby = SORTMEM;
fmt++;
break;
case 'u':
parsefmt(UFMT);
sortby = SORTCPU;
fmt++;
break;
case 'j':
parsefmt(JFMT);
fmt++;
break;
case 's':
parsefmt(SFMT);
fmt++;
break;
case 'T':
case 't': {
struct stat stbuf;
char *tname, *ttyname();
char termname[MAXPATHLEN+1];
if (ch == 'T') {
if ((tname = ttyname(0)) == NULL)
error("<stdin>: not a terminal");
} else
tname = optarg;
if (strlen(tname) == 2) {
if (strcmp(tname, "co") == 0)
strcpy(termname, "/dev/console");
else {
strcpy(termname, "/dev/tty");
strcat(termname, tname);
}
} else if (*tname != '/') {
strcpy(termname, "/dev/");
strcat(termname, tname);
} else
strcpy(termname, tname);
if (stat(termname, &stbuf) == -1)
syserror(termname);
if ((stbuf.st_mode & S_IFMT) != S_IFCHR)
error("%s: not a terminal", termname);
ttydev = stbuf.st_rdev;
break;
}
case 'p':
pid = atoi(optarg);
xflg++;
break;
case 'S':
sumrusage++;
break;
case 'C':
rawcpu++;
break;
case 'L': {
int i = 0;
struct combovar *cb = &combovar[0];
char *cp;
v = &var[0];
for (;;) {
if (v->name[0] != NULL) {
cp = v->name[0];
v++;
} else if (cb->name != NULL) {
cp = cb->name;
cb++;
} else
break;
if (termwidth &&
(i += strlen(cp)+1) > termwidth)
i = strlen(cp), printf("\n");
printf("%s ", cp);
}
printf("\n");
exit(0);
}
case 'a':
all++;
break;
case 'x':
xflg++;
break;
case 'm':
sortby = SORTMEM;
break;
case 'r':
sortby = SORTCPU;
break;
case 'h':
prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
break;
case 'g':
break; /* no-op */
case '?':
default:
fprintf(stderr, "usage: %s\n", USAGE);
exit(1);
}
argc -= optind;
argv += optind;
if (*argv) {
char *nlistf, *memf = NULL, *swapf = NULL;
nlistf = *argv++;
if (*argv) {
memf = *argv++;
if (*argv)
swapf = *argv++;
}
if (kvm_openfiles(nlistf, memf, swapf) == -1)
error("kvm_openfiles: %s", kvm_geterr());
}
if (!fmt)
parsefmt(DFMT);
if (!all && ttydev == NODEV && pid == -1) /* XXX - should be cleaner */
uid = getuid();
/*
* scan requested variables, noting what structures are needed,
* and adjusting header widths as appropiate.
*/
scanvars();
#ifdef notdef
if (sortby == SORTCPU)
neednlist = 1;
#endif
if (neednlist)
donlist();
/*
* get proc list
*/
if (uid != -1) {
what = KINFO_PROC_UID;
flag = uid;
} else if (ttydev != NODEV) {
what = KINFO_PROC_TTY;
flag = ttydev;
} else if (pid != -1) {
what = KINFO_PROC_PID;
flag = pid;
/*
* select procs
*/
if ((nentries = kvm_getprocs(what, flag)) == -1) {
fprintf(stderr, "ps: %s\n", kvm_geterr());
exit(1);
}
kinfo = (struct kinfo *)malloc(nentries * sizeof (struct kinfo));
if (kinfo == NULL)
error("out of memory");
i = 0;
while ((p = kvm_nextproc()) != NULL) {
kinfo[i].ki_p = p;
kinfo[i].ki_e = kvm_geteproc(p);
if (needuser)
saveuser(&kinfo[i]);
i++;
}
nentries = i;
/*
* print header
*/
printheader();
if (nentries == 0)
exit(0);
/*
* sort proc list
*/
qsort(kinfo, nentries, sizeof (struct kinfo), pscomp);
/*
* for each proc, call each variable output function.
*/
for (i = 0; i < nentries; i++) {
if (xflg == 0 && (kinfo[i].ki_e->e_tdev == NODEV ||
(kinfo[i].ki_p->p_flag & SCTTY ) == 0))
continue;
for (v = vhead; v != NULL; v = v->next) {
(*v->oproc)(&kinfo[i], v);
if (v->next != NULL)
putchar(' ');
}
putchar('\n');
if (prtheader && lineno++ == prtheader-4) {
putchar('\n');
printheader();
lineno = 0;
}
}
exit(0);
}
#define FMTSEP " \t,\n"
parsefmt(fmt)
char *fmt;
{
register char *f = fmt, *cp, *hp;
struct var *v;
char *strtok(), *index();
char newbuf[1024], *nb = newbuf; /* XXX */
char *lookupcombo();
struct var *lookupvar();
/*
* strtok is not &^%^& re-entrant, so we have
* only one level of expansion, looking for combo
* variables once here, and expanding the string
* before really parsing it. With strtok_r,
* you would move the expansion to before the
* lookupvar inside the 2nd while loop with a
* recursive call to parsefmt.
*/
while ((cp = strtok(f, FMTSEP)) != NULL) {
if ((hp = lookupcombo(cp)) == NULL);
hp = cp;
if (((nb + strlen(hp)) - newbuf) >= 1024)
error("format too large");
strcpy(nb, hp);
while (*nb)
nb++;
*nb++ = ' ';
*nb = '\0';
f = NULL;
}
f = newbuf;
while ((cp = strtok(f, FMTSEP)) != NULL) {
if (hp = index(cp, '='))
*hp++ = '\0';
v = lookupvar(cp);
if (v == NULL)
error("unknown variable in format: %s", cp);
if (v->next != NULL || vtail == v)
error("can't specify a variable twice: %s", cp);
if (hp)
v->header = hp;
if (vhead == NULL)
vhead = vtail = v;
else {
vtail->next = v;
vtail = v;
}
f = NULL; /* for strtok */
}
}
scanvars()
{
register i;
register struct var *v;
for (v = vhead; v != NULL; v = v->next) {
i = strlen(v->header);
if (v->width < i)
v->width = i;
totwidth += v->width + 1; /* +1 for space */
if (v->flag & USER)
needuser = 1;
if (v->flag & COMM)
needcomm = 1;
if (v->flag & NLIST)
neednlist = 1;
}
totwidth--;
}
printheader()
{
register struct var *v;
for (v = vhead; v != NULL; v = v->next) {
if (v->flag & LJUST) {
if (v->next == NULL) /* last one */
printf("%s", v->header);
else
printf("%-*s",v->width, v->header);
} else
printf("%*s",v->width, v->header);
if (v->next != NULL)
putchar(' ');
}
putchar('\n');
}
command(k, v)
struct kinfo *k;
struct var *v;
{
if (v->next == NULL) {
/* last field */
if (termwidth == UNLIMITED)
printf("%s", k->ki_args);
else {
register left = termwidth - (totwidth - v->width);
register char *cp = k->ki_args;
if (left < 1) /* already wrapped, just use std width */
left = v->width;
while (left-- && *cp)
putchar(*cp++);
}
} else
printf("%-*.*s", v->width, v->width, k->ki_args);
}
ucomm(k, v)
struct kinfo *k;
struct var *v;
{
printf("%-*s", v->width, k->ki_p->p_comm);
}
logname(k, v)
struct kinfo *k;
struct var *v;
{
printf("%-*s", v->width, k->ki_p->p_logname);
}
state(k, v)
struct kinfo *k;
struct var *v;
{
char buf[16];
register char *cp = buf;
register struct proc *p = k->ki_p;
register flag = p->p_flag;
switch (p->p_stat) {
case SSTOP:
*cp = 'T';
break;
case SSLEEP:
if (flag & SSINTR) /* interuptable (long) */
*cp = p->p_slptime >= MAXSLP ? 'I' : 'S';
else
*cp = (flag & SPAGE) ? 'P' : 'D';
break;
case SRUN:
case SIDL:
*cp = 'R';
break;
case SZOMB:
*cp = 'Z';
break;
default:
*cp = '?';
}
cp++;
if (flag & SLOAD) {
if (p->p_rssize > p->p_maxrss)
*cp++ = '>';
} else
*cp++ = 'W';
if (p->p_nice < NZERO)
*cp++ = '<';
else if (p->p_nice > NZERO)
*cp++ = 'N';
if (flag & SUANOM)
*cp++ = 'A';
else if (flag & SSEQL)
*cp++ = 'S';
if (flag & STRC)
*cp++ = 'X';
if (flag & SWEXIT)
*cp++ = 'E';
if (flag & SVFORK)
*cp++ = 'V';
if (flag & (SSYS|SLOCK|SULOCK|SKEEP|SPHYSIO))
*cp++ = 'L';
if (k->ki_e->e_flag & EPROC_SLEADER)
*cp++ = 's';
if ((flag & SCTTY) && k->ki_e->e_pgid == k->ki_e->e_tpgid)
*cp++ = '+';
*cp = '\0';
printf("%-*s", v->width, buf);
}
pri(k, v)
struct kinfo *k;
struct var *v;
{
printf("%*d", v->width, k->ki_p->p_pri - PZERO);
}
uname(k, v)
struct kinfo *k;
struct var *v;
{
printf("%-*s", v->width, user_from_uid(k->ki_p->p_uid, 0));
}
runame(k, v)
struct kinfo *k;
struct var *v;
{
printf("%-*s", v->width, user_from_uid(k->ki_p->p_ruid, 0));
}
tdev(k, v)
struct kinfo *k;
struct var *v;
{
dev_t dev = k->ki_e->e_tdev;
if (dev == NODEV)
printf("%*s", v->width, "??");
else {
char buff[16];
sprintf(buff, "%d/%d", major(dev), minor(dev));
printf("%*s", v->width, buff);
}
}
extern char *devname();
tname(k, v)
struct kinfo *k;
struct var *v;
{
dev_t dev = k->ki_e->e_tdev;
char *tname;
if (dev == NODEV || (tname = devname(dev, S_IFCHR)) == NULL)
printf("%-*s", v->width, "??");
else {
if (strncmp(tname, "tty", 3) == 0)
tname += 3;
printf("%*.*s%c", v->width-1, v->width-1, tname,
k->ki_e->e_flag & EPROC_CTTY ? ' ' : '-');
}
}
longtname(k, v)
struct kinfo *k;
struct var *v;
{
dev_t dev = k->ki_e->e_tdev;
char *tname;
if (dev == NODEV || (tname = devname(dev, S_IFCHR)) == NULL)
printf("%-*s", v->width, "??");
else
printf("%-*s", v->width, tname);
}
#include <sys/time.h>
started(k, v)
struct kinfo *k;
struct var *v;
{
extern char *attime();
printf("%-*s", v->width, k->ki_u ?
attime(&k->ki_u->u_start.tv_sec) : "-");
}
lstarted(k, v)
struct kinfo *k;
struct var *v;
{
extern char *ctime();
char *tp;
if (k->ki_u)
(tp = ctime(&k->ki_u->u_start.tv_sec))[24] = '\0';
else
tp = "-";
printf("%-*s", v->width, tp);
}
wchan(k, v)
struct kinfo *k;
struct var *v;
{
if (k->ki_p->p_wchan) {
if (k->ki_p->p_pri > PZERO)
printf("%-*.*s", v->width, v->width, k->ki_e->e_wmesg);
else
printf("%*x", v->width,
(int)k->ki_p->p_wchan &~ KERNBASE);
} else
printf("%-*s", v->width, "-");
}
#define pgtok(a) (((a)*NBPG)/1024)
vsize(k, v)
struct kinfo *k;
struct var *v;
{
printf("%*d", v->width,
pgtok(k->ki_p->p_dsize + k->ki_p->p_ssize + k->ki_e->e_xsize));
}
rssize(k, v)
struct kinfo *k;
struct var *v;
{
printf("%*d", v->width,
pgtok(k->ki_p->p_rssize + (k->ki_e->e_xccount ?
(k->ki_e->e_xrssize / k->ki_e->e_xccount) : 0)));
}
p_rssize(k, v) /* doesn't account for text */
struct kinfo *k;
struct var *v;
{
printf("%*d", v->width, pgtok(k->ki_p->p_rssize));
}
cputime(k, v)
struct kinfo *k;
struct var *v;
{
long secs;
long psecs; /* "parts" of a second. first micro, then centi */
char obuff[128];
if (k->ki_p->p_stat == SZOMB || k->ki_u == NULL) {
secs = 0;
psecs = 0;
} else {
secs = k->ki_p->p_utime.tv_sec +
k->ki_p->p_stime.tv_sec;
psecs = k->ki_p->p_utime.tv_usec +
k->ki_p->p_stime.tv_usec;
if (sumrusage) {
secs += k->ki_u->u_cru.ru_utime.tv_sec +
k->ki_u->u_cru.ru_stime.tv_sec;
psecs += k->ki_u->u_cru.ru_utime.tv_usec +
k->ki_u->u_cru.ru_stime.tv_usec;
}
/*
* round and scale to 100's
*/
psecs = (psecs + 5000) / 10000;
if (psecs >= 100) {
psecs -= 100;
secs++;
}
}
sprintf(obuff, "%3ld:%02ld.%02ld", secs/60, secs%60, psecs);
printf("%*s", v->width, obuff);
}
double
getpcpu(k)
struct kinfo *k;
{
/*
* note: this routine requires ccpu and fscale
* be initialized. If you call this routine from
* somewhere new, insure that the "neednlist" flag
* gets set.
*/
struct proc *p = k->ki_p;
#define fxtofl(fixpt) ((double)(fixpt) / fscale)
if (p->p_time == 0 || (p->p_flag & SLOAD) == 0) /* XXX - I don't like this */
return (0.0);
if (rawcpu)
return (100.0 * fxtofl(p->p_pctcpu));
return (100.0 * fxtofl(p->p_pctcpu) /
(1.0 - exp(p->p_time * log(fxtofl(ccpu)))));
}
pcpu(k, v)
struct kinfo *k;
struct var *v;
{
printf("%*.1f", v->width, getpcpu(k));
}
double
getpmem(k, v)
struct kinfo *k;
struct var *v;
{
struct proc *p = k->ki_p;
struct eproc *e = k->ki_e;
double fracmem;
int szptudot;
/*
* note: this routine requires that ecmx
* be initialized. If you call this routine from
* somewhere new, insure that the "neednlist" flag
* gets set.
*/
if (p->p_flag & SLOAD == 0)
return (0.0);
szptudot = UPAGES + clrnd(ctopt(p->p_dsize + p->p_ssize + e->e_xsize));
fracmem = ((float)p->p_rssize + szptudot)/CLSIZE/ecmx;
if (p->p_textp && e->e_xccount)
fracmem += ((float)e->e_xrssize)/CLSIZE/e->e_xccount/ecmx;
return (100.0 * fracmem);
}
pmem(k, v)
struct kinfo *k;
struct var *v;
{
printf("%*.1f", v->width, getpmem(k));
}
pagein(k, v)
struct kinfo *k;
struct var *v;
{
printf("%*d", v->width, k->ki_u ? k->ki_u->u_ru.ru_majflt : 0);
}
maxrss(k, v)
struct kinfo *k;
struct var *v;
{
if (k->ki_p->p_maxrss != (RLIM_INFINITY/NBPG))
printf("%*d", v->width, pgtok(k->ki_p->p_maxrss));
else
printf("%*s", v->width, "-");
}
tsize(k, v)
struct kinfo *k;
struct var *v;
{
printf("%*d", v->width, pgtok(k->ki_e->e_xsize));
}
trss(k, v)
struct kinfo *k;
struct var *v;
{
printf("%*d", v->width, pgtok(k->ki_e->e_xrssize));
}
/*
* Generic output routines. Print fields from various prototype
* structures.
*/
pvar(k, v)
struct kinfo *k;
struct var *v;
{
printval((char *)((char *)k->ki_p + v->off), v);
}
evar(k, v)
struct kinfo *k;
struct var *v;
{
printval((char *)((char *)k->ki_e + v->off), v);
}
uvar(k, v)
struct kinfo *k;
struct var *v;
{
if (k->ki_u)
printval((char *)((char *)k->ki_u + v->off), v);
else
printf("%*s", v->width, "-");
}
rvar(k, v)
struct kinfo *k;
struct var *v;
{
if (k->ki_u)
printval((char *)((char *)(&k->ki_u->u_ru) + v->off), v);
else
printf("%*s", v->width, "-");
}
char *
lookupcombo(cp)
char *cp;
{
register struct combovar *cv = &combovar[0];
for (; cv->name; cv++)
if (strcmp(cp, cv->name) == 0)
return (cv->replace);
return (NULL);
}
struct var *
lookupvar(cp)
char *cp;
{
register int i, j;
for (i=0; var[i].name[0] != NULL; i++)
for (j=0; var[i].name[j] != NULL; j++)
if (strcmp(cp, var[i].name[j]) == 0)
return (&var[i]);
return (NULL);
}
printval(bp, v)
char *bp;
struct var *v;
{
static char ofmt[32] = "%";
register char *cp = ofmt+1, *fcp = v->fmt;
if (v->flag & LJUST)
*cp++ = '-';
*cp++ = '*';
while (*cp++ = *fcp++)
;
switch (v->type) {
case CHAR:
printf(ofmt, v->width, *(char *)bp);
break;
case UCHAR:
printf(ofmt, v->width, *(u_char *)bp);
break;
case SHORT:
printf(ofmt, v->width, *(short *)bp);
break;
case USHORT:
printf(ofmt, v->width, *(u_short *)bp);
break;
case LONG:
printf(ofmt, v->width, *(long *)bp);
break;
case ULONG:
printf(ofmt, v->width, *(u_long *)bp);
break;
case KPTR:
printf(ofmt, v->width, *(u_long *)bp &~ KERNBASE);
break;
default:
error("unknown type %d", v->type);
}
}
/* XXX - redo */
struct usave *
saveuser(ki)
struct kinfo *ki;
{
register struct usave *usp;
register struct user *up;
if ((usp = (struct usave *)calloc(1, sizeof (struct usave))) == NULL) {
fprintf(stderr, "ps: out of memory\n");
exit(1);
}
ki->ki_u = usp;
up = kvm_getu(ki->ki_p);
/*
* save arguments if needed
*/
if (needcomm)
ki->ki_args = saveargs(ki->ki_p, up);
else
ki->ki_args = NULL;
if (up != NULL) {
/*
* save important fields
*/
usp->u_procp = up->u_procp;
usp->u_start = up->u_start;
usp->u_ru = up->u_ru;
usp->u_cru = up->u_cru;
usp->u_cmask = up->u_cmask;
usp->u_acflag = up->u_acflag;
}
return;
}
char *
saveargs(p, up)
struct proc *p;
struct user *up;
{
char *savestr();
return(savestr(kvm_getargs(p, up)));
}
pscomp(k1, k2)
struct kinfo *k1, *k2;
{
int i;
#define VSIZE(k) ((k)->ki_p->p_dsize + (k)->ki_p->p_ssize + (k)->ki_e->e_xsize)
if (sortby == SORTCPU)
return (getpcpu(k2) - getpcpu(k1));
#ifdef notyet
if (sortby == SORTRUN)
return (proc_compare(k1->ki_p, k2->ki_p));
#endif
if (sortby == SORTMEM)
return (VSIZE(k2) - VSIZE(k1));
i = k1->ki_e->e_tdev - k2->ki_e->e_tdev;
if (i == 0)
i = k1->ki_p->p_pid - k2->ki_p->p_pid;
return (i);
}
donlist()
{
if (kvm_nlist(psnl) != 0)
error("can't get namelist");
if (kvm_read(psnl[X_FSCALE].n_value, &fscale, sizeof(int)) !=
sizeof (int))
error("error reading fscale: %s", kvm_geterr());
if (kvm_read(psnl[X_ECMX].n_value, &ecmx, sizeof(int)) !=
sizeof (int))
error("error reading ecmx: %s", kvm_geterr());
if (kvm_read(psnl[X_CCPU].n_value, &ccpu, sizeof(fixpt_t)) !=
sizeof (fixpt_t))
error("error reading ccpu: %s", kvm_geterr());
}
char *
savestr(cp)
char *cp;
{
register unsigned len;
register char *dp;
len = strlen(cp);
dp = (char *)calloc(len+1, sizeof (char));
(void) strcpy(dp, cp);
return (dp);
}
error(a, b, c, d, e)
char *a, *b, *c, *d, *e;
{
fprintf(stderr, "ps: ");
fprintf(stderr, a, b, c, d, e);
fprintf(stderr, "\n");
exit(1);
}
syserror(a)
char *a;
{
extern errno;
error("%s: %s", a, strerror(errno));
}
/*
* ICK (all for getopt), would rather hide the ugliness
* here than taint the main code.
*
* ps foo -> ps -foo
* ps 34 -> ps -p34
*
* The old convention that 't' with no trailing tty arg means the users
* tty, is only supported if argv[1] doesn't begin with a '-'. This same
* feature is available with the option 'T', which takes no argument.
*/
char *
kludge_oldps_options(s)
char *s;
{
int len = strlen(s), numlen = 0;
char *newopts, *ns, *cp;
if ((newopts = ns = (char *)malloc(len+2)) == NULL)
error("out of memory");
/*
* options begin with '-'
*/
if (*s != '-')
*ns++ = '-'; /* add option flag */
/*
* gaze to end of argv[1]
*/
cp = s + len - 1;
/*
* if last letter is a 't' flag with no argument (in the context
* of the oldps options -- option string NOT starting with a '-' --
* then convert to 'T' (meaning *this* terminal, i.e. ttyname(0).
*/
if (*cp == 't' && *s != '-')
*cp = 'T';
else {
/*
* otherwise check for trailing number, which *may* be a
* pid.
*/
while (isdigit(*cp)) {
--cp;
numlen++;
}
}
cp++;
bcopy(s, ns, cp - s); /* copy everything up to trailing number */
while (*ns)
ns++;
/*
* 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(*cp) && (cp == s || *(cp-1) != 't' && *(cp-1) != 'p' &&
((cp-1) == s || *(cp-2) != 't')))
*ns++ = 'p';
strcat(ns, cp); /* and append the number */
return (newopts);
}