ucbps.c revision fba40a479337f3eb612fb77d692257a45d282023
0N/A/*
0N/A * CDDL HEADER START
0N/A *
0N/A * The contents of this file are subject to the terms of the
0N/A * Common Development and Distribution License (the "License").
0N/A * You may not use this file except in compliance with the License.
0N/A *
0N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
0N/A * or http://www.opensolaris.org/os/licensing.
0N/A * See the License for the specific language governing permissions
0N/A * and limitations under the License.
0N/A *
0N/A * When distributing Covered Code, include this CDDL HEADER in each
0N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
0N/A * If applicable, add the following below this CDDL HEADER, with the
0N/A * fields enclosed by brackets "[]" replaced with your own identifying
0N/A * information: Portions Copyright [yyyy] [name of copyright owner]
0N/A *
0N/A * CDDL HEADER END
0N/A */
0N/A/*
0N/A * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
0N/A * Use is subject to license terms.
0N/A */
0N/A
0N/A/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
0N/A/* All Rights Reserved */
0N/A
0N/A/*
0N/A * University Copyright- Copyright (c) 1982, 1986, 1988
0N/A * The Regents of the University of California
0N/A * All Rights Reserved
0N/A *
0N/A * University Acknowledgment- Portions of this document are derived from
0N/A * software developed by the University of California, Berkeley, and its
0N/A * contributors.
0N/A */
0N/A
0N/A/*
0N/A * ps -- print things about processes.
0N/A */
0N/A
0N/A#define _SYSCALL32
0N/A
0N/A#include <stdio.h>
0N/A#include <ctype.h>
0N/A#include <string.h>
0N/A#include <errno.h>
0N/A#include <fcntl.h>
0N/A#include <pwd.h>
0N/A#include <sys/types.h>
0N/A#include <sys/stat.h>
0N/A#include <sys/mkdev.h>
0N/A#include <unistd.h>
0N/A#include <stdlib.h>
0N/A#include <limits.h>
0N/A#include <dirent.h>
0N/A#include <procfs.h>
0N/A#include <sys/param.h>
0N/A#include <sys/ttold.h>
0N/A#include <libelf.h>
0N/A#include <gelf.h>
0N/A#include <locale.h>
0N/A#include <wctype.h>
0N/A#include <stdarg.h>
0N/A#include <sys/proc.h>
0N/A#include <priv_utils.h>
0N/A#include <zone.h>
0N/A
0N/A#define NTTYS 2 /* max ttys that can be specified with the -t option */
0N/A /* only one tty can be specified with SunOS ps */
0N/A#define SIZ 30 /* max processes that can be specified with -p and -g */
0N/A#define ARGSIZ 30 /* size of buffer holding args for -t, -p, -u options */
0N/A
0N/A#define FSTYPE_MAX 8
0N/A
0N/Astruct psent {
0N/A psinfo_t *psinfo;
0N/A char *psargs;
0N/A int found;
0N/A};
0N/A
0N/Astatic int tplen, maxlen, twidth;
0N/Astatic char hdr[81];
0N/Astatic struct winsize win;
0N/A
0N/Astatic int retcode = 1;
0N/Astatic int lflg; /* long format */
0N/Astatic int uflg; /* user-oriented output */
0N/Astatic int aflg; /* Display all processes */
0N/Astatic int eflg; /* Display environment as well as arguments */
0N/Astatic int gflg; /* Display process group leaders */
0N/Astatic int tflg; /* Processes running on specific terminals */
0N/Astatic int rflg; /* Running processes only flag */
0N/Astatic int Sflg; /* Accumulated time plus all reaped children */
0N/Astatic int xflg; /* Include processes with no controlling tty */
0N/Astatic int cflg; /* Display command name */
0N/Astatic int vflg; /* Virtual memory-oriented output */
0N/Astatic int nflg; /* Numerical output */
0N/Astatic int pflg; /* Specific process id passed as argument */
0N/Astatic int Uflg; /* Update private database, ups_data */
0N/Astatic int errflg;
0N/A
0N/Astatic char *gettty();
0N/Astatic char argbuf[ARGSIZ];
0N/Astatic char *parg;
0N/Astatic char *p1; /* points to successive option arguments */
0N/Astatic uid_t my_uid;
0N/Astatic char stdbuf[BUFSIZ];
0N/A
0N/Astatic int ndev; /* number of devices */
0N/Astatic int maxdev; /* number of devl structures allocated */
0N/A
0N/A#define DNINCR 100
0N/A#define DNSIZE 14
0N/Astatic struct devl { /* device list */
0N/A char dname[DNSIZE]; /* device name */
0N/A dev_t ddev; /* device number */
0N/A} *devl;
0N/A
0N/Astatic struct tty {
0N/A char *tname;
0N/A dev_t tdev;
0N/A} tty[NTTYS]; /* for t option */
0N/Astatic int ntty = 0;
0N/Astatic pid_t pidsave;
0N/Astatic int pidwidth;
0N/A
0N/Astatic char *procdir = "/proc"; /* standard /proc directory */
0N/Astatic void usage(); /* print usage message and quit */
0N/Astatic void getarg(void);
0N/Astatic void prtime(timestruc_t st);
0N/Astatic void przom(psinfo_t *psinfo);
0N/Astatic int num(char *);
0N/Astatic int preadargs(int, psinfo_t *, char *);
0N/Astatic int preadenvs(int, psinfo_t *, char *);
0N/Astatic int prcom(int, psinfo_t *, char *);
0N/Astatic int namencnt(char *, int, int);
0N/Astatic int pscompare(const void *, const void *);
0N/Astatic char *err_string(int);
0N/A
0N/Aextern int scrwidth(wchar_t); /* header file? */
0N/A
0N/Aint
0N/Aucbmain(int argc, char **argv)
0N/A{
0N/A psinfo_t info; /* process information structure from /proc */
0N/A char *psargs = NULL; /* pointer to buffer for -w and -ww options */
0N/A char *svpsargs = NULL;
0N/A struct psent *psent;
0N/A int entsize;
0N/A int nent;
0N/A pid_t maxpid;
0N/A
0N/A struct tty *ttyp = tty;
0N/A char *tmp;
0N/A char *p;
0N/A int c;
0N/A pid_t pid; /* pid: process id */
0N/A pid_t ppid; /* ppid: parent process id */
0N/A int i, found;
0N/A
0N/A size_t size;
0N/A
0N/A DIR *dirp;
0N/A struct dirent *dentp;
0N/A char psname[100];
0N/A char asname[100];
0N/A int pdlen;
0N/A size_t len;
0N/A
0N/A (void) setlocale(LC_ALL, "");
0N/A
0N/A my_uid = getuid();
0N/A
0N/A /*
0N/A * This program needs the proc_owner privilege
0N/A */
0N/A (void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_PROC_OWNER,
0N/A (char *)NULL);
0N/A
0N/A /*
0N/A * calculate width of pid fields based on configured MAXPID
0N/A * (must be at least 5 to retain output format compatibility)
0N/A */
0N/A maxpid = (pid_t)sysconf(_SC_MAXPID);
0N/A pidwidth = 1;
0N/A while ((maxpid /= 10) > 0)
0N/A ++pidwidth;
0N/A pidwidth = pidwidth < 5 ? 5 : pidwidth;
0N/A
0N/A if (ioctl(1, TIOCGWINSZ, &win) == -1)
0N/A twidth = 80;
0N/A else
0N/A twidth = (win.ws_col == 0 ? 80 : win.ws_col);
0N/A
0N/A /* add the '-' for BSD compatibility */
0N/A if (argc > 1) {
0N/A if (argv[1][0] != '-' && !isdigit(argv[1][0])) {
0N/A len = strlen(argv[1]) + 2;
0N/A tmp = malloc(len);
0N/A if (tmp != NULL) {
0N/A (void) snprintf(tmp, len, "%s%s", "-", argv[1]);
0N/A argv[1] = tmp;
0N/A }
0N/A }
0N/A }
0N/A
0N/A setbuf(stdout, stdbuf);
0N/A while ((c = getopt(argc, argv, "lcaengrSt:xuvwU")) != EOF)
0N/A switch (c) {
0N/A case 'g':
0N/A gflg++; /* include process group leaders */
0N/A break;
0N/A case 'c': /* display internal command name */
0N/A cflg++;
0N/A break;
0N/A case 'r': /* restrict output to running processes */
0N/A rflg++;
0N/A break;
0N/A case 'S': /* display time by process and all reaped children */
0N/A Sflg++;
0N/A break;
0N/A case 'x': /* process w/o controlling tty */
0N/A xflg++;
0N/A break;
0N/A case 'l': /* long listing */
0N/A lflg++;
0N/A uflg = vflg = 0;
0N/A break;
0N/A case 'u': /* user-oriented output */
0N/A uflg++;
0N/A lflg = vflg = 0;
0N/A break;
0N/A case 'U': /* update private database ups_data */
0N/A Uflg++;
0N/A break;
0N/A case 'w': /* increase display width */
0N/A if (twidth < 132)
0N/A twidth = 132;
0N/A else /* second w option */
0N/A twidth = NCARGS;
0N/A break;
0N/A case 'v': /* display virtual memory format */
0N/A vflg++;
0N/A lflg = uflg = 0;
0N/A break;
0N/A case 'a':
0N/A /*
0N/A * display all processes except process group
0N/A * leaders and processes w/o controlling tty
0N/A */
0N/A aflg++;
0N/A gflg++;
0N/A break;
0N/A case 'e':
0N/A /* Display environment along with aguments. */
0N/A eflg++;
0N/A break;
0N/A case 'n': /* Display numerical output */
0N/A nflg++;
0N/A break;
0N/A case 't': /* restrict output to named terminal */
0N/A#define TSZ 30
0N/A tflg++;
0N/A gflg++;
0N/A xflg = 0;
0N/A
0N/A p1 = optarg;
0N/A do { /* only loop through once (NTTYS = 2) */
0N/A parg = argbuf;
0N/A if (ntty >= NTTYS-1)
0N/A break;
0N/A getarg();
0N/A if ((p = malloc(TSZ+1)) == NULL) {
0N/A (void) fprintf(stderr,
0N/A "ps: no memory\n");
0N/A exit(1);
0N/A }
0N/A p[0] = '\0';
0N/A size = TSZ;
0N/A if (isdigit(*parg)) {
0N/A (void) strcpy(p, "tty");
0N/A size -= 3;
0N/A }
0N/A
0N/A (void) strncat(p, parg, size);
0N/A ttyp->tdev = PRNODEV;
0N/A if (parg && *parg == '?')
0N/A xflg++;
0N/A else {
0N/A char nambuf[TSZ+6]; /* for /dev/+\0 */
0N/A struct stat64 s;
0N/A (void) strcpy(nambuf, "/dev/");
0N/A (void) strcat(nambuf, p);
0N/A if (stat64(nambuf, &s) == 0)
0N/A ttyp->tdev = s.st_rdev;
0N/A }
0N/A ttyp++->tname = p;
0N/A ntty++;
0N/A } while (*p1);
0N/A break;
0N/A default: /* error on ? */
0N/A errflg++;
0N/A break;
0N/A }
0N/A
0N/A if (errflg)
0N/A usage();
0N/A
0N/A if (optind + 1 < argc) { /* more than one additional argument */
0N/A (void) fprintf(stderr, "ps: too many arguments\n");
0N/A usage();
0N/A }
0N/A
0N/A /*
0N/A * The -U option is obsolete. Attempts to use it cause ps to exit
0N/A * without printing anything.
0N/A */
0N/A if (Uflg)
0N/A exit(0);
0N/A
0N/A if (optind < argc) { /* user specified a specific proc id */
0N/A pflg++;
0N/A p1 = argv[optind];
0N/A parg = argbuf;
0N/A getarg();
0N/A if (!num(parg)) {
0N/A (void) fprintf(stderr,
0N/A "ps: %s is an invalid non-numeric argument for a process id\n", parg);
0N/A usage();
0N/A }
0N/A pidsave = (pid_t)atol(parg);
0N/A aflg = rflg = xflg = 0;
0N/A gflg++;
0N/A }
0N/A
0N/A if (tflg)
0N/A ttyp->tname = NULL;
0N/A
0N/A /* allocate an initial guess for the number of processes */
0N/A entsize = 1024;
0N/A psent = malloc(entsize * sizeof (struct psent));
0N/A if (psent == NULL) {
0N/A (void) fprintf(stderr, "ps: no memory\n");
0N/A exit(1);
0N/A }
0N/A nent = 0; /* no active entries yet */
0N/A
0N/A if (lflg) {
0N/A (void) sprintf(hdr,
0N/A " F UID%*s%*s %%C PRI NI SZ RSS "
0N/A "WCHAN S TT TIME COMMAND", pidwidth + 1, "PID",
0N/A pidwidth + 1, "PPID");
0N/A } else if (uflg) {
0N/A if (nflg)
0N/A (void) sprintf(hdr,
0N/A " UID%*s %%CPU %%MEM SZ RSS "
0N/A "TT S START TIME COMMAND",
0N/A pidwidth + 1, "PID");
0N/A else
0N/A (void) sprintf(hdr,
0N/A "USER %*s %%CPU %%MEM SZ RSS "
0N/A "TT S START TIME COMMAND",
0N/A pidwidth + 1, "PID");
0N/A } else if (vflg) {
0N/A (void) sprintf(hdr,
0N/A "%*s TT S TIME SIZE RSS %%CPU %%MEM "
0N/A "COMMAND", pidwidth + 1, "PID");
0N/A } else
0N/A (void) sprintf(hdr, "%*s TT S TIME COMMAND",
0N/A pidwidth + 1, "PID");
0N/A
0N/A twidth = twidth - strlen(hdr) + 6;
0N/A (void) printf("%s\n", hdr);
0N/A
0N/A if (twidth > PRARGSZ && (psargs = malloc(twidth)) == NULL) {
0N/A (void) fprintf(stderr, "ps: no memory\n");
0N/A exit(1);
0N/A }
0N/A svpsargs = psargs;
0N/A
0N/A /*
0N/A * Determine which processes to print info about by searching
0N/A * the /proc directory and looking at each process.
0N/A */
0N/A if ((dirp = opendir(procdir)) == NULL) {
0N/A (void) fprintf(stderr, "ps: cannot open PROC directory %s\n",
0N/A procdir);
0N/A exit(1);
0N/A }
0N/A
0N/A (void) strcpy(psname, procdir);
0N/A pdlen = strlen(psname);
0N/A psname[pdlen++] = '/';
0N/A
0N/A /* for each active process --- */
0N/A while (dentp = readdir(dirp)) {
0N/A int psfd; /* file descriptor for /proc/nnnnn/psinfo */
0N/A int asfd; /* file descriptor for /proc/nnnnn/as */
0N/A
0N/A if (dentp->d_name[0] == '.') /* skip . and .. */
0N/A continue;
0N/A (void) strcpy(psname + pdlen, dentp->d_name);
0N/A (void) strcpy(asname, psname);
0N/A (void) strcat(psname, "/psinfo");
0N/A (void) strcat(asname, "/as");
0N/Aretry:
0N/A if ((psfd = open(psname, O_RDONLY)) == -1)
0N/A continue;
0N/A asfd = -1;
0N/A if (psargs != NULL || eflg) {
0N/A
0N/A /* now we need the proc_owner privilege */
0N/A (void) __priv_bracket(PRIV_ON);
0N/A
0N/A asfd = open(asname, O_RDONLY);
0N/A
0N/A /* drop proc_owner privilege after open */
0N/A (void) __priv_bracket(PRIV_OFF);
0N/A }
0N/A
0N/A /*
0N/A * Get the info structure for the process
0N/A */
0N/A if (read(psfd, &info, sizeof (info)) != sizeof (info)) {
0N/A int saverr = errno;
0N/A
0N/A (void) close(psfd);
0N/A if (asfd > 0)
0N/A (void) close(asfd);
0N/A if (saverr == EAGAIN)
0N/A goto retry;
0N/A if (saverr != ENOENT)
0N/A (void) fprintf(stderr, "ps: read() on %s: %s\n",
0N/A psname, err_string(saverr));
0N/A continue;
0N/A }
0N/A (void) close(psfd);
0N/A
0N/A found = 0;
0N/A if (info.pr_lwp.pr_state == 0) /* can't happen? */
0N/A goto closeit;
0N/A pid = info.pr_pid;
0N/A ppid = info.pr_ppid;
0N/A
0N/A /* Display only process from command line */
0N/A if (pflg) { /* pid in arg list */
0N/A if (pidsave == pid)
0N/A found++;
0N/A else
0N/A goto closeit;
0N/A }
0N/A
0N/A /*
0N/A * Omit "uninteresting" processes unless 'g' option.
0N/A */
0N/A if ((ppid == 1) && !(gflg))
0N/A goto closeit;
0N/A
0N/A /*
0N/A * Omit non-running processes for 'r' option
0N/A */
0N/A if (rflg &&
0N/A !(info.pr_lwp.pr_sname == 'O' ||
0N/A info.pr_lwp.pr_sname == 'R'))
0N/A goto closeit;
0N/A
0N/A if (!found && !tflg && !aflg && info.pr_euid != my_uid)
0N/A goto closeit;
0N/A
0N/A /*
0N/A * Read the args for the -w and -ww cases
0N/A */
0N/A if (asfd > 0) {
0N/A if ((psargs != NULL &&
0N/A preadargs(asfd, &info, psargs) == -1) ||
0N/A (eflg && preadenvs(asfd, &info, psargs) == -1)) {
0N/A int saverr = errno;
0N/A
0N/A (void) close(asfd);
0N/A if (saverr == EAGAIN)
0N/A goto retry;
0N/A if (saverr != ENOENT)
0N/A (void) fprintf(stderr,
0N/A "ps: read() on %s: %s\n",
0N/A asname, err_string(saverr));
0N/A continue;
0N/A }
0N/A } else {
0N/A psargs = info.pr_psargs;
0N/A }
0N/A
0N/A if (nent >= entsize) {
0N/A entsize *= 2;
0N/A psent = (struct psent *)realloc((char *)psent,
0N/A entsize * sizeof (struct psent));
0N/A if (psent == NULL) {
0N/A (void) fprintf(stderr, "ps: no memory\n");
0N/A exit(1);
0N/A }
0N/A }
0N/A if ((psent[nent].psinfo = malloc(sizeof (psinfo_t)))
0N/A == NULL) {
0N/A (void) fprintf(stderr, "ps: no memory\n");
0N/A exit(1);
0N/A }
0N/A *psent[nent].psinfo = info;
0N/A if (psargs == NULL)
0N/A psent[nent].psargs = NULL;
0N/A else {
0N/A if ((psent[nent].psargs = malloc(strlen(psargs)+1))
0N/A == NULL) {
0N/A (void) fprintf(stderr, "ps: no memory\n");
0N/A exit(1);
0N/A }
0N/A (void) strcpy(psent[nent].psargs, psargs);
0N/A }
0N/A psent[nent].found = found;
0N/A nent++;
0N/Acloseit:
0N/A if (asfd > 0)
0N/A (void) close(asfd);
0N/A psargs = svpsargs;
0N/A }
0N/A
0N/A /* revert to non-privileged user */
0N/A (void) __priv_relinquish();
0N/A
0N/A (void) closedir(dirp);
0N/A
0N/A qsort((char *)psent, nent, sizeof (psent[0]), pscompare);
0N/A
0N/A for (i = 0; i < nent; i++) {
0N/A struct psent *pp = &psent[i];
0N/A if (prcom(pp->found, pp->psinfo, pp->psargs)) {
0N/A (void) printf("\n");
0N/A retcode = 0;
0N/A }
0N/A }
0N/A
0N/A return (retcode);
0N/A}
0N/A
0N/Astatic void
0N/Ausage() /* print usage message and quit */
0N/A{
0N/A static char usage1[] = "ps [ -aceglnrSuUvwx ] [ -t term ] [ num ]";
0N/A
0N/A (void) fprintf(stderr, "usage: %s\n", usage1);
0N/A exit(1);
0N/A}
0N/A
0N/A/*
0N/A * Read the process arguments from the process.
0N/A * This allows >PRARGSZ characters of arguments to be displayed but,
0N/A * unlike pr_psargs[], the process may have changed them.
0N/A */
0N/A#define NARG 100
0N/Astatic int
0N/Apreadargs(int pfd, psinfo_t *psinfo, char *psargs)
0N/A{
0N/A off_t argvoff = (off_t)psinfo->pr_argv;
0N/A size_t len;
0N/A char *psa = psargs;
0N/A int bsize = twidth;
0N/A int narg = NARG;
0N/A off_t argv[NARG];
0N/A off_t argoff;
0N/A off_t nextargoff;
0N/A int i;
0N/A#ifdef _LP64
0N/A caddr32_t argv32[NARG];
0N/A int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64);
0N/A#endif
0N/A
0N/A if (psinfo->pr_nlwp == 0 ||
0N/A 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);
static zoneid_t zid = -1;
char devname[TTYNAME_MAX];
char *retval;
if (zid == -1)
zid = getzoneid();
if (psinfo->pr_ttydev == PRNODEV || psinfo->pr_zoneid != zid)
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);
}