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