Home | History | Annotate | Download | only in ps
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
     28 /*	  All Rights Reserved	*/
     29 
     30 /*
     31  * University Copyright- Copyright (c) 1982, 1986, 1988
     32  * The Regents of the University of California
     33  * All Rights Reserved
     34  *
     35  * University Acknowledgment- Portions of this document are derived from
     36  * software developed by the University of California, Berkeley, and its
     37  * contributors.
     38  */
     39 
     40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     41 
     42 /*
     43  * ps -- print things about processes.
     44  */
     45 
     46 #define	_SYSCALL32
     47 
     48 #include <stdio.h>
     49 #include <ctype.h>
     50 #include <string.h>
     51 #include <errno.h>
     52 #include <fcntl.h>
     53 #include <pwd.h>
     54 #include <sys/types.h>
     55 #include <sys/stat.h>
     56 #include <sys/mkdev.h>
     57 #include <unistd.h>
     58 #include <stdlib.h>
     59 #include <limits.h>
     60 #include <dirent.h>
     61 #include <procfs.h>
     62 #include <sys/param.h>
     63 #include <sys/ttold.h>
     64 #include <libelf.h>
     65 #include <gelf.h>
     66 #include <locale.h>
     67 #include <wctype.h>
     68 #include <stdarg.h>
     69 #include <sys/proc.h>
     70 #include <priv_utils.h>
     71 
     72 #define	NTTYS	2	/* max ttys that can be specified with the -t option */
     73 			/* only one tty can be specified with SunOS ps */
     74 #define	SIZ	30	/* max processes that can be specified with -p and -g */
     75 #define	ARGSIZ	30	/* size of buffer holding args for -t, -p, -u options */
     76 
     77 #define	FSTYPE_MAX	8
     78 
     79 struct psent {
     80 	psinfo_t *psinfo;
     81 	char *psargs;
     82 	int found;
     83 };
     84 
     85 static	int	tplen, maxlen, twidth;
     86 static	char	hdr[81];
     87 static	struct	winsize win;
     88 
     89 static	int	retcode = 1;
     90 static	int	lflg;	/* long format */
     91 static	int	uflg;	/* user-oriented output */
     92 static	int	aflg;	/* Display all processes */
     93 static	int	eflg;	/* Display environment as well as arguments */
     94 static	int	gflg;	/* Display process group leaders */
     95 static	int	tflg;	/* Processes running on specific terminals */
     96 static	int	rflg;	/* Running processes only flag */
     97 static	int	Sflg;	/* Accumulated time plus all reaped children */
     98 static	int	xflg;	/* Include processes with no controlling tty */
     99 static	int	cflg;	/* Display command name */
    100 static	int	vflg;	/* Virtual memory-oriented output */
    101 static	int	nflg;	/* Numerical output */
    102 static	int	pflg;	/* Specific process id passed as argument */
    103 static	int	Uflg;	/* Update private database, ups_data */
    104 static	int	errflg;
    105 
    106 static	char	*gettty();
    107 static	char	argbuf[ARGSIZ];
    108 static	char	*parg;
    109 static	char	*p1;		/* points to successive option arguments */
    110 static	uid_t	my_uid;
    111 static char	stdbuf[BUFSIZ];
    112 
    113 static	int	ndev;		/* number of devices */
    114 static	int	maxdev;		/* number of devl structures allocated */
    115 
    116 #define	DNINCR	100
    117 #define	DNSIZE	14
    118 static	struct devl {		/* device list	 */
    119 	char	dname[DNSIZE];	/* device name	 */
    120 	dev_t	ddev;		/* device number */
    121 } *devl;
    122 
    123 static	struct tty {
    124 	char *tname;
    125 	dev_t tdev;
    126 } tty[NTTYS];			/* for t option */
    127 static	int	ntty = 0;
    128 static	pid_t	pidsave;
    129 static	int	pidwidth;
    130 
    131 static	char	*procdir = "/proc";	/* standard /proc directory */
    132 static	void	usage();		/* print usage message and quit */
    133 static	void	getarg(void);
    134 static	void	prtime(timestruc_t st);
    135 static	void	przom(psinfo_t *psinfo);
    136 static	int	num(char *);
    137 static	int	preadargs(int, psinfo_t *, char *);
    138 static	int	preadenvs(int, psinfo_t *, char *);
    139 static	int	prcom(int, psinfo_t *, char *);
    140 static	int	namencnt(char *, int, int);
    141 static	int	pscompare(const void *, const void *);
    142 static	char	*err_string(int);
    143 
    144 extern int	scrwidth(wchar_t);	/* header file? */
    145 
    146 int
    147 main(int argc, char **argv)
    148 {
    149 	psinfo_t info;		/* process information structure from /proc */
    150 	char *psargs = NULL;	/* pointer to buffer for -w and -ww options */
    151 	char *svpsargs = NULL;
    152 	struct psent *psent;
    153 	int entsize;
    154 	int nent;
    155 	pid_t maxpid;
    156 
    157 	struct tty *ttyp = tty;
    158 	char	*tmp;
    159 	char	*p;
    160 	int	c;
    161 	pid_t	pid;		/* pid: process id */
    162 	pid_t	ppid;		/* ppid: parent process id */
    163 	int	i, found;
    164 
    165 	size_t	size;
    166 
    167 	DIR *dirp;
    168 	struct dirent *dentp;
    169 	char	psname[100];
    170 	char	asname[100];
    171 	int	pdlen;
    172 	size_t  len;
    173 
    174 	(void) setlocale(LC_ALL, "");
    175 
    176 	my_uid = getuid();
    177 
    178 	/*
    179 	 * This program needs the proc_owner privilege
    180 	 */
    181 	(void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_PROC_OWNER,
    182 	    (char *)NULL);
    183 
    184 	/*
    185 	 * calculate width of pid fields based on configured MAXPID
    186 	 * (must be at least 5 to retain output format compatibility)
    187 	 */
    188 	maxpid = (pid_t)sysconf(_SC_MAXPID);
    189 	pidwidth = 1;
    190 	while ((maxpid /= 10) > 0)
    191 		++pidwidth;
    192 	pidwidth = pidwidth < 5 ? 5 : pidwidth;
    193 
    194 	if (ioctl(1, TIOCGWINSZ, &win) == -1)
    195 		twidth = 80;
    196 	else
    197 		twidth = (win.ws_col == 0 ? 80 : win.ws_col);
    198 
    199 	/* add the '-' for BSD compatibility */
    200 	if (argc > 1) {
    201 		if (argv[1][0] != '-' && !isdigit(argv[1][0])) {
    202 			len = strlen(argv[1]) + 2;
    203 			tmp = malloc(len);
    204 			if (tmp != NULL) {
    205 				(void) snprintf(tmp, len, "%s%s", "-", argv[1]);
    206 				argv[1] = tmp;
    207 			}
    208 		}
    209 	}
    210 
    211 	setbuf(stdout, stdbuf);
    212 	while ((c = getopt(argc, argv, "lcaengrSt:xuvwU")) != EOF)
    213 		switch (c) {
    214 		case 'g':
    215 			gflg++;	/* include process group leaders */
    216 			break;
    217 		case 'c':	/* display internal command name */
    218 			cflg++;
    219 			break;
    220 		case 'r':	/* restrict output to running processes */
    221 			rflg++;
    222 			break;
    223 		case 'S': /* display time by process and all reaped children */
    224 			Sflg++;
    225 			break;
    226 		case 'x':	/* process w/o controlling tty */
    227 			xflg++;
    228 			break;
    229 		case 'l':	/* long listing */
    230 			lflg++;
    231 			uflg = vflg = 0;
    232 			break;
    233 		case 'u':	/* user-oriented output */
    234 			uflg++;
    235 			lflg = vflg = 0;
    236 			break;
    237 		case 'U':	/* update private database ups_data */
    238 			Uflg++;
    239 			break;
    240 		case 'w':	/* increase display width */
    241 			if (twidth < 132)
    242 				twidth = 132;
    243 			else	/* second w option */
    244 				twidth = NCARGS;
    245 			break;
    246 		case 'v':	/* display virtual memory format */
    247 			vflg++;
    248 			lflg = uflg = 0;
    249 			break;
    250 		case 'a':
    251 			/*
    252 			 * display all processes except process group
    253 			 * leaders and processes w/o controlling tty
    254 			 */
    255 			aflg++;
    256 			gflg++;
    257 			break;
    258 		case 'e':
    259 			/* Display environment along with aguments. */
    260 			eflg++;
    261 			break;
    262 		case 'n':	/* Display numerical output */
    263 			nflg++;
    264 			break;
    265 		case 't':	/* restrict output to named terminal */
    266 #define	TSZ	30
    267 			tflg++;
    268 			gflg++;
    269 			xflg = 0;
    270 
    271 			p1 = optarg;
    272 			do {	/* only loop through once (NTTYS = 2) */
    273 				parg = argbuf;
    274 				if (ntty >= NTTYS-1)
    275 					break;
    276 				getarg();
    277 				if ((p = malloc(TSZ+1)) == NULL) {
    278 					(void) fprintf(stderr,
    279 					    "ps: no memory\n");
    280 					exit(1);
    281 				}
    282 				p[0] = '\0';
    283 				size = TSZ;
    284 				if (isdigit(*parg)) {
    285 					(void) strcpy(p, "tty");
    286 					size -= 3;
    287 				}
    288 
    289 				(void) strncat(p, parg, size);
    290 				ttyp->tdev = PRNODEV;
    291 				if (parg && *parg == '?')
    292 					xflg++;
    293 				else {
    294 					char nambuf[TSZ+6]; /* for /dev/+\0 */
    295 					struct stat64 s;
    296 					(void) strcpy(nambuf, "/dev/");
    297 					(void) strcat(nambuf, p);
    298 					if (stat64(nambuf, &s) == 0)
    299 						ttyp->tdev = s.st_rdev;
    300 				}
    301 				ttyp++->tname = p;
    302 				ntty++;
    303 			} while (*p1);
    304 			break;
    305 		default:			/* error on ? */
    306 			errflg++;
    307 			break;
    308 		}
    309 
    310 	if (errflg)
    311 		usage();
    312 
    313 	if (optind + 1 < argc) { /* more than one additional argument */
    314 		(void) fprintf(stderr, "ps: too many arguments\n");
    315 		usage();
    316 	}
    317 
    318 	/*
    319 	 * The -U option is obsolete.  Attempts to use it cause ps to exit
    320 	 * without printing anything.
    321 	 */
    322 	if (Uflg)
    323 		exit(0);
    324 
    325 	if (optind < argc) { /* user specified a specific proc id */
    326 		pflg++;
    327 		p1 = argv[optind];
    328 		parg = argbuf;
    329 		getarg();
    330 		if (!num(parg)) {
    331 			(void) fprintf(stderr,
    332 	"ps: %s is an invalid non-numeric argument for a process id\n", parg);
    333 			usage();
    334 		}
    335 		pidsave = (pid_t)atol(parg);
    336 		aflg = rflg = xflg = 0;
    337 		gflg++;
    338 	}
    339 
    340 	if (tflg)
    341 		ttyp->tname = NULL;
    342 
    343 	/* allocate an initial guess for the number of processes */
    344 	entsize = 1024;
    345 	psent = malloc(entsize * sizeof (struct psent));
    346 	if (psent == NULL) {
    347 		(void) fprintf(stderr, "ps: no memory\n");
    348 		exit(1);
    349 	}
    350 	nent = 0;	/* no active entries yet */
    351 
    352 	if (lflg) {
    353 		(void) sprintf(hdr,
    354 		    " F   UID%*s%*s %%C PRI NI   SZ  RSS    "
    355 		    "WCHAN S TT        TIME COMMAND", pidwidth + 1, "PID",
    356 		    pidwidth + 1, "PPID");
    357 	} else if (uflg) {
    358 		if (nflg)
    359 			(void) sprintf(hdr,
    360 			    "   UID%*s %%CPU %%MEM   SZ  RSS "
    361 			    "TT       S    START  TIME COMMAND",
    362 			    pidwidth + 1, "PID");
    363 		else
    364 			(void) sprintf(hdr,
    365 			    "USER    %*s %%CPU %%MEM   SZ  RSS "
    366 			    "TT       S    START  TIME COMMAND",
    367 			    pidwidth + 1, "PID");
    368 	} else if (vflg) {
    369 		(void) sprintf(hdr,
    370 		    "%*s TT       S  TIME SIZE  RSS %%CPU %%MEM "
    371 		    "COMMAND", pidwidth + 1, "PID");
    372 	} else
    373 		(void) sprintf(hdr, "%*s TT       S  TIME COMMAND",
    374 		    pidwidth + 1, "PID");
    375 
    376 	twidth = twidth - strlen(hdr) + 6;
    377 	(void) printf("%s\n", hdr);
    378 
    379 	if (twidth > PRARGSZ && (psargs = malloc(twidth)) == NULL) {
    380 		(void) fprintf(stderr, "ps: no memory\n");
    381 		exit(1);
    382 	}
    383 	svpsargs = psargs;
    384 
    385 	/*
    386 	 * Determine which processes to print info about by searching
    387 	 * the /proc directory and looking at each process.
    388 	 */
    389 	if ((dirp = opendir(procdir)) == NULL) {
    390 		(void) fprintf(stderr, "ps: cannot open PROC directory %s\n",
    391 		    procdir);
    392 		exit(1);
    393 	}
    394 
    395 	(void) strcpy(psname, procdir);
    396 	pdlen = strlen(psname);
    397 	psname[pdlen++] = '/';
    398 
    399 	/* for each active process --- */
    400 	while (dentp = readdir(dirp)) {
    401 		int	psfd;	/* file descriptor for /proc/nnnnn/psinfo */
    402 		int	asfd;	/* file descriptor for /proc/nnnnn/as */
    403 
    404 		if (dentp->d_name[0] == '.')		/* skip . and .. */
    405 			continue;
    406 		(void) strcpy(psname + pdlen, dentp->d_name);
    407 		(void) strcpy(asname, psname);
    408 		(void) strcat(psname, "/psinfo");
    409 		(void) strcat(asname, "/as");
    410 retry:
    411 		if ((psfd = open(psname, O_RDONLY)) == -1)
    412 			continue;
    413 		asfd = -1;
    414 		if (psargs != NULL || eflg) {
    415 
    416 			/* now we need the proc_owner privilege */
    417 			(void) __priv_bracket(PRIV_ON);
    418 
    419 			asfd = open(asname, O_RDONLY);
    420 
    421 			/* drop proc_owner privilege after open */
    422 			(void) __priv_bracket(PRIV_OFF);
    423 		}
    424 
    425 		/*
    426 		 * Get the info structure for the process
    427 		 */
    428 		if (read(psfd, &info, sizeof (info)) != sizeof (info)) {
    429 			int	saverr = errno;
    430 
    431 			(void) close(psfd);
    432 			if (asfd > 0)
    433 				(void) close(asfd);
    434 			if (saverr == EAGAIN)
    435 				goto retry;
    436 			if (saverr != ENOENT)
    437 				(void) fprintf(stderr, "ps: read() on %s: %s\n",
    438 				    psname, err_string(saverr));
    439 			continue;
    440 		}
    441 		(void) close(psfd);
    442 
    443 		found = 0;
    444 		if (info.pr_lwp.pr_state == 0)		/* can't happen? */
    445 			goto closeit;
    446 		pid = info.pr_pid;
    447 		ppid = info.pr_ppid;
    448 
    449 		/* Display only process from command line */
    450 		if (pflg) {	/* pid in arg list */
    451 			if (pidsave == pid)
    452 				found++;
    453 			else
    454 				goto closeit;
    455 		}
    456 
    457 		/*
    458 		 * Omit "uninteresting" processes unless 'g' option.
    459 		 */
    460 		if ((ppid == 1) && !(gflg))
    461 			goto closeit;
    462 
    463 		/*
    464 		 * Omit non-running processes for 'r' option
    465 		 */
    466 		if (rflg &&
    467 		    !(info.pr_lwp.pr_sname == 'O' ||
    468 		    info.pr_lwp.pr_sname == 'R'))
    469 			goto closeit;
    470 
    471 		if (!found && !tflg && !aflg && info.pr_euid != my_uid)
    472 			goto closeit;
    473 
    474 		/*
    475 		 * Read the args for the -w and -ww cases
    476 		 */
    477 		if (asfd > 0) {
    478 			if ((psargs != NULL &&
    479 			    preadargs(asfd, &info, psargs) == -1) ||
    480 			    (eflg && preadenvs(asfd, &info, psargs) == -1)) {
    481 				int	saverr = errno;
    482 
    483 				(void) close(asfd);
    484 				if (saverr == EAGAIN)
    485 					goto retry;
    486 				if (saverr != ENOENT)
    487 					(void) fprintf(stderr,
    488 					    "ps: read() on %s: %s\n",
    489 					    asname, err_string(saverr));
    490 				continue;
    491 			}
    492 		} else {
    493 			psargs = info.pr_psargs;
    494 		}
    495 
    496 		if (nent >= entsize) {
    497 			entsize *= 2;
    498 			psent = (struct psent *)realloc((char *)psent,
    499 				entsize * sizeof (struct psent));
    500 			if (psent == NULL) {
    501 				(void) fprintf(stderr, "ps: no memory\n");
    502 				exit(1);
    503 			}
    504 		}
    505 		if ((psent[nent].psinfo = malloc(sizeof (psinfo_t)))
    506 		    == NULL) {
    507 			(void) fprintf(stderr, "ps: no memory\n");
    508 			exit(1);
    509 		}
    510 		*psent[nent].psinfo = info;
    511 		if (psargs == NULL)
    512 			psent[nent].psargs = NULL;
    513 		else {
    514 			if ((psent[nent].psargs = malloc(strlen(psargs)+1))
    515 			    == NULL) {
    516 				(void) fprintf(stderr, "ps: no memory\n");
    517 				exit(1);
    518 			}
    519 			(void) strcpy(psent[nent].psargs, psargs);
    520 		}
    521 		psent[nent].found = found;
    522 		nent++;
    523 closeit:
    524 		if (asfd > 0)
    525 			(void) close(asfd);
    526 		psargs = svpsargs;
    527 	}
    528 
    529 	/* revert to non-privileged user */
    530 	(void) __priv_relinquish();
    531 
    532 	(void) closedir(dirp);
    533 
    534 	qsort((char *)psent, nent, sizeof (psent[0]), pscompare);
    535 
    536 	for (i = 0; i < nent; i++) {
    537 		struct psent *pp = &psent[i];
    538 		if (prcom(pp->found, pp->psinfo, pp->psargs)) {
    539 			(void) printf("\n");
    540 			retcode = 0;
    541 		}
    542 	}
    543 
    544 	return (retcode);
    545 }
    546 
    547 static void
    548 usage()		/* print usage message and quit */
    549 {
    550 	static char usage1[] = "ps [ -aceglnrSuUvwx ] [ -t term ] [ num ]";
    551 
    552 	(void) fprintf(stderr, "usage: %s\n", usage1);
    553 	exit(1);
    554 }
    555 
    556 /*
    557  * Read the process arguments from the process.
    558  * This allows >PRARGSZ characters of arguments to be displayed but,
    559  * unlike pr_psargs[], the process may have changed them.
    560  */
    561 #define	NARG	100
    562 static int
    563 preadargs(int pfd, psinfo_t *psinfo, char *psargs)
    564 {
    565 	off_t argvoff = (off_t)psinfo->pr_argv;
    566 	size_t len;
    567 	char *psa = psargs;
    568 	int bsize = twidth;
    569 	int narg = NARG;
    570 	off_t argv[NARG];
    571 	off_t argoff;
    572 	off_t nextargoff;
    573 	int i;
    574 #ifdef _LP64
    575 	caddr32_t argv32[NARG];
    576 	int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64);
    577 #endif
    578 
    579 	if (psinfo->pr_nlwp == 0 ||
    580 	    strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0)
    581 		goto out;
    582 
    583 	(void) memset(psa, 0, bsize--);
    584 	nextargoff = 0;
    585 	errno = EIO;
    586 	while (bsize > 0) {
    587 		if (narg == NARG) {
    588 			(void) memset(argv, 0, sizeof (argv));
    589 #ifdef _LP64
    590 			if (is32) {
    591 				if ((i = pread(pfd, argv32, sizeof (argv32),
    592 				    argvoff)) <= 0) {
    593 					if (i == 0 || errno == EIO)
    594 						break;
    595 					return (-1);
    596 				}
    597 				for (i = 0; i < NARG; i++)
    598 					argv[i] = argv32[i];
    599 			} else
    600 #endif
    601 				if ((i = pread(pfd, argv, sizeof (argv),
    602 				    argvoff)) <= 0) {
    603 					if (i == 0 || errno == EIO)
    604 						break;
    605 					return (-1);
    606 				}
    607 			narg = 0;
    608 		}
    609 		if ((argoff = argv[narg++]) == 0)
    610 			break;
    611 		if (argoff != nextargoff &&
    612 		    (i = pread(pfd, psa, bsize, argoff)) <= 0) {
    613 			if (i == 0 || errno == EIO)
    614 				break;
    615 			return (-1);
    616 		}
    617 		len = strlen(psa);
    618 		psa += len;
    619 		*psa++ = ' ';
    620 		bsize -= len + 1;
    621 		nextargoff = argoff + len + 1;
    622 #ifdef _LP64
    623 		argvoff += is32? sizeof (caddr32_t) : sizeof (caddr_t);
    624 #else
    625 		argvoff += sizeof (caddr_t);
    626 #endif
    627 	}
    628 	while (psa > psargs && isspace(*(psa-1)))
    629 		psa--;
    630 
    631 out:
    632 	*psa = '\0';
    633 	if (strlen(psinfo->pr_psargs) > strlen(psargs))
    634 		(void) strcpy(psargs, psinfo->pr_psargs);
    635 
    636 	return (0);
    637 }
    638 
    639 /*
    640  * Read environment variables from the process.
    641  * Append them to psargs if there is room.
    642  */
    643 static int
    644 preadenvs(int pfd, psinfo_t *psinfo, char *psargs)
    645 {
    646 	off_t envpoff = (off_t)psinfo->pr_envp;
    647 	int len;
    648 	char *psa;
    649 	char *psainit;
    650 	int bsize;
    651 	int nenv = NARG;
    652 	off_t envp[NARG];
    653 	off_t envoff;
    654 	off_t nextenvoff;
    655 	int i;
    656 #ifdef _LP64
    657 	caddr32_t envp32[NARG];
    658 	int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64);
    659 #endif
    660 
    661 	psainit = psa = (psargs != NULL)? psargs : psinfo->pr_psargs;
    662 	len = strlen(psa);
    663 	psa += len;
    664 	bsize = twidth - len - 1;
    665 
    666 	if (bsize <= 0 || psinfo->pr_nlwp == 0 ||
    667 	    strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0)
    668 		return (0);
    669 
    670 	nextenvoff = 0;
    671 	errno = EIO;
    672 	while (bsize > 0) {
    673 		if (nenv == NARG) {
    674 			(void) memset(envp, 0, sizeof (envp));
    675 #ifdef _LP64
    676 			if (is32) {
    677 				if ((i = pread(pfd, envp32, sizeof (envp32),
    678 				    envpoff)) <= 0) {
    679 					if (i == 0 || errno == EIO)
    680 						break;
    681 					return (-1);
    682 				}
    683 				for (i = 0; i < NARG; i++)
    684 					envp[i] = envp32[i];
    685 			} else
    686 #endif
    687 				if ((i = pread(pfd, envp, sizeof (envp),
    688 				    envpoff)) <= 0) {
    689 					if (i == 0 || errno == EIO)
    690 						break;
    691 					return (-1);
    692 				}
    693 			nenv = 0;
    694 		}
    695 		if ((envoff = envp[nenv++]) == 0)
    696 			break;
    697 		if (envoff != nextenvoff &&
    698 		    (i = pread(pfd, psa+1, bsize, envoff)) <= 0) {
    699 			if (i == 0 || errno == EIO)
    700 				break;
    701 			return (-1);
    702 		}
    703 		*psa++ = ' ';
    704 		len = strlen(psa);
    705 		psa += len;
    706 		bsize -= len + 1;
    707 		nextenvoff = envoff + len + 1;
    708 #ifdef _LP64
    709 		envpoff += is32? sizeof (caddr32_t) : sizeof (caddr_t);
    710 #else
    711 		envpoff += sizeof (caddr_t);
    712 #endif
    713 	}
    714 	while (psa > psainit && isspace(*(psa-1)))
    715 		psa--;
    716 	*psa = '\0';
    717 
    718 	return (0);
    719 }
    720 
    721 /*
    722  * getarg() finds the next argument in list and copies arg into argbuf.
    723  * p1 first pts to arg passed back from getopt routine.  p1 is then
    724  * bumped to next character that is not a comma or blank -- p1 NULL
    725  * indicates end of list.
    726  */
    727 
    728 static void
    729 getarg()
    730 {
    731 	char	*parga;
    732 	int c;
    733 
    734 	while ((c = *p1) != '\0' && (c == ',' || isspace(c)))
    735 		p1++;
    736 
    737 	parga = argbuf;
    738 	while ((c = *p1) != '\0' && c != ',' && !isspace(c)) {
    739 		if (parga < argbuf + ARGSIZ - 1)
    740 			*parga++ = c;
    741 		p1++;
    742 	}
    743 	*parga = '\0';
    744 
    745 	while ((c = *p1) != '\0' && (c == ',' || isspace(c)))
    746 		p1++;
    747 }
    748 
    749 static char *
    750 devlookup(dev_t ddev)
    751 {
    752 	struct devl *dp;
    753 	int i;
    754 
    755 	for (dp = devl, i = 0; i < ndev; dp++, i++) {
    756 		if (dp->ddev == ddev)
    757 			return (dp->dname);
    758 	}
    759 	return (NULL);
    760 }
    761 
    762 static char *
    763 devadd(char *name, dev_t ddev)
    764 {
    765 	struct devl *dp;
    766 	int leng, start, i;
    767 
    768 	if (ndev == maxdev) {
    769 		maxdev += DNINCR;
    770 		devl = realloc(devl, maxdev * sizeof (struct devl));
    771 		if (devl == NULL) {
    772 			(void) fprintf(stderr,
    773 			    "ps: not enough memory for %d devices\n", maxdev);
    774 			exit(1);
    775 		}
    776 	}
    777 	dp = &devl[ndev++];
    778 
    779 	dp->ddev = ddev;
    780 	if (name == NULL) {
    781 		(void) strcpy(dp->dname, "??");
    782 		return (dp->dname);
    783 	}
    784 
    785 	leng = strlen(name);
    786 	/* Strip off /dev/ */
    787 	if (leng < DNSIZE + 4)
    788 		(void) strcpy(dp->dname, &name[5]);
    789 	else {
    790 		start = leng - (DNSIZE - 1);
    791 
    792 		for (i = start; i < leng && name[i] != '/'; i++)
    793 				;
    794 		if (i == leng)
    795 			(void) strlcpy(dp->dname, &name[start], DNSIZE);
    796 		else
    797 			(void) strlcpy(dp->dname, &name[i+1], DNSIZE);
    798 	}
    799 	return (dp->dname);
    800 }
    801 
    802 /*
    803  * gettty returns the user's tty number or ? if none.
    804  */
    805 static char *
    806 gettty(psinfo_t *psinfo)
    807 {
    808 	extern char *_ttyname_dev(dev_t, char *, size_t);
    809 	char devname[TTYNAME_MAX];
    810 	char *retval;
    811 
    812 	if (psinfo->pr_ttydev == PRNODEV)
    813 		return ("?");
    814 
    815 	if ((retval = devlookup(psinfo->pr_ttydev)) != NULL)
    816 		return (retval);
    817 
    818 	retval = _ttyname_dev(psinfo->pr_ttydev, devname, sizeof (devname));
    819 
    820 	return (devadd(retval, psinfo->pr_ttydev));
    821 }
    822 
    823 /*
    824  * Print percent from 16-bit binary fraction [0 .. 1]
    825  * Round up .01 to .1 to indicate some small percentage (the 0x7000 below).
    826  */
    827 static void
    828 prtpct(ushort_t pct)
    829 {
    830 	uint_t value = pct;	/* need 32 bits to compute with */
    831 
    832 	value = ((value * 1000) + 0x7000) >> 15;	/* [0 .. 1000] */
    833 	(void) printf("%3u.%u", value / 10, value % 10);
    834 }
    835 
    836 /*
    837  * Print info about the process.
    838  */
    839 static int
    840 prcom(int found, psinfo_t *psinfo, char *psargs)
    841 {
    842 	char	*cp;
    843 	char	*tp;
    844 	char	*psa;
    845 	long	tm;
    846 	int	i, wcnt, length;
    847 	wchar_t	wchar;
    848 	struct tty *ttyp;
    849 
    850 	/*
    851 	 * If process is zombie, call print routine and return.
    852 	 */
    853 	if (psinfo->pr_nlwp == 0) {
    854 		if (tflg && !found)
    855 			return (0);
    856 		else {
    857 			przom(psinfo);
    858 			return (1);
    859 		}
    860 	}
    861 
    862 	/*
    863 	 * Get current terminal.  If none ("?") and 'a' is set, don't print
    864 	 * info.  If 't' is set, check if term is in list of desired terminals
    865 	 * and print it if it is.
    866 	 */
    867 	i = 0;
    868 	tp = gettty(psinfo);
    869 
    870 	if (*tp == '?' && !found && !xflg)
    871 		return (0);
    872 
    873 	if (!(*tp == '?' && aflg) && tflg && !found) {
    874 		int match = 0;
    875 		char *other = NULL;
    876 		for (ttyp = tty; ttyp->tname != NULL; ttyp++) {
    877 			/*
    878 			 * Look for a name match
    879 			 */
    880 			if (strcmp(tp, ttyp->tname) == 0) {
    881 				match = 1;
    882 				break;
    883 			}
    884 			/*
    885 			 * Look for same device under different names.
    886 			 */
    887 			if ((other == NULL) &&
    888 			    (psinfo->pr_ttydev == ttyp->tdev))
    889 				other = ttyp->tname;
    890 		}
    891 		if (!match) {
    892 			if (other == NULL)
    893 				return (0);
    894 			tp = other;
    895 		}
    896 	}
    897 
    898 	if (lflg)
    899 		(void) printf("%2x", psinfo->pr_flag & 0377);
    900 	if (uflg) {
    901 		if (!nflg) {
    902 			struct passwd *pwd;
    903 
    904 			if ((pwd = getpwuid(psinfo->pr_euid)) != NULL)
    905 								/* USER */
    906 				(void) printf("%-8.8s", pwd->pw_name);
    907 			else
    908 								/* UID */
    909 				(void) printf(" %7.7d", (int)psinfo->pr_euid);
    910 		} else {
    911 			(void) printf(" %5d", (int)psinfo->pr_euid); /* UID */
    912 		}
    913 	} else if (lflg)
    914 		(void) printf(" %5d", (int)psinfo->pr_euid);	/* UID */
    915 
    916 	(void) printf("%*d", pidwidth + 1, (int)psinfo->pr_pid); /* PID */
    917 	if (lflg)
    918 		(void) printf("%*d", pidwidth + 1,
    919 		    (int)psinfo->pr_ppid); /* PPID */
    920 	if (lflg)
    921 		(void) printf("%3d", psinfo->pr_lwp.pr_cpu & 0377); /* CP */
    922 	if (uflg) {
    923 		prtpct(psinfo->pr_pctcpu);			/* %CPU */
    924 		prtpct(psinfo->pr_pctmem);			/* %MEM */
    925 	}
    926 	if (lflg) {
    927 		(void) printf("%4d", psinfo->pr_lwp.pr_pri);	/* PRI */
    928 		(void) printf("%3d", psinfo->pr_lwp.pr_nice);	/* NICE */
    929 	}
    930 	if (lflg || uflg) {
    931 		if (psinfo->pr_flag & SSYS)			/* SZ */
    932 			(void) printf("    0");
    933 		else if (psinfo->pr_size)
    934 			(void) printf("%5lu", (ulong_t)psinfo->pr_size);
    935 		else
    936 			(void) printf("    ?");
    937 		if (psinfo->pr_flag & SSYS)			/* RSS */
    938 			(void) printf("    0");
    939 		else if (psinfo->pr_rssize)
    940 			(void) printf("%5lu", (ulong_t)psinfo->pr_rssize);
    941 		else
    942 			(void) printf("    ?");
    943 	}
    944 	if (lflg) {						/* WCHAN */
    945 		if (psinfo->pr_lwp.pr_sname != 'S') {
    946 			(void) printf("         ");
    947 		} else if (psinfo->pr_lwp.pr_wchan) {
    948 			(void) printf(" %+8.8lx",
    949 				(ulong_t)psinfo->pr_lwp.pr_wchan);
    950 		} else {
    951 			(void) printf("        ?");
    952 		}
    953 	}
    954 	if ((tplen = strlen(tp)) > 9)
    955 		maxlen = twidth - tplen + 9;
    956 	else
    957 		maxlen = twidth;
    958 
    959 	if (!lflg)
    960 		(void) printf(" %-8.14s", tp);			/* TTY */
    961 	(void) printf(" %c", psinfo->pr_lwp.pr_sname);		/* STATE */
    962 	if (lflg)
    963 		(void) printf(" %-8.14s", tp);			/* TTY */
    964 	if (uflg)
    965 		prtime(psinfo->pr_start);			/* START */
    966 
    967 	/* time just for process */
    968 	tm = psinfo->pr_time.tv_sec;
    969 	if (Sflg) {	/* calculate time for process and all reaped children */
    970 		tm += psinfo->pr_ctime.tv_sec;
    971 		if (psinfo->pr_time.tv_nsec + psinfo->pr_ctime.tv_nsec
    972 		    >= 1000000000)
    973 			tm += 1;
    974 	}
    975 
    976 	(void) printf(" %2ld:%.2ld", tm / 60, tm % 60);		/* TIME */
    977 
    978 	if (vflg) {
    979 		if (psinfo->pr_flag & SSYS)			/* SZ */
    980 			(void) printf("    0");
    981 		else if (psinfo->pr_size)
    982 			(void) printf("%5lu", (ulong_t)psinfo->pr_size);
    983 		else
    984 			(void) printf("    ?");
    985 		if (psinfo->pr_flag & SSYS)			/* SZ */
    986 			(void) printf("    0");
    987 		else if (psinfo->pr_rssize)
    988 			(void) printf("%5lu", (ulong_t)psinfo->pr_rssize);
    989 		else
    990 			(void) printf("    ?");
    991 		prtpct(psinfo->pr_pctcpu);			/* %CPU */
    992 		prtpct(psinfo->pr_pctmem);			/* %MEM */
    993 	}
    994 	if (cflg) {						/* CMD */
    995 		wcnt = namencnt(psinfo->pr_fname, 16, maxlen);
    996 		(void) printf(" %.*s", wcnt, psinfo->pr_fname);
    997 		return (1);
    998 	}
    999 	/*
   1000 	 * PRARGSZ == length of cmd arg string.
   1001 	 */
   1002 	if (psargs == NULL) {
   1003 		psa = &psinfo->pr_psargs[0];
   1004 		i = PRARGSZ;
   1005 		tp = &psinfo->pr_psargs[PRARGSZ];
   1006 	} else {
   1007 		psa = psargs;
   1008 		i = strlen(psargs);
   1009 		tp = psa + i;
   1010 	}
   1011 
   1012 	for (cp = psa; cp < tp; /* empty */) {
   1013 		if (*cp == 0)
   1014 			break;
   1015 		length = mbtowc(&wchar, cp, MB_LEN_MAX);
   1016 		if (length < 0 || !iswprint(wchar)) {
   1017 			(void) printf(" [ %.16s ]", psinfo->pr_fname);
   1018 			return (1);
   1019 		}
   1020 		cp += length;
   1021 	}
   1022 	wcnt = namencnt(psa, i, maxlen);
   1023 #if 0
   1024 	/* dumps core on really long strings */
   1025 	(void) printf(" %.*s", wcnt, psa);
   1026 #else
   1027 	(void) putchar(' ');
   1028 	(void) fwrite(psa, 1, wcnt, stdout);
   1029 #endif
   1030 	return (1);
   1031 }
   1032 
   1033 /*
   1034  * Print starting time of process unless process started more than 24 hours
   1035  * ago, in which case the date is printed.
   1036  */
   1037 static void
   1038 prtime(timestruc_t st)
   1039 {
   1040 	char sttim[26];
   1041 	static time_t tim = 0L;
   1042 	time_t starttime;
   1043 
   1044 	if (tim == 0L)
   1045 		tim = time((time_t *)0);
   1046 	starttime = st.tv_sec;
   1047 	if (tim - starttime > 24*60*60) {
   1048 		(void) strftime(sttim, sizeof (sttim), "%b %d",
   1049 						localtime(&starttime));
   1050 	} else {
   1051 		(void) strftime(sttim, sizeof (sttim), "%H:%M:%S",
   1052 						localtime(&starttime));
   1053 	}
   1054 	(void) printf("%9.9s", sttim);
   1055 }
   1056 
   1057 static void
   1058 przom(psinfo_t *psinfo)
   1059 {
   1060 	long	tm;
   1061 
   1062 	if (lflg)
   1063 		(void) printf("%2x", psinfo->pr_flag & 0377);
   1064 	if (uflg) {
   1065 		struct passwd *pwd;
   1066 
   1067 		if ((pwd = getpwuid(psinfo->pr_euid)) != NULL)
   1068 			(void) printf("%-8.8s", pwd->pw_name);	/* USER */
   1069 		else
   1070 			(void) printf(" %7.7d", (int)psinfo->pr_euid); /* UID */
   1071 	} else if (lflg)
   1072 		(void) printf(" %5d", (int)psinfo->pr_euid);	/* UID */
   1073 
   1074 	(void) printf("%*d", pidwidth + 1, (int)psinfo->pr_pid); /* PID */
   1075 	if (lflg)
   1076 		(void) printf("%*d", pidwidth + 1,
   1077 		    (int)psinfo->pr_ppid); /* PPID */
   1078 	if (lflg)
   1079 		(void) printf("  0");				/* CP */
   1080 	if (uflg) {
   1081 		prtpct(0);					/* %CPU */
   1082 		prtpct(0);					/* %MEM */
   1083 	}
   1084 	if (lflg) {
   1085 		(void) printf("%4d", psinfo->pr_lwp.pr_pri);	/* PRI */