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