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