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