xref: /illumos-gate/usr/src/cmd/ps/ucbps.c (revision d583b39b)
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 #include <zone.h>
69 
70 #define	NTTYS	2	/* max ttys that can be specified with the -t option */
71 			/* only one tty can be specified with SunOS ps */
72 #define	SIZ	30	/* max processes that can be specified with -p and -g */
73 #define	ARGSIZ	30	/* size of buffer holding args for -t, -p, -u options */
74 
75 #define	FSTYPE_MAX	8
76 
77 struct psent {
78 	psinfo_t *psinfo;
79 	char *psargs;
80 	int found;
81 };
82 
83 static	int	tplen, maxlen, twidth;
84 static	char	hdr[81];
85 static	struct	winsize win;
86 
87 static	int	retcode = 1;
88 static	int	lflg;	/* long format */
89 static	int	uflg;	/* user-oriented output */
90 static	int	aflg;	/* Display all processes */
91 static	int	eflg;	/* Display environment as well as arguments */
92 static	int	gflg;	/* Display process group leaders */
93 static	int	tflg;	/* Processes running on specific terminals */
94 static	int	rflg;	/* Running processes only flag */
95 static	int	Sflg;	/* Accumulated time plus all reaped children */
96 static	int	xflg;	/* Include processes with no controlling tty */
97 static	int	cflg;	/* Display command name */
98 static	int	vflg;	/* Virtual memory-oriented output */
99 static	int	nflg;	/* Numerical output */
100 static	int	pflg;	/* Specific process id passed as argument */
101 static	int	Uflg;	/* Update private database, ups_data */
102 static	int	errflg;
103 
104 static	char	*gettty();
105 static	char	argbuf[ARGSIZ];
106 static	char	*parg;
107 static	char	*p1;		/* points to successive option arguments */
108 static	uid_t	my_uid;
109 static char	stdbuf[BUFSIZ];
110 
111 static	int	ndev;		/* number of devices */
112 static	int	maxdev;		/* number of devl structures allocated */
113 
114 #define	DNINCR	100
115 #define	DNSIZE	14
116 static	struct devl {		/* device list	 */
117 	char	dname[DNSIZE];	/* device name	 */
118 	dev_t	ddev;		/* device number */
119 } *devl;
120 
121 static	struct tty {
122 	char *tname;
123 	dev_t tdev;
124 } tty[NTTYS];			/* for t option */
125 static	int	ntty = 0;
126 static	pid_t	pidsave;
127 static	int	pidwidth;
128 
129 static	char	*procdir = "/proc";	/* standard /proc directory */
130 static	void	usage();		/* print usage message and quit */
131 static	void	getarg(void);
132 static	void	prtime(timestruc_t st);
133 static	void	przom(psinfo_t *psinfo);
134 static	int	num(char *);
135 static	int	preadargs(int, psinfo_t *, char *);
136 static	int	preadenvs(int, psinfo_t *, char *);
137 static	int	prcom(int, psinfo_t *, char *);
138 static	int	namencnt(char *, int, int);
139 static	int	pscompare(const void *, const void *);
140 static	char	*err_string(int);
141 
142 extern int	scrwidth(wchar_t);	/* header file? */
143 
144 int
145 ucbmain(int argc, char **argv)
146 {
147 	psinfo_t info;		/* process information structure from /proc */
148 	char *psargs = NULL;	/* pointer to buffer for -w and -ww options */
149 	char *svpsargs = NULL;
150 	struct psent *psent;
151 	int entsize;
152 	int nent;
153 	pid_t maxpid;
154 
155 	struct tty *ttyp = tty;
156 	char	*tmp;
157 	char	*p;
158 	int	c;
159 	pid_t	pid;		/* pid: process id */
160 	pid_t	ppid;		/* ppid: parent process id */
161 	int	i, found;
162 
163 	size_t	size;
164 
165 	DIR *dirp;
166 	struct dirent *dentp;
167 	char	psname[100];
168 	char	asname[100];
169 	int	pdlen;
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 	(void) strcpy(psname, procdir);
394 	pdlen = strlen(psname);
395 	psname[pdlen++] = '/';
396 
397 	/* for each active process --- */
398 	while (dentp = readdir(dirp)) {
399 		int	psfd;	/* file descriptor for /proc/nnnnn/psinfo */
400 		int	asfd;	/* file descriptor for /proc/nnnnn/as */
401 
402 		if (dentp->d_name[0] == '.')		/* skip . and .. */
403 			continue;
404 		(void) strcpy(psname + pdlen, dentp->d_name);
405 		(void) strcpy(asname, psname);
406 		(void) strcat(psname, "/psinfo");
407 		(void) strcat(asname, "/as");
408 retry:
409 		if ((psfd = open(psname, O_RDONLY)) == -1)
410 			continue;
411 		asfd = -1;
412 		if (psargs != NULL || eflg) {
413 
414 			/* now we need the proc_owner privilege */
415 			(void) __priv_bracket(PRIV_ON);
416 
417 			asfd = open(asname, O_RDONLY);
418 
419 			/* drop proc_owner privilege after open */
420 			(void) __priv_bracket(PRIV_OFF);
421 		}
422 
423 		/*
424 		 * Get the info structure for the process
425 		 */
426 		if (read(psfd, &info, sizeof (info)) != sizeof (info)) {
427 			int	saverr = errno;
428 
429 			(void) close(psfd);
430 			if (asfd > 0)
431 				(void) close(asfd);
432 			if (saverr == EAGAIN)
433 				goto retry;
434 			if (saverr != ENOENT)
435 				(void) fprintf(stderr, "ps: read() on %s: %s\n",
436 				    psname, err_string(saverr));
437 			continue;
438 		}
439 		(void) close(psfd);
440 
441 		found = 0;
442 		if (info.pr_lwp.pr_state == 0)		/* can't happen? */
443 			goto closeit;
444 		pid = info.pr_pid;
445 		ppid = info.pr_ppid;
446 
447 		/* Display only process from command line */
448 		if (pflg) {	/* pid in arg list */
449 			if (pidsave == pid)
450 				found++;
451 			else
452 				goto closeit;
453 		}
454 
455 		/*
456 		 * Omit "uninteresting" processes unless 'g' option.
457 		 */
458 		if ((ppid == 1) && !(gflg))
459 			goto closeit;
460 
461 		/*
462 		 * Omit non-running processes for 'r' option
463 		 */
464 		if (rflg &&
465 		    !(info.pr_lwp.pr_sname == 'O' ||
466 		    info.pr_lwp.pr_sname == 'R'))
467 			goto closeit;
468 
469 		if (!found && !tflg && !aflg && info.pr_euid != my_uid)
470 			goto closeit;
471 
472 		/*
473 		 * Read the args for the -w and -ww cases
474 		 */
475 		if (asfd > 0) {
476 			if ((psargs != NULL &&
477 			    preadargs(asfd, &info, psargs) == -1) ||
478 			    (eflg && preadenvs(asfd, &info, psargs) == -1)) {
479 				int	saverr = errno;
480 
481 				(void) close(asfd);
482 				if (saverr == EAGAIN)
483 					goto retry;
484 				if (saverr != ENOENT)
485 					(void) fprintf(stderr,
486 					    "ps: read() on %s: %s\n",
487 					    asname, err_string(saverr));
488 				continue;
489 			}
490 		} else {
491 			psargs = info.pr_psargs;
492 		}
493 
494 		if (nent >= entsize) {
495 			entsize *= 2;
496 			psent = (struct psent *)realloc((char *)psent,
497 			    entsize * sizeof (struct psent));
498 			if (psent == NULL) {
499 				(void) fprintf(stderr, "ps: no memory\n");
500 				exit(1);
501 			}
502 		}
503 		if ((psent[nent].psinfo = malloc(sizeof (psinfo_t)))
504 		    == NULL) {
505 			(void) fprintf(stderr, "ps: no memory\n");
506 			exit(1);
507 		}
508 		*psent[nent].psinfo = info;
509 		if (psargs == NULL)
510 			psent[nent].psargs = NULL;
511 		else {
512 			if ((psent[nent].psargs = malloc(strlen(psargs)+1))
513 			    == NULL) {
514 				(void) fprintf(stderr, "ps: no memory\n");
515 				exit(1);
516 			}
517 			(void) strcpy(psent[nent].psargs, psargs);
518 		}
519 		psent[nent].found = found;
520 		nent++;
521 closeit:
522 		if (asfd > 0)
523 			(void) close(asfd);
524 		psargs = svpsargs;
525 	}
526 
527 	/* revert to non-privileged user */
528 	(void) __priv_relinquish();
529 
530 	(void) closedir(dirp);
531 
532 	qsort((char *)psent, nent, sizeof (psent[0]), pscompare);
533 
534 	for (i = 0; i < nent; i++) {
535 		struct psent *pp = &psent[i];
536 		if (prcom(pp->found, pp->psinfo, pp->psargs)) {
537 			(void) printf("\n");
538 			retcode = 0;
539 		}
540 	}
541 
542 	return (retcode);
543 }
544 
545 static void
546 usage()		/* print usage message and quit */
547 {
548 	static char usage1[] = "ps [ -aceglnrSuUvwx ] [ -t term ] [ num ]";
549 
550 	(void) fprintf(stderr, "usage: %s\n", usage1);
551 	exit(1);
552 }
553 
554 /*
555  * Read the process arguments from the process.
556  * This allows >PRARGSZ characters of arguments to be displayed but,
557  * unlike pr_psargs[], the process may have changed them.
558  */
559 #define	NARG	100
560 static int
561 preadargs(int pfd, psinfo_t *psinfo, char *psargs)
562 {
563 	off_t argvoff = (off_t)psinfo->pr_argv;
564 	size_t len;
565 	char *psa = psargs;
566 	int bsize = twidth;
567 	int narg = NARG;
568 	off_t argv[NARG];
569 	off_t argoff;
570 	off_t nextargoff;
571 	int i;
572 #ifdef _LP64
573 	caddr32_t argv32[NARG];
574 	int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64);
575 #endif
576 
577 	if (psinfo->pr_nlwp == 0 ||
578 	    strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0)
579 		goto out;
580 
581 	(void) memset(psa, 0, bsize--);
582 	nextargoff = 0;
583 	errno = EIO;
584 	while (bsize > 0) {
585 		if (narg == NARG) {
586 			(void) memset(argv, 0, sizeof (argv));
587 #ifdef _LP64
588 			if (is32) {
589 				if ((i = pread(pfd, argv32, sizeof (argv32),
590 				    argvoff)) <= 0) {
591 					if (i == 0 || errno == EIO)
592 						break;
593 					return (-1);
594 				}
595 				for (i = 0; i < NARG; i++)
596 					argv[i] = argv32[i];
597 			} else
598 #endif
599 				if ((i = pread(pfd, argv, sizeof (argv),
600 				    argvoff)) <= 0) {
601 					if (i == 0 || errno == EIO)
602 						break;
603 					return (-1);
604 				}
605 			narg = 0;
606 		}
607 		if ((argoff = argv[narg++]) == 0)
608 			break;
609 		if (argoff != nextargoff &&
610 		    (i = pread(pfd, psa, bsize, argoff)) <= 0) {
611 			if (i == 0 || errno == EIO)
612 				break;
613 			return (-1);
614 		}
615 		len = strlen(psa);
616 		psa += len;
617 		*psa++ = ' ';
618 		bsize -= len + 1;
619 		nextargoff = argoff + len + 1;
620 #ifdef _LP64
621 		argvoff += is32? sizeof (caddr32_t) : sizeof (caddr_t);
622 #else
623 		argvoff += sizeof (caddr_t);
624 #endif
625 	}
626 	while (psa > psargs && isspace(*(psa-1)))
627 		psa--;
628 
629 out:
630 	*psa = '\0';
631 	if (strlen(psinfo->pr_psargs) > strlen(psargs))
632 		(void) strcpy(psargs, psinfo->pr_psargs);
633 
634 	return (0);
635 }
636 
637 /*
638  * Read environment variables from the process.
639  * Append them to psargs if there is room.
640  */
641 static int
642 preadenvs(int pfd, psinfo_t *psinfo, char *psargs)
643 {
644 	off_t envpoff = (off_t)psinfo->pr_envp;
645 	int len;
646 	char *psa;
647 	char *psainit;
648 	int bsize;
649 	int nenv = NARG;
650 	off_t envp[NARG];
651 	off_t envoff;
652 	off_t nextenvoff;
653 	int i;
654 #ifdef _LP64
655 	caddr32_t envp32[NARG];
656 	int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64);
657 #endif
658 
659 	psainit = psa = (psargs != NULL)? psargs : psinfo->pr_psargs;
660 	len = strlen(psa);
661 	psa += len;
662 	bsize = twidth - len - 1;
663 
664 	if (bsize <= 0 || psinfo->pr_nlwp == 0 ||
665 	    strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0)
666 		return (0);
667 
668 	nextenvoff = 0;
669 	errno = EIO;
670 	while (bsize > 0) {
671 		if (nenv == NARG) {
672 			(void) memset(envp, 0, sizeof (envp));
673 #ifdef _LP64
674 			if (is32) {
675 				if ((i = pread(pfd, envp32, sizeof (envp32),
676 				    envpoff)) <= 0) {
677 					if (i == 0 || errno == EIO)
678 						break;
679 					return (-1);
680 				}
681 				for (i = 0; i < NARG; i++)
682 					envp[i] = envp32[i];
683 			} else
684 #endif
685 				if ((i = pread(pfd, envp, sizeof (envp),
686 				    envpoff)) <= 0) {
687 					if (i == 0 || errno == EIO)
688 						break;
689 					return (-1);
690 				}
691 			nenv = 0;
692 		}
693 		if ((envoff = envp[nenv++]) == 0)
694 			break;
695 		if (envoff != nextenvoff &&
696 		    (i = pread(pfd, psa+1, bsize, envoff)) <= 0) {
697 			if (i == 0 || errno == EIO)
698 				break;
699 			return (-1);
700 		}
701 		*psa++ = ' ';
702 		len = strlen(psa);
703 		psa += len;
704 		bsize -= len + 1;
705 		nextenvoff = envoff + len + 1;
706 #ifdef _LP64
707 		envpoff += is32? sizeof (caddr32_t) : sizeof (caddr_t);
708 #else
709 		envpoff += sizeof (caddr_t);
710 #endif
711 	}
712 	while (psa > psainit && isspace(*(psa-1)))
713 		psa--;
714 	*psa = '\0';
715 
716 	return (0);
717 }
718 
719 /*
720  * getarg() finds the next argument in list and copies arg into argbuf.
721  * p1 first pts to arg passed back from getopt routine.  p1 is then
722  * bumped to next character that is not a comma or blank -- p1 NULL
723  * indicates end of list.
724  */
725 
726 static void
727 getarg()
728 {
729 	char	*parga;
730 	int c;
731 
732 	while ((c = *p1) != '\0' && (c == ',' || isspace(c)))
733 		p1++;
734 
735 	parga = argbuf;
736 	while ((c = *p1) != '\0' && c != ',' && !isspace(c)) {
737 		if (parga < argbuf + ARGSIZ - 1)
738 			*parga++ = c;
739 		p1++;
740 	}
741 	*parga = '\0';
742 
743 	while ((c = *p1) != '\0' && (c == ',' || isspace(c)))
744 		p1++;
745 }
746 
747 static char *
748 devlookup(dev_t ddev)
749 {
750 	struct devl *dp;
751 	int i;
752 
753 	for (dp = devl, i = 0; i < ndev; dp++, i++) {
754 		if (dp->ddev == ddev)
755 			return (dp->dname);
756 	}
757 	return (NULL);
758 }
759 
760 static char *
761 devadd(char *name, dev_t ddev)
762 {
763 	struct devl *dp;
764 	int leng, start, i;
765 
766 	if (ndev == maxdev) {
767 		maxdev += DNINCR;
768 		devl = realloc(devl, maxdev * sizeof (struct devl));
769 		if (devl == NULL) {
770 			(void) fprintf(stderr,
771 			    "ps: not enough memory for %d devices\n", maxdev);
772 			exit(1);
773 		}
774 	}
775 	dp = &devl[ndev++];
776 
777 	dp->ddev = ddev;
778 	if (name == NULL) {
779 		(void) strcpy(dp->dname, "??");
780 		return (dp->dname);
781 	}
782 
783 	leng = strlen(name);
784 	/* Strip off /dev/ */
785 	if (leng < DNSIZE + 4)
786 		(void) strcpy(dp->dname, &name[5]);
787 	else {
788 		start = leng - (DNSIZE - 1);
789 
790 		for (i = start; i < leng && name[i] != '/'; i++)
791 				;
792 		if (i == leng)
793 			(void) strlcpy(dp->dname, &name[start], DNSIZE);
794 		else
795 			(void) strlcpy(dp->dname, &name[i+1], DNSIZE);
796 	}
797 	return (dp->dname);
798 }
799 
800 /*
801  * gettty returns the user's tty number or ? if none.
802  */
803 static char *
804 gettty(psinfo_t *psinfo)
805 {
806 	extern char *_ttyname_dev(dev_t, char *, size_t);
807 	static zoneid_t zid = -1;
808 	char devname[TTYNAME_MAX];
809 	char *retval;
810 
811 	if (zid == -1)
812 		zid = getzoneid();
813 
814 	if (psinfo->pr_ttydev == PRNODEV || psinfo->pr_zoneid != zid)
815 		return ("?");
816 
817 	if ((retval = devlookup(psinfo->pr_ttydev)) != NULL)
818 		return (retval);
819 
820 	retval = _ttyname_dev(psinfo->pr_ttydev, devname, sizeof (devname));
821 
822 	return (devadd(retval, psinfo->pr_ttydev));
823 }
824 
825 /*
826  * Print percent from 16-bit binary fraction [0 .. 1]
827  * Round up .01 to .1 to indicate some small percentage (the 0x7000 below).
828  */
829 static void
830 prtpct(ushort_t pct)
831 {
832 	uint_t value = pct;	/* need 32 bits to compute with */
833 
834 	value = ((value * 1000) + 0x7000) >> 15;	/* [0 .. 1000] */
835 	(void) printf("%3u.%u", value / 10, value % 10);
836 }
837 
838 /*
839  * Print info about the process.
840  */
841 static int
842 prcom(int found, psinfo_t *psinfo, char *psargs)
843 {
844 	char	*cp;
845 	char	*tp;
846 	char	*psa;
847 	long	tm;
848 	int	i, wcnt, length;
849 	wchar_t	wchar;
850 	struct tty *ttyp;
851 
852 	/*
853 	 * If process is zombie, call print routine and return.
854 	 */
855 	if (psinfo->pr_nlwp == 0) {
856 		if (tflg && !found)
857 			return (0);
858 		else {
859 			przom(psinfo);
860 			return (1);
861 		}
862 	}
863 
864 	/*
865 	 * Get current terminal.  If none ("?") and 'a' is set, don't print
866 	 * info.  If 't' is set, check if term is in list of desired terminals
867 	 * and print it if it is.
868 	 */
869 	i = 0;
870 	tp = gettty(psinfo);
871 
872 	if (*tp == '?' && !found && !xflg)
873 		return (0);
874 
875 	if (!(*tp == '?' && aflg) && tflg && !found) {
876 		int match = 0;
877 		char *other = NULL;
878 		for (ttyp = tty; ttyp->tname != NULL; ttyp++) {
879 			/*
880 			 * Look for a name match
881 			 */
882 			if (strcmp(tp, ttyp->tname) == 0) {
883 				match = 1;
884 				break;
885 			}
886 			/*
887 			 * Look for same device under different names.
888 			 */
889 			if ((other == NULL) &&
890 			    (psinfo->pr_ttydev == ttyp->tdev))
891 				other = ttyp->tname;
892 		}
893 		if (!match) {
894 			if (other == NULL)
895 				return (0);
896 			tp = other;
897 		}
898 	}
899 
900 	if (lflg)
901 		(void) printf("%2x", psinfo->pr_flag & 0377);
902 	if (uflg) {
903 		if (!nflg) {
904 			struct passwd *pwd;
905 
906 			if ((pwd = getpwuid(psinfo->pr_euid)) != NULL)
907 								/* USER */
908 				(void) printf("%-8.8s", pwd->pw_name);
909 			else
910 								/* UID */
911 				(void) printf(" %7.7d", (int)psinfo->pr_euid);
912 		} else {
913 			(void) printf(" %5d", (int)psinfo->pr_euid); /* UID */
914 		}
915 	} else if (lflg)
916 		(void) printf(" %5d", (int)psinfo->pr_euid);	/* UID */
917 
918 	(void) printf("%*d", pidwidth + 1, (int)psinfo->pr_pid); /* PID */
919 	if (lflg)
920 		(void) printf("%*d", pidwidth + 1,
921 		    (int)psinfo->pr_ppid); /* PPID */
922 	if (lflg)
923 		(void) printf("%3d", psinfo->pr_lwp.pr_cpu & 0377); /* CP */
924 	if (uflg) {
925 		prtpct(psinfo->pr_pctcpu);			/* %CPU */
926 		prtpct(psinfo->pr_pctmem);			/* %MEM */
927 	}
928 	if (lflg) {
929 		(void) printf("%4d", psinfo->pr_lwp.pr_pri);	/* PRI */
930 		(void) printf("%3d", psinfo->pr_lwp.pr_nice);	/* NICE */
931 	}
932 	if (lflg || uflg) {
933 		if (psinfo->pr_flag & SSYS)			/* SZ */
934 			(void) printf("    0");
935 		else if (psinfo->pr_size)
936 			(void) printf("%5lu", (ulong_t)psinfo->pr_size);
937 		else
938 			(void) printf("    ?");
939 		if (psinfo->pr_flag & SSYS)			/* RSS */
940 			(void) printf("    0");
941 		else if (psinfo->pr_rssize)
942 			(void) printf("%5lu", (ulong_t)psinfo->pr_rssize);
943 		else
944 			(void) printf("    ?");
945 	}
946 	if (lflg) {						/* WCHAN */
947 		if (psinfo->pr_lwp.pr_sname != 'S') {
948 			(void) printf("         ");
949 		} else if (psinfo->pr_lwp.pr_wchan) {
950 			(void) printf(" %+8.8lx",
951 			    (ulong_t)psinfo->pr_lwp.pr_wchan);
952 		} else {
953 			(void) printf("        ?");
954 		}
955 	}
956 	if ((tplen = strlen(tp)) > 9)
957 		maxlen = twidth - tplen + 9;
958 	else
959 		maxlen = twidth;
960 
961 	if (!lflg)
962 		(void) printf(" %-8.14s", tp);			/* TTY */
963 	(void) printf(" %c", psinfo->pr_lwp.pr_sname);		/* STATE */
964 	if (lflg)
965 		(void) printf(" %-8.14s", tp);			/* TTY */
966 	if (uflg)
967 		prtime(psinfo->pr_start);			/* START */
968 
969 	/* time just for process */
970 	tm = psinfo->pr_time.tv_sec;
971 	if (Sflg) {	/* calculate time for process and all reaped children */
972 		tm += psinfo->pr_ctime.tv_sec;
973 		if (psinfo->pr_time.tv_nsec + psinfo->pr_ctime.tv_nsec
974 		    >= 1000000000)
975 			tm += 1;
976 	}
977 
978 	(void) printf(" %2ld:%.2ld", tm / 60, tm % 60);		/* TIME */
979 
980 	if (vflg) {
981 		if (psinfo->pr_flag & SSYS)			/* SZ */
982 			(void) printf("    0");
983 		else if (psinfo->pr_size)
984 			(void) printf("%5lu", (ulong_t)psinfo->pr_size);
985 		else
986 			(void) printf("    ?");
987 		if (psinfo->pr_flag & SSYS)			/* SZ */
988 			(void) printf("    0");
989 		else if (psinfo->pr_rssize)
990 			(void) printf("%5lu", (ulong_t)psinfo->pr_rssize);
991 		else
992 			(void) printf("    ?");
993 		prtpct(psinfo->pr_pctcpu);			/* %CPU */
994 		prtpct(psinfo->pr_pctmem);			/* %MEM */
995 	}
996 	if (cflg) {						/* CMD */
997 		wcnt = namencnt(psinfo->pr_fname, 16, maxlen);
998 		(void) printf(" %.*s", wcnt, psinfo->pr_fname);
999 		return (1);
1000 	}
1001 	/*
1002 	 * PRARGSZ == length of cmd arg string.
1003 	 */
1004 	if (psargs == NULL) {
1005 		psa = &psinfo->pr_psargs[0];
1006 		i = PRARGSZ;
1007 		tp = &psinfo->pr_psargs[PRARGSZ];
1008 	} else {
1009 		psa = psargs;
1010 		i = strlen(psargs);
1011 		tp = psa + i;
1012 	}
1013 
1014 	for (cp = psa; cp < tp; /* empty */) {
1015 		if (*cp == 0)
1016 			break;
1017 		length = mbtowc(&wchar, cp, MB_LEN_MAX);
1018 		if (length < 0 || !iswprint(wchar)) {
1019 			(void) printf(" [ %.16s ]", psinfo->pr_fname);
1020 			return (1);
1021 		}
1022 		cp += length;
1023 	}
1024 	wcnt = namencnt(psa, i, maxlen);
1025 #if 0
1026 	/* dumps core on really long strings */
1027 	(void) printf(" %.*s", wcnt, psa);
1028 #else
1029 	(void) putchar(' ');
1030 	(void) fwrite(psa, 1, wcnt, stdout);
1031 #endif
1032 	return (1);
1033 }
1034 
1035 /*
1036  * Print starting time of process unless process started more than 24 hours
1037  * ago, in which case the date is printed.
1038  */
1039 static void
1040 prtime(timestruc_t st)
1041 {
1042 	char sttim[26];
1043 	static time_t tim = 0L;
1044 	time_t starttime;
1045 
1046 	if (tim == 0L)
1047 		tim = time((time_t *)0);
1048 	starttime = st.tv_sec;
1049 	if (tim - starttime > 24*60*60) {
1050 		(void) strftime(sttim, sizeof (sttim), "%b %d",
1051 		    localtime(&starttime));
1052 	} else {
1053 		(void) strftime(sttim, sizeof (sttim), "%H:%M:%S",
1054 		    localtime(&starttime));
1055 	}
1056 	(void) printf("%9.9s", sttim);
1057 }
1058 
1059 static void
1060 przom(psinfo_t *psinfo)
1061 {
1062 	long	tm;
1063 
1064 	if (lflg)
1065 		(void) printf("%2x", psinfo->pr_flag & 0377);
1066 	if (uflg) {
1067 		struct passwd *pwd;
1068 
1069 		if ((pwd = getpwuid(psinfo->pr_euid)) != NULL)
1070 			(void) printf("%-8.8s", pwd->pw_name);	/* USER */
1071 		else
1072 			(void) printf(" %7.7d", (int)psinfo->pr_euid); /* UID */
1073 	} else if (lflg)
1074 		(void) printf(" %5d", (int)psinfo->pr_euid);	/* UID */
1075 
1076 	(void) printf("%*d", pidwidth + 1, (int)psinfo->pr_pid); /* PID */
1077 	if (lflg)
1078 		(void) printf("%*d", pidwidth + 1,
1079 		    (int)psinfo->pr_ppid); /* PPID */
1080 	if (lflg)
1081 		(void) printf("  0");				/* CP */
1082 	if (uflg) {
1083 		prtpct(0);					/* %CPU */
1084 		prtpct(0);					/* %MEM */
1085 	}
1086 	if (lflg) {
1087 		(void) printf("%4d", psinfo->pr_lwp.pr_pri);	/* PRI */
1088 		(void) printf("   ");				/* NICE */
1089 	}
1090 	if (lflg || uflg) {
1091 		(void) printf("    0");				/* SZ */
1092 		(void) printf("    0");				/* RSS */
1093 	}
1094 	if (lflg)
1095 		(void) printf("         ");			/* WCHAN */
1096 	(void) printf("          ");				/* TTY */
1097 	(void) printf("%c", psinfo->pr_lwp.pr_sname);		/* STATE */
1098 	if (uflg)
1099 		(void) printf("         ");			/* START */
1100 
1101 	/* time just for process */
1102 	tm = psinfo->pr_time.tv_sec;
1103 	if (Sflg) {	/* calculate time for process and all reaped children */
1104 		tm += psinfo->pr_ctime.tv_sec;
1105 		if (psinfo->pr_time.tv_nsec + psinfo->pr_ctime.tv_nsec
1106 		    >= 1000000000)
1107 			tm += 1;
1108 	}
1109 	(void) printf(" %2ld:%.2ld", tm / 60, tm % 60);		/* TIME */
1110 
1111 	if (vflg) {
1112 		(void) printf("    0");				/* SZ */
1113 		(void) printf("    0");				/* RSS */
1114 		prtpct(0);					/* %CPU */
1115 		prtpct(0);					/* %MEM */
1116 	}
1117 	(void) printf(" %.*s", maxlen, " <defunct>");
1118 }
1119 
1120 /*
1121  * Returns true iff string is all numeric.
1122  */
1123 static int
1124 num(char *s)
1125 {
1126 	int c;
1127 
1128 	if (s == NULL)
1129 		return (0);
1130 	c = *s;
1131 	do {
1132 		if (!isdigit(c))
1133 			return (0);
1134 	} while ((c = *++s) != '\0');
1135 	return (1);
1136 }
1137 
1138 /*
1139  * Function to compute the number of printable bytes in a multibyte
1140  * command string ("internationalization").
1141  */
1142 static int
1143 namencnt(char *cmd, int eucsize, int scrsize)
1144 {
1145 	int eucwcnt = 0, scrwcnt = 0;
1146 	int neucsz, nscrsz;
1147 	wchar_t	wchar;
1148 
1149 	while (*cmd != '\0') {
1150 		if ((neucsz = mbtowc(&wchar, cmd, MB_LEN_MAX)) < 0)
1151 			return (8); /* default to use for illegal chars */
1152 		if ((nscrsz = scrwidth(wchar)) == 0)
1153 			return (8);
1154 		if (eucwcnt + neucsz > eucsize || scrwcnt + nscrsz > scrsize)
1155 			break;
1156 		eucwcnt += neucsz;
1157 		scrwcnt += nscrsz;
1158 		cmd += neucsz;
1159 	}
1160 	return (eucwcnt);
1161 }
1162 
1163 static int
1164 pscompare(const void *v1, const void *v2)
1165 {
1166 	const struct psent *p1 = v1;
1167 	const struct psent *p2 = v2;
1168 	int i;
1169 
1170 	if (uflg)
1171 		i = p2->psinfo->pr_pctcpu - p1->psinfo->pr_pctcpu;
1172 	else if (vflg)
1173 		i = p2->psinfo->pr_rssize - p1->psinfo->pr_rssize;
1174 	else
1175 		i = p1->psinfo->pr_ttydev - p2->psinfo->pr_ttydev;
1176 	if (i == 0)
1177 		i = p1->psinfo->pr_pid - p2->psinfo->pr_pid;
1178 	return (i);
1179 }
1180 
1181 static char *
1182 err_string(int err)
1183 {
1184 	static char buf[32];
1185 	char *str = strerror(err);
1186 
1187 	if (str == NULL)
1188 		(void) sprintf(str = buf, "Errno #%d", err);
1189 
1190 	return (str);
1191 }
1192