summaryrefslogtreecommitdiff
path: root/usr/src/cmd/ps/ucbps.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/ps/ucbps.c')
-rw-r--r--usr/src/cmd/ps/ucbps.c1186
1 files changed, 1186 insertions, 0 deletions
diff --git a/usr/src/cmd/ps/ucbps.c b/usr/src/cmd/ps/ucbps.c
new file mode 100644
index 0000000000..4731f260e9
--- /dev/null
+++ b/usr/src/cmd/ps/ucbps.c
@@ -0,0 +1,1186 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+/*
+ * ps -- print things about processes.
+ */
+
+#define _SYSCALL32
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mkdev.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <dirent.h>
+#include <procfs.h>
+#include <sys/param.h>
+#include <sys/ttold.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <locale.h>
+#include <wctype.h>
+#include <stdarg.h>
+#include <sys/proc.h>
+#include <priv_utils.h>
+
+#define NTTYS 2 /* max ttys that can be specified with the -t option */
+ /* only one tty can be specified with SunOS ps */
+#define SIZ 30 /* max processes that can be specified with -p and -g */
+#define ARGSIZ 30 /* size of buffer holding args for -t, -p, -u options */
+
+#define FSTYPE_MAX 8
+
+struct psent {
+ psinfo_t *psinfo;
+ char *psargs;
+ int found;
+};
+
+static int tplen, maxlen, twidth;
+static char hdr[81];
+static struct winsize win;
+
+static int retcode = 1;
+static int lflg; /* long format */
+static int uflg; /* user-oriented output */
+static int aflg; /* Display all processes */
+static int eflg; /* Display environment as well as arguments */
+static int gflg; /* Display process group leaders */
+static int tflg; /* Processes running on specific terminals */
+static int rflg; /* Running processes only flag */
+static int Sflg; /* Accumulated time plus all reaped children */
+static int xflg; /* Include processes with no controlling tty */
+static int cflg; /* Display command name */
+static int vflg; /* Virtual memory-oriented output */
+static int nflg; /* Numerical output */
+static int pflg; /* Specific process id passed as argument */
+static int Uflg; /* Update private database, ups_data */
+static int errflg;
+
+static char *gettty();
+static char argbuf[ARGSIZ];
+static char *parg;
+static char *p1; /* points to successive option arguments */
+static uid_t my_uid;
+static char stdbuf[BUFSIZ];
+
+static int ndev; /* number of devices */
+static int maxdev; /* number of devl structures allocated */
+
+#define DNINCR 100
+#define DNSIZE 14
+static struct devl { /* device list */
+ char dname[DNSIZE]; /* device name */
+ dev_t ddev; /* device number */
+} *devl;
+
+static struct tty {
+ char *tname;
+ dev_t tdev;
+} tty[NTTYS]; /* for t option */
+static int ntty = 0;
+static pid_t pidsave;
+static int pidwidth;
+
+static char *procdir = "/proc"; /* standard /proc directory */
+static void usage(); /* print usage message and quit */
+static void getarg(void);
+static void prtime(timestruc_t st);
+static void przom(psinfo_t *psinfo);
+static int num(char *);
+static int preadargs(int, psinfo_t *, char *);
+static int preadenvs(int, psinfo_t *, char *);
+static int prcom(int, psinfo_t *, char *);
+static int namencnt(char *, int, int);
+static int pscompare(const void *, const void *);
+static char *err_string(int);
+
+extern int scrwidth(wchar_t); /* header file? */
+
+int
+ucbmain(int argc, char **argv)
+{
+ psinfo_t info; /* process information structure from /proc */
+ char *psargs = NULL; /* pointer to buffer for -w and -ww options */
+ char *svpsargs = NULL;
+ struct psent *psent;
+ int entsize;
+ int nent;
+ pid_t maxpid;
+
+ struct tty *ttyp = tty;
+ char *tmp;
+ char *p;
+ int c;
+ pid_t pid; /* pid: process id */
+ pid_t ppid; /* ppid: parent process id */
+ int i, found;
+
+ size_t size;
+
+ DIR *dirp;
+ struct dirent *dentp;
+ char psname[100];
+ char asname[100];
+ int pdlen;
+ size_t len;
+
+ (void) setlocale(LC_ALL, "");
+
+ my_uid = getuid();
+
+ /*
+ * This program needs the proc_owner privilege
+ */
+ (void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_PROC_OWNER,
+ (char *)NULL);
+
+ /*
+ * calculate width of pid fields based on configured MAXPID
+ * (must be at least 5 to retain output format compatibility)
+ */
+ maxpid = (pid_t)sysconf(_SC_MAXPID);
+ pidwidth = 1;
+ while ((maxpid /= 10) > 0)
+ ++pidwidth;
+ pidwidth = pidwidth < 5 ? 5 : pidwidth;
+
+ if (ioctl(1, TIOCGWINSZ, &win) == -1)
+ twidth = 80;
+ else
+ twidth = (win.ws_col == 0 ? 80 : win.ws_col);
+
+ /* add the '-' for BSD compatibility */
+ if (argc > 1) {
+ if (argv[1][0] != '-' && !isdigit(argv[1][0])) {
+ len = strlen(argv[1]) + 2;
+ tmp = malloc(len);
+ if (tmp != NULL) {
+ (void) snprintf(tmp, len, "%s%s", "-", argv[1]);
+ argv[1] = tmp;
+ }
+ }
+ }
+
+ setbuf(stdout, stdbuf);
+ while ((c = getopt(argc, argv, "lcaengrSt:xuvwU")) != EOF)
+ switch (c) {
+ case 'g':
+ gflg++; /* include process group leaders */
+ break;
+ case 'c': /* display internal command name */
+ cflg++;
+ break;
+ case 'r': /* restrict output to running processes */
+ rflg++;
+ break;
+ case 'S': /* display time by process and all reaped children */
+ Sflg++;
+ break;
+ case 'x': /* process w/o controlling tty */
+ xflg++;
+ break;
+ case 'l': /* long listing */
+ lflg++;
+ uflg = vflg = 0;
+ break;
+ case 'u': /* user-oriented output */
+ uflg++;
+ lflg = vflg = 0;
+ break;
+ case 'U': /* update private database ups_data */
+ Uflg++;
+ break;
+ case 'w': /* increase display width */
+ if (twidth < 132)
+ twidth = 132;
+ else /* second w option */
+ twidth = NCARGS;
+ break;
+ case 'v': /* display virtual memory format */
+ vflg++;
+ lflg = uflg = 0;
+ break;
+ case 'a':
+ /*
+ * display all processes except process group
+ * leaders and processes w/o controlling tty
+ */
+ aflg++;
+ gflg++;
+ break;
+ case 'e':
+ /* Display environment along with aguments. */
+ eflg++;
+ break;
+ case 'n': /* Display numerical output */
+ nflg++;
+ break;
+ case 't': /* restrict output to named terminal */
+#define TSZ 30
+ tflg++;
+ gflg++;
+ xflg = 0;
+
+ p1 = optarg;
+ do { /* only loop through once (NTTYS = 2) */
+ parg = argbuf;
+ if (ntty >= NTTYS-1)
+ break;
+ getarg();
+ if ((p = malloc(TSZ+1)) == NULL) {
+ (void) fprintf(stderr,
+ "ps: no memory\n");
+ exit(1);
+ }
+ p[0] = '\0';
+ size = TSZ;
+ if (isdigit(*parg)) {
+ (void) strcpy(p, "tty");
+ size -= 3;
+ }
+
+ (void) strncat(p, parg, size);
+ ttyp->tdev = PRNODEV;
+ if (parg && *parg == '?')
+ xflg++;
+ else {
+ char nambuf[TSZ+6]; /* for /dev/+\0 */
+ struct stat64 s;
+ (void) strcpy(nambuf, "/dev/");
+ (void) strcat(nambuf, p);
+ if (stat64(nambuf, &s) == 0)
+ ttyp->tdev = s.st_rdev;
+ }
+ ttyp++->tname = p;
+ ntty++;
+ } while (*p1);
+ break;
+ default: /* error on ? */
+ errflg++;
+ break;
+ }
+
+ if (errflg)
+ usage();
+
+ if (optind + 1 < argc) { /* more than one additional argument */
+ (void) fprintf(stderr, "ps: too many arguments\n");
+ usage();
+ }
+
+ /*
+ * The -U option is obsolete. Attempts to use it cause ps to exit
+ * without printing anything.
+ */
+ if (Uflg)
+ exit(0);
+
+ if (optind < argc) { /* user specified a specific proc id */
+ pflg++;
+ p1 = argv[optind];
+ parg = argbuf;
+ getarg();
+ if (!num(parg)) {
+ (void) fprintf(stderr,
+ "ps: %s is an invalid non-numeric argument for a process id\n", parg);
+ usage();
+ }
+ pidsave = (pid_t)atol(parg);
+ aflg = rflg = xflg = 0;
+ gflg++;
+ }
+
+ if (tflg)
+ ttyp->tname = NULL;
+
+ /* allocate an initial guess for the number of processes */
+ entsize = 1024;
+ psent = malloc(entsize * sizeof (struct psent));
+ if (psent == NULL) {
+ (void) fprintf(stderr, "ps: no memory\n");
+ exit(1);
+ }
+ nent = 0; /* no active entries yet */
+
+ if (lflg) {
+ (void) sprintf(hdr,
+ " F UID%*s%*s %%C PRI NI SZ RSS "
+ "WCHAN S TT TIME COMMAND", pidwidth + 1, "PID",
+ pidwidth + 1, "PPID");
+ } else if (uflg) {
+ if (nflg)
+ (void) sprintf(hdr,
+ " UID%*s %%CPU %%MEM SZ RSS "
+ "TT S START TIME COMMAND",
+ pidwidth + 1, "PID");
+ else
+ (void) sprintf(hdr,
+ "USER %*s %%CPU %%MEM SZ RSS "
+ "TT S START TIME COMMAND",
+ pidwidth + 1, "PID");
+ } else if (vflg) {
+ (void) sprintf(hdr,
+ "%*s TT S TIME SIZE RSS %%CPU %%MEM "
+ "COMMAND", pidwidth + 1, "PID");
+ } else
+ (void) sprintf(hdr, "%*s TT S TIME COMMAND",
+ pidwidth + 1, "PID");
+
+ twidth = twidth - strlen(hdr) + 6;
+ (void) printf("%s\n", hdr);
+
+ if (twidth > PRARGSZ && (psargs = malloc(twidth)) == NULL) {
+ (void) fprintf(stderr, "ps: no memory\n");
+ exit(1);
+ }
+ svpsargs = psargs;
+
+ /*
+ * Determine which processes to print info about by searching
+ * the /proc directory and looking at each process.
+ */
+ if ((dirp = opendir(procdir)) == NULL) {
+ (void) fprintf(stderr, "ps: cannot open PROC directory %s\n",
+ procdir);
+ exit(1);
+ }
+
+ (void) strcpy(psname, procdir);
+ pdlen = strlen(psname);
+ psname[pdlen++] = '/';
+
+ /* for each active process --- */
+ while (dentp = readdir(dirp)) {
+ int psfd; /* file descriptor for /proc/nnnnn/psinfo */
+ int asfd; /* file descriptor for /proc/nnnnn/as */
+
+ if (dentp->d_name[0] == '.') /* skip . and .. */
+ continue;
+ (void) strcpy(psname + pdlen, dentp->d_name);
+ (void) strcpy(asname, psname);
+ (void) strcat(psname, "/psinfo");
+ (void) strcat(asname, "/as");
+retry:
+ if ((psfd = open(psname, O_RDONLY)) == -1)
+ continue;
+ asfd = -1;
+ if (psargs != NULL || eflg) {
+
+ /* now we need the proc_owner privilege */
+ (void) __priv_bracket(PRIV_ON);
+
+ asfd = open(asname, O_RDONLY);
+
+ /* drop proc_owner privilege after open */
+ (void) __priv_bracket(PRIV_OFF);
+ }
+
+ /*
+ * Get the info structure for the process
+ */
+ if (read(psfd, &info, sizeof (info)) != sizeof (info)) {
+ int saverr = errno;
+
+ (void) close(psfd);
+ if (asfd > 0)
+ (void) close(asfd);
+ if (saverr == EAGAIN)
+ goto retry;
+ if (saverr != ENOENT)
+ (void) fprintf(stderr, "ps: read() on %s: %s\n",
+ psname, err_string(saverr));
+ continue;
+ }
+ (void) close(psfd);
+
+ found = 0;
+ if (info.pr_lwp.pr_state == 0) /* can't happen? */
+ goto closeit;
+ pid = info.pr_pid;
+ ppid = info.pr_ppid;
+
+ /* Display only process from command line */
+ if (pflg) { /* pid in arg list */
+ if (pidsave == pid)
+ found++;
+ else
+ goto closeit;
+ }
+
+ /*
+ * Omit "uninteresting" processes unless 'g' option.
+ */
+ if ((ppid == 1) && !(gflg))
+ goto closeit;
+
+ /*
+ * Omit non-running processes for 'r' option
+ */
+ if (rflg &&
+ !(info.pr_lwp.pr_sname == 'O' ||
+ info.pr_lwp.pr_sname == 'R'))
+ goto closeit;
+
+ if (!found && !tflg && !aflg && info.pr_euid != my_uid)
+ goto closeit;
+
+ /*
+ * Read the args for the -w and -ww cases
+ */
+ if (asfd > 0) {
+ if ((psargs != NULL &&
+ preadargs(asfd, &info, psargs) == -1) ||
+ (eflg && preadenvs(asfd, &info, psargs) == -1)) {
+ int saverr = errno;
+
+ (void) close(asfd);
+ if (saverr == EAGAIN)
+ goto retry;
+ if (saverr != ENOENT)
+ (void) fprintf(stderr,
+ "ps: read() on %s: %s\n",
+ asname, err_string(saverr));
+ continue;
+ }
+ } else {
+ psargs = info.pr_psargs;
+ }
+
+ if (nent >= entsize) {
+ entsize *= 2;
+ psent = (struct psent *)realloc((char *)psent,
+ entsize * sizeof (struct psent));
+ if (psent == NULL) {
+ (void) fprintf(stderr, "ps: no memory\n");
+ exit(1);
+ }
+ }
+ if ((psent[nent].psinfo = malloc(sizeof (psinfo_t)))
+ == NULL) {
+ (void) fprintf(stderr, "ps: no memory\n");
+ exit(1);
+ }
+ *psent[nent].psinfo = info;
+ if (psargs == NULL)
+ psent[nent].psargs = NULL;
+ else {
+ if ((psent[nent].psargs = malloc(strlen(psargs)+1))
+ == NULL) {
+ (void) fprintf(stderr, "ps: no memory\n");
+ exit(1);
+ }
+ (void) strcpy(psent[nent].psargs, psargs);
+ }
+ psent[nent].found = found;
+ nent++;
+closeit:
+ if (asfd > 0)
+ (void) close(asfd);
+ psargs = svpsargs;
+ }
+
+ /* revert to non-privileged user */
+ (void) __priv_relinquish();
+
+ (void) closedir(dirp);
+
+ qsort((char *)psent, nent, sizeof (psent[0]), pscompare);
+
+ for (i = 0; i < nent; i++) {
+ struct psent *pp = &psent[i];
+ if (prcom(pp->found, pp->psinfo, pp->psargs)) {
+ (void) printf("\n");
+ retcode = 0;
+ }
+ }
+
+ return (retcode);
+}
+
+static void
+usage() /* print usage message and quit */
+{
+ static char usage1[] = "ps [ -aceglnrSuUvwx ] [ -t term ] [ num ]";
+
+ (void) fprintf(stderr, "usage: %s\n", usage1);
+ exit(1);
+}
+
+/*
+ * Read the process arguments from the process.
+ * This allows >PRARGSZ characters of arguments to be displayed but,
+ * unlike pr_psargs[], the process may have changed them.
+ */
+#define NARG 100
+static int
+preadargs(int pfd, psinfo_t *psinfo, char *psargs)
+{
+ off_t argvoff = (off_t)psinfo->pr_argv;
+ size_t len;
+ char *psa = psargs;
+ int bsize = twidth;
+ int narg = NARG;
+ off_t argv[NARG];
+ off_t argoff;
+ off_t nextargoff;
+ int i;
+#ifdef _LP64
+ caddr32_t argv32[NARG];
+ int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64);
+#endif
+
+ if (psinfo->pr_nlwp == 0 ||
+ strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0)
+ goto out;
+
+ (void) memset(psa, 0, bsize--);
+ nextargoff = 0;
+ errno = EIO;
+ while (bsize > 0) {
+ if (narg == NARG) {
+ (void) memset(argv, 0, sizeof (argv));
+#ifdef _LP64
+ if (is32) {
+ if ((i = pread(pfd, argv32, sizeof (argv32),
+ argvoff)) <= 0) {
+ if (i == 0 || errno == EIO)
+ break;
+ return (-1);
+ }
+ for (i = 0; i < NARG; i++)
+ argv[i] = argv32[i];
+ } else
+#endif
+ if ((i = pread(pfd, argv, sizeof (argv),
+ argvoff)) <= 0) {
+ if (i == 0 || errno == EIO)
+ break;
+ return (-1);
+ }
+ narg = 0;
+ }
+ if ((argoff = argv[narg++]) == 0)
+ break;
+ if (argoff != nextargoff &&
+ (i = pread(pfd, psa, bsize, argoff)) <= 0) {
+ if (i == 0 || errno == EIO)
+ break;
+ return (-1);
+ }
+ len = strlen(psa);
+ psa += len;
+ *psa++ = ' ';
+ bsize -= len + 1;
+ nextargoff = argoff + len + 1;
+#ifdef _LP64
+ argvoff += is32? sizeof (caddr32_t) : sizeof (caddr_t);
+#else
+ argvoff += sizeof (caddr_t);
+#endif
+ }
+ while (psa > psargs && isspace(*(psa-1)))
+ psa--;
+
+out:
+ *psa = '\0';
+ if (strlen(psinfo->pr_psargs) > strlen(psargs))
+ (void) strcpy(psargs, psinfo->pr_psargs);
+
+ return (0);
+}
+
+/*
+ * Read environment variables from the process.
+ * Append them to psargs if there is room.
+ */
+static int
+preadenvs(int pfd, psinfo_t *psinfo, char *psargs)
+{
+ off_t envpoff = (off_t)psinfo->pr_envp;
+ int len;
+ char *psa;
+ char *psainit;
+ int bsize;
+ int nenv = NARG;
+ off_t envp[NARG];
+ off_t envoff;
+ off_t nextenvoff;
+ int i;
+#ifdef _LP64
+ caddr32_t envp32[NARG];
+ int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64);
+#endif
+
+ psainit = psa = (psargs != NULL)? psargs : psinfo->pr_psargs;
+ len = strlen(psa);
+ psa += len;
+ bsize = twidth - len - 1;
+
+ if (bsize <= 0 || psinfo->pr_nlwp == 0 ||
+ strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0)
+ return (0);
+
+ nextenvoff = 0;
+ errno = EIO;
+ while (bsize > 0) {
+ if (nenv == NARG) {
+ (void) memset(envp, 0, sizeof (envp));
+#ifdef _LP64
+ if (is32) {
+ if ((i = pread(pfd, envp32, sizeof (envp32),
+ envpoff)) <= 0) {
+ if (i == 0 || errno == EIO)
+ break;
+ return (-1);
+ }
+ for (i = 0; i < NARG; i++)
+ envp[i] = envp32[i];
+ } else
+#endif
+ if ((i = pread(pfd, envp, sizeof (envp),
+ envpoff)) <= 0) {
+ if (i == 0 || errno == EIO)
+ break;
+ return (-1);
+ }
+ nenv = 0;
+ }
+ if ((envoff = envp[nenv++]) == 0)
+ break;
+ if (envoff != nextenvoff &&
+ (i = pread(pfd, psa+1, bsize, envoff)) <= 0) {
+ if (i == 0 || errno == EIO)
+ break;
+ return (-1);
+ }
+ *psa++ = ' ';
+ len = strlen(psa);
+ psa += len;
+ bsize -= len + 1;
+ nextenvoff = envoff + len + 1;
+#ifdef _LP64
+ envpoff += is32? sizeof (caddr32_t) : sizeof (caddr_t);
+#else
+ envpoff += sizeof (caddr_t);
+#endif
+ }
+ while (psa > psainit && isspace(*(psa-1)))
+ psa--;
+ *psa = '\0';
+
+ return (0);
+}
+
+/*
+ * getarg() finds the next argument in list and copies arg into argbuf.
+ * p1 first pts to arg passed back from getopt routine. p1 is then
+ * bumped to next character that is not a comma or blank -- p1 NULL
+ * indicates end of list.
+ */
+
+static void
+getarg()
+{
+ char *parga;
+ int c;
+
+ while ((c = *p1) != '\0' && (c == ',' || isspace(c)))
+ p1++;
+
+ parga = argbuf;
+ while ((c = *p1) != '\0' && c != ',' && !isspace(c)) {
+ if (parga < argbuf + ARGSIZ - 1)
+ *parga++ = c;
+ p1++;
+ }
+ *parga = '\0';
+
+ while ((c = *p1) != '\0' && (c == ',' || isspace(c)))
+ p1++;
+}
+
+static char *
+devlookup(dev_t ddev)
+{
+ struct devl *dp;
+ int i;
+
+ for (dp = devl, i = 0; i < ndev; dp++, i++) {
+ if (dp->ddev == ddev)
+ return (dp->dname);
+ }
+ return (NULL);
+}
+
+static char *
+devadd(char *name, dev_t ddev)
+{
+ struct devl *dp;
+ int leng, start, i;
+
+ if (ndev == maxdev) {
+ maxdev += DNINCR;
+ devl = realloc(devl, maxdev * sizeof (struct devl));
+ if (devl == NULL) {
+ (void) fprintf(stderr,
+ "ps: not enough memory for %d devices\n", maxdev);
+ exit(1);
+ }
+ }
+ dp = &devl[ndev++];
+
+ dp->ddev = ddev;
+ if (name == NULL) {
+ (void) strcpy(dp->dname, "??");
+ return (dp->dname);
+ }
+
+ leng = strlen(name);
+ /* Strip off /dev/ */
+ if (leng < DNSIZE + 4)
+ (void) strcpy(dp->dname, &name[5]);
+ else {
+ start = leng - (DNSIZE - 1);
+
+ for (i = start; i < leng && name[i] != '/'; i++)
+ ;
+ if (i == leng)
+ (void) strlcpy(dp->dname, &name[start], DNSIZE);
+ else
+ (void) strlcpy(dp->dname, &name[i+1], DNSIZE);
+ }
+ return (dp->dname);
+}
+
+/*
+ * gettty returns the user's tty number or ? if none.
+ */
+static char *
+gettty(psinfo_t *psinfo)
+{
+ extern char *_ttyname_dev(dev_t, char *, size_t);
+ char devname[TTYNAME_MAX];
+ char *retval;
+
+ if (psinfo->pr_ttydev == PRNODEV)
+ return ("?");
+
+ if ((retval = devlookup(psinfo->pr_ttydev)) != NULL)
+ return (retval);
+
+ retval = _ttyname_dev(psinfo->pr_ttydev, devname, sizeof (devname));
+
+ return (devadd(retval, psinfo->pr_ttydev));
+}
+
+/*
+ * Print percent from 16-bit binary fraction [0 .. 1]
+ * Round up .01 to .1 to indicate some small percentage (the 0x7000 below).
+ */
+static void
+prtpct(ushort_t pct)
+{
+ uint_t value = pct; /* need 32 bits to compute with */
+
+ value = ((value * 1000) + 0x7000) >> 15; /* [0 .. 1000] */
+ (void) printf("%3u.%u", value / 10, value % 10);
+}
+
+/*
+ * Print info about the process.
+ */
+static int
+prcom(int found, psinfo_t *psinfo, char *psargs)
+{
+ char *cp;
+ char *tp;
+ char *psa;
+ long tm;
+ int i, wcnt, length;
+ wchar_t wchar;
+ struct tty *ttyp;
+
+ /*
+ * If process is zombie, call print routine and return.
+ */
+ if (psinfo->pr_nlwp == 0) {
+ if (tflg && !found)
+ return (0);
+ else {
+ przom(psinfo);
+ return (1);
+ }
+ }
+
+ /*
+ * Get current terminal. If none ("?") and 'a' is set, don't print
+ * info. If 't' is set, check if term is in list of desired terminals
+ * and print it if it is.
+ */
+ i = 0;
+ tp = gettty(psinfo);
+
+ if (*tp == '?' && !found && !xflg)
+ return (0);
+
+ if (!(*tp == '?' && aflg) && tflg && !found) {
+ int match = 0;
+ char *other = NULL;
+ for (ttyp = tty; ttyp->tname != NULL; ttyp++) {
+ /*
+ * Look for a name match
+ */
+ if (strcmp(tp, ttyp->tname) == 0) {
+ match = 1;
+ break;
+ }
+ /*
+ * Look for same device under different names.
+ */
+ if ((other == NULL) &&
+ (psinfo->pr_ttydev == ttyp->tdev))
+ other = ttyp->tname;
+ }
+ if (!match) {
+ if (other == NULL)
+ return (0);
+ tp = other;
+ }
+ }
+
+ if (lflg)
+ (void) printf("%2x", psinfo->pr_flag & 0377);
+ if (uflg) {
+ if (!nflg) {
+ struct passwd *pwd;
+
+ if ((pwd = getpwuid(psinfo->pr_euid)) != NULL)
+ /* USER */
+ (void) printf("%-8.8s", pwd->pw_name);
+ else
+ /* UID */
+ (void) printf(" %7.7d", (int)psinfo->pr_euid);
+ } else {
+ (void) printf(" %5d", (int)psinfo->pr_euid); /* UID */
+ }
+ } else if (lflg)
+ (void) printf(" %5d", (int)psinfo->pr_euid); /* UID */
+
+ (void) printf("%*d", pidwidth + 1, (int)psinfo->pr_pid); /* PID */
+ if (lflg)
+ (void) printf("%*d", pidwidth + 1,
+ (int)psinfo->pr_ppid); /* PPID */
+ if (lflg)
+ (void) printf("%3d", psinfo->pr_lwp.pr_cpu & 0377); /* CP */
+ if (uflg) {
+ prtpct(psinfo->pr_pctcpu); /* %CPU */
+ prtpct(psinfo->pr_pctmem); /* %MEM */
+ }
+ if (lflg) {
+ (void) printf("%4d", psinfo->pr_lwp.pr_pri); /* PRI */
+ (void) printf("%3d", psinfo->pr_lwp.pr_nice); /* NICE */
+ }
+ if (lflg || uflg) {
+ if (psinfo->pr_flag & SSYS) /* SZ */
+ (void) printf(" 0");
+ else if (psinfo->pr_size)
+ (void) printf("%5lu", (ulong_t)psinfo->pr_size);
+ else
+ (void) printf(" ?");
+ if (psinfo->pr_flag & SSYS) /* RSS */
+ (void) printf(" 0");
+ else if (psinfo->pr_rssize)
+ (void) printf("%5lu", (ulong_t)psinfo->pr_rssize);
+ else
+ (void) printf(" ?");
+ }
+ if (lflg) { /* WCHAN */
+ if (psinfo->pr_lwp.pr_sname != 'S') {
+ (void) printf(" ");
+ } else if (psinfo->pr_lwp.pr_wchan) {
+ (void) printf(" %+8.8lx",
+ (ulong_t)psinfo->pr_lwp.pr_wchan);
+ } else {
+ (void) printf(" ?");
+ }
+ }
+ if ((tplen = strlen(tp)) > 9)
+ maxlen = twidth - tplen + 9;
+ else
+ maxlen = twidth;
+
+ if (!lflg)
+ (void) printf(" %-8.14s", tp); /* TTY */
+ (void) printf(" %c", psinfo->pr_lwp.pr_sname); /* STATE */
+ if (lflg)
+ (void) printf(" %-8.14s", tp); /* TTY */
+ if (uflg)
+ prtime(psinfo->pr_start); /* START */
+
+ /* time just for process */
+ tm = psinfo->pr_time.tv_sec;
+ if (Sflg) { /* calculate time for process and all reaped children */
+ tm += psinfo->pr_ctime.tv_sec;
+ if (psinfo->pr_time.tv_nsec + psinfo->pr_ctime.tv_nsec
+ >= 1000000000)
+ tm += 1;
+ }
+
+ (void) printf(" %2ld:%.2ld", tm / 60, tm % 60); /* TIME */
+
+ if (vflg) {
+ if (psinfo->pr_flag & SSYS) /* SZ */
+ (void) printf(" 0");
+ else if (psinfo->pr_size)
+ (void) printf("%5lu", (ulong_t)psinfo->pr_size);
+ else
+ (void) printf(" ?");
+ if (psinfo->pr_flag & SSYS) /* SZ */
+ (void) printf(" 0");
+ else if (psinfo->pr_rssize)
+ (void) printf("%5lu", (ulong_t)psinfo->pr_rssize);
+ else
+ (void) printf(" ?");
+ prtpct(psinfo->pr_pctcpu); /* %CPU */
+ prtpct(psinfo->pr_pctmem); /* %MEM */
+ }
+ if (cflg) { /* CMD */
+ wcnt = namencnt(psinfo->pr_fname, 16, maxlen);
+ (void) printf(" %.*s", wcnt, psinfo->pr_fname);
+ return (1);
+ }
+ /*
+ * PRARGSZ == length of cmd arg string.
+ */
+ if (psargs == NULL) {
+ psa = &psinfo->pr_psargs[0];
+ i = PRARGSZ;
+ tp = &psinfo->pr_psargs[PRARGSZ];
+ } else {
+ psa = psargs;
+ i = strlen(psargs);
+ tp = psa + i;
+ }
+
+ for (cp = psa; cp < tp; /* empty */) {
+ if (*cp == 0)
+ break;
+ length = mbtowc(&wchar, cp, MB_LEN_MAX);
+ if (length < 0 || !iswprint(wchar)) {
+ (void) printf(" [ %.16s ]", psinfo->pr_fname);
+ return (1);
+ }
+ cp += length;
+ }
+ wcnt = namencnt(psa, i, maxlen);
+#if 0
+ /* dumps core on really long strings */
+ (void) printf(" %.*s", wcnt, psa);
+#else
+ (void) putchar(' ');
+ (void) fwrite(psa, 1, wcnt, stdout);
+#endif
+ return (1);
+}
+
+/*
+ * Print starting time of process unless process started more than 24 hours
+ * ago, in which case the date is printed.
+ */
+static void
+prtime(timestruc_t st)
+{
+ char sttim[26];
+ static time_t tim = 0L;
+ time_t starttime;
+
+ if (tim == 0L)
+ tim = time((time_t *)0);
+ starttime = st.tv_sec;
+ if (tim - starttime > 24*60*60) {
+ (void) strftime(sttim, sizeof (sttim), "%b %d",
+ localtime(&starttime));
+ } else {
+ (void) strftime(sttim, sizeof (sttim), "%H:%M:%S",
+ localtime(&starttime));
+ }
+ (void) printf("%9.9s", sttim);
+}
+
+static void
+przom(psinfo_t *psinfo)
+{
+ long tm;
+
+ if (lflg)
+ (void) printf("%2x", psinfo->pr_flag & 0377);
+ if (uflg) {
+ struct passwd *pwd;
+
+ if ((pwd = getpwuid(psinfo->pr_euid)) != NULL)
+ (void) printf("%-8.8s", pwd->pw_name); /* USER */
+ else
+ (void) printf(" %7.7d", (int)psinfo->pr_euid); /* UID */
+ } else if (lflg)
+ (void) printf(" %5d", (int)psinfo->pr_euid); /* UID */
+
+ (void) printf("%*d", pidwidth + 1, (int)psinfo->pr_pid); /* PID */
+ if (lflg)
+ (void) printf("%*d", pidwidth + 1,
+ (int)psinfo->pr_ppid); /* PPID */
+ if (lflg)
+ (void) printf(" 0"); /* CP */
+ if (uflg) {
+ prtpct(0); /* %CPU */
+ prtpct(0); /* %MEM */
+ }
+ if (lflg) {
+ (void) printf("%4d", psinfo->pr_lwp.pr_pri); /* PRI */
+ (void) printf(" "); /* NICE */
+ }
+ if (lflg || uflg) {
+ (void) printf(" 0"); /* SZ */
+ (void) printf(" 0"); /* RSS */
+ }
+ if (lflg)
+ (void) printf(" "); /* WCHAN */
+ (void) printf(" "); /* TTY */
+ (void) printf("%c", psinfo->pr_lwp.pr_sname); /* STATE */
+ if (uflg)
+ (void) printf(" "); /* START */
+
+ /* time just for process */
+ tm = psinfo->pr_time.tv_sec;
+ if (Sflg) { /* calculate time for process and all reaped children */
+ tm += psinfo->pr_ctime.tv_sec;
+ if (psinfo->pr_time.tv_nsec + psinfo->pr_ctime.tv_nsec
+ >= 1000000000)
+ tm += 1;
+ }
+ (void) printf(" %2ld:%.2ld", tm / 60, tm % 60); /* TIME */
+
+ if (vflg) {
+ (void) printf(" 0"); /* SZ */
+ (void) printf(" 0"); /* RSS */
+ prtpct(0); /* %CPU */
+ prtpct(0); /* %MEM */
+ }
+ (void) printf(" %.*s", maxlen, " <defunct>");
+}
+
+/*
+ * Returns true iff string is all numeric.
+ */
+static int
+num(char *s)
+{
+ int c;
+
+ if (s == NULL)
+ return (0);
+ c = *s;
+ do {
+ if (!isdigit(c))
+ return (0);
+ } while ((c = *++s) != '\0');
+ return (1);
+}
+
+/*
+ * Function to compute the number of printable bytes in a multibyte
+ * command string ("internationalization").
+ */
+static int
+namencnt(char *cmd, int eucsize, int scrsize)
+{
+ int eucwcnt = 0, scrwcnt = 0;
+ int neucsz, nscrsz;
+ wchar_t wchar;
+
+ while (*cmd != '\0') {
+ if ((neucsz = mbtowc(&wchar, cmd, MB_LEN_MAX)) < 0)
+ return (8); /* default to use for illegal chars */
+ if ((nscrsz = scrwidth(wchar)) == 0)
+ return (8);
+ if (eucwcnt + neucsz > eucsize || scrwcnt + nscrsz > scrsize)
+ break;
+ eucwcnt += neucsz;
+ scrwcnt += nscrsz;
+ cmd += neucsz;
+ }
+ return (eucwcnt);
+}
+
+static int
+pscompare(const void *v1, const void *v2)
+{
+ const struct psent *p1 = v1;
+ const struct psent *p2 = v2;
+ int i;
+
+ if (uflg)
+ i = p2->psinfo->pr_pctcpu - p1->psinfo->pr_pctcpu;
+ else if (vflg)
+ i = p2->psinfo->pr_rssize - p1->psinfo->pr_rssize;
+ else
+ i = p1->psinfo->pr_ttydev - p2->psinfo->pr_ttydev;
+ if (i == 0)
+ i = p1->psinfo->pr_pid - p2->psinfo->pr_pid;
+ return (i);
+}
+
+static char *
+err_string(int err)
+{
+ static char buf[32];
+ char *str = strerror(err);
+
+ if (str == NULL)
+ (void) sprintf(str = buf, "Errno #%d", err);
+
+ return (str);
+}