xref: /illumos-gate/usr/src/cmd/ps/ps.c (revision a57549b4a2006e67bc7e247816f7ccd7fb62fce6)
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 /*
23  * Copyright (c) 2013 Gary Mills
24  *
25  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  */
28 
29 /*
30  * Copyright (c) 2018, Joyent, Inc.
31  */
32 
33 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
34 /*	  All Rights Reserved	*/
35 
36 /*
37  * ps -- print things about processes.
38  */
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <string.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <pwd.h>
45 #include <grp.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <sys/mkdev.h>
49 #include <unistd.h>
50 #include <stdlib.h>
51 #include <limits.h>
52 #include <dirent.h>
53 #include <sys/signal.h>
54 #include <sys/fault.h>
55 #include <sys/syscall.h>
56 #include <sys/time.h>
57 #include <procfs.h>
58 #include <locale.h>
59 #include <wctype.h>
60 #include <wchar.h>
61 #include <libw.h>
62 #include <stdarg.h>
63 #include <sys/proc.h>
64 #include <sys/pset.h>
65 #include <project.h>
66 #include <zone.h>
67 
68 #define	min(a, b)	((a) > (b) ? (b) : (a))
69 #define	max(a, b)	((a) < (b) ? (b) : (a))
70 
71 #define	NTTYS	20	/* initial size of table for -t option  */
72 #define	SIZ	30	/* initial size of tables for -p, -s, -g, -h and -z */
73 
74 /*
75  * Size of buffer holding args for t, p, s, g, u, U, G, z options.
76  * Set to ZONENAME_MAX, the minimum value needed to allow any
77  * zone to be specified.
78  */
79 #define	ARGSIZ ZONENAME_MAX
80 
81 /* Max chars in a user/group name or printed u/g id */
82 #define	MAXUGNAME (LOGNAME_MAX+2)
83 
84 /* Structure for storing user or group info */
85 struct ugdata {
86 	id_t	id;			/* numeric user-id or group-id */
87 	char	name[MAXUGNAME+1];	/* user/group name, null terminated */
88 };
89 
90 struct ughead {
91 	size_t	size;		/* number of ugdata structs allocated */
92 	size_t	nent;		/* number of active entries */
93 	struct ugdata *ent;	/* pointer to array of actual entries */
94 };
95 
96 enum fname {	/* enumeration of field names */
97 	F_USER,		/* effective user of the process */
98 	F_RUSER,	/* real user of the process */
99 	F_GROUP,	/* effective group of the process */
100 	F_RGROUP,	/* real group of the process */
101 	F_UID,		/* numeric effective uid of the process */
102 	F_RUID,		/* numeric real uid of the process */
103 	F_GID,		/* numeric effective gid of the process */
104 	F_RGID,		/* numeric real gid of the process */
105 	F_PID,		/* process id */
106 	F_PPID,		/* parent process id */
107 	F_PGID,		/* process group id */
108 	F_SID,		/* session id */
109 	F_PSR,		/* bound processor */
110 	F_LWP,		/* lwp-id */
111 	F_LWPNAME,	/* lwp name */
112 	F_NLWP,		/* number of lwps */
113 	F_OPRI,		/* old priority (obsolete) */
114 	F_PRI,		/* new priority */
115 	F_F,		/* process flags */
116 	F_S,		/* letter indicating the state */
117 	F_C,		/* processor utilization (obsolete) */
118 	F_PCPU,		/* percent of recently used cpu time */
119 	F_PMEM,		/* percent of physical memory used (rss) */
120 	F_OSZ,		/* virtual size of the process in pages */
121 	F_VSZ,		/* virtual size of the process in kilobytes */
122 	F_RSS,		/* resident set size of the process in kilobytes */
123 	F_NICE,		/* "nice" value of the process */
124 	F_CLASS,	/* scheduler class */
125 	F_STIME,	/* start time of the process, hh:mm:ss or Month Day */
126 	F_ETIME,	/* elapsed time of the process, [[dd-]hh:]mm:ss */
127 	F_TIME,		/* cpu time of the process, [[dd-]hh:]mm:ss */
128 	F_TTY,		/* name of the controlling terminal */
129 	F_ADDR,		/* address of the process (obsolete) */
130 	F_WCHAN,	/* wait channel (sleep condition variable) */
131 	F_FNAME,	/* file name of command */
132 	F_COMM,		/* name of command (argv[0] value) */
133 	F_ARGS,		/* name of command plus all its arguments */
134 	F_TASKID,	/* task id */
135 	F_PROJID,	/* project id */
136 	F_PROJECT,	/* project name of the process */
137 	F_PSET,		/* bound processor set */
138 	F_ZONE,		/* zone name */
139 	F_ZONEID,	/* zone id */
140 	F_CTID,		/* process contract id */
141 	F_LGRP,		/* process home lgroup */
142 	F_DMODEL	/* process data model */
143 };
144 
145 struct field {
146 	struct field	*next;		/* linked list */
147 	int		fname;		/* field index */
148 	const char	*header;	/* header to use */
149 	int		width;		/* width of field */
150 };
151 
152 static	struct field *fields = NULL;	/* fields selected via -o */
153 static	struct field *last_field = NULL;
154 static	int do_header = 0;
155 static	struct timeval now;
156 
157 /* array of defined fields, in fname order */
158 struct def_field {
159 	const char *fname;
160 	const char *header;
161 	int width;
162 	int minwidth;
163 };
164 
165 static struct def_field fname[] = {
166 	/* fname	header		width	minwidth */
167 	{ "user",	"USER",		8,	8	},
168 	{ "ruser",	"RUSER",	8,	8	},
169 	{ "group",	"GROUP",	8,	8	},
170 	{ "rgroup",	"RGROUP",	8,	8	},
171 	{ "uid",	"UID",		5,	5	},
172 	{ "ruid",	"RUID",		5,	5	},
173 	{ "gid",	"GID",		5,	5	},
174 	{ "rgid",	"RGID",		5,	5	},
175 	{ "pid",	"PID",		5,	5	},
176 	{ "ppid",	"PPID",		5,	5	},
177 	{ "pgid",	"PGID",		5,	5	},
178 	{ "sid",	"SID",		5,	5	},
179 	{ "psr",	"PSR",		3,	2	},
180 	{ "lwp",	"LWP",		6,	2	},
181 	{ "lwpname",	"LWPNAME",	32,	8	},
182 	{ "nlwp",	"NLWP",		4,	2	},
183 	{ "opri",	"PRI",		3,	2	},
184 	{ "pri",	"PRI",		3,	2	},
185 	{ "f",		"F",		2,	2	},
186 	{ "s",		"S",		1,	1	},
187 	{ "c",		"C",		2,	2	},
188 	{ "pcpu",	"%CPU",		4,	4	},
189 	{ "pmem",	"%MEM",		4,	4	},
190 	{ "osz",	"SZ",		4,	4	},
191 	{ "vsz",	"VSZ",		4,	4	},
192 	{ "rss",	"RSS",		4,	4	},
193 	{ "nice",	"NI",		2,	2	},
194 	{ "class",	"CLS",		4,	2	},
195 	{ "stime",	"STIME",	8,	8	},
196 	{ "etime",	"ELAPSED",	11,	7	},
197 	{ "time",	"TIME",		11,	5	},
198 	{ "tty",	"TT",		7,	7	},
199 #ifdef _LP64
200 	{ "addr",	"ADDR",		16,	8	},
201 	{ "wchan",	"WCHAN",	16,	8	},
202 #else
203 	{ "addr",	"ADDR",		8,	8	},
204 	{ "wchan",	"WCHAN",	8,	8	},
205 #endif
206 	{ "fname",	"COMMAND",	8,	8	},
207 	{ "comm",	"COMMAND",	80,	8	},
208 	{ "args",	"COMMAND",	80,	80	},
209 	{ "taskid",	"TASKID",	5,	5	},
210 	{ "projid",	"PROJID",	5,	5	},
211 	{ "project",	"PROJECT",	8,	8	},
212 	{ "pset",	"PSET",		3,	3	},
213 	{ "zone",	"ZONE",		8,	8	},
214 	{ "zoneid",	"ZONEID",	5,	5	},
215 	{ "ctid",	"CTID",		5,	5	},
216 	{ "lgrp",	"LGRP",		4,	2	},
217 	{ "dmodel",	"DMODEL",	6,	6	},
218 };
219 
220 #define	NFIELDS	(sizeof (fname) / sizeof (fname[0]))
221 
222 static	int	retcode = 1;
223 static	int	lflg;
224 static	int	Aflg;
225 static	int	uflg;
226 static	int	Uflg;
227 static	int	Gflg;
228 static	int	aflg;
229 static	int	dflg;
230 static	int	Lflg;
231 static	int	Pflg;
232 static	int	Wflg;
233 static	int	yflg;
234 static	int	pflg;
235 static	int	fflg;
236 static	int	cflg;
237 static	int	jflg;
238 static	int	gflg;
239 static	int	sflg;
240 static	int	tflg;
241 static	int	zflg;
242 static	int	Zflg;
243 static	int	hflg;
244 static	int	Hflg;
245 static	uid_t	tuid = (uid_t)-1;
246 static	int	errflg;
247 
248 static	int	ndev;		/* number of devices */
249 static	int	maxdev;		/* number of devl structures allocated */
250 
251 #define	DNINCR	100
252 #define	DNSIZE	14
253 static struct devl {		/* device list   */
254 	char	dname[DNSIZE];	/* device name   */
255 	dev_t	ddev;		/* device number */
256 } *devl;
257 
258 static	struct tty {
259 	char *tname;
260 	dev_t tdev;
261 } *tty = NULL;			/* for t option */
262 static	size_t	ttysz = 0;
263 static	int	ntty = 0;
264 
265 static	pid_t	*pid = NULL;	/* for p option */
266 static	size_t	pidsz = 0;
267 static	size_t	npid = 0;
268 
269 static	int	*lgrps = NULL;	/* list of lgroup IDs for for h option */
270 static	size_t	lgrps_size = 0;	/* size of the lgrps list */
271 static	size_t	nlgrps = 0;	/* number elements in the list */
272 
273 /* Maximum possible lgroup ID value */
274 #define	MAX_LGRP_ID 256
275 
276 static	pid_t	*grpid = NULL;	/* for g option */
277 static	size_t	grpidsz = 0;
278 static	int	ngrpid = 0;
279 
280 static	pid_t	*sessid = NULL;	/* for s option */
281 static	size_t	sessidsz = 0;
282 static	int	nsessid = 0;
283 
284 static	zoneid_t *zoneid = NULL; /* for z option */
285 static	size_t	zoneidsz = 0;
286 static	int	nzoneid = 0;
287 
288 static	int	kbytes_per_page;
289 static	int	pidwidth;
290 
291 static	char	*procdir = "/proc";	/* standard /proc directory */
292 
293 static struct ughead	euid_tbl;	/* table to store selected euid's */
294 static struct ughead	ruid_tbl;	/* table to store selected real uid's */
295 static struct ughead	egid_tbl;	/* table to store selected egid's */
296 static struct ughead	rgid_tbl;	/* table to store selected real gid's */
297 static prheader_t *lpsinfobuf;		/* buffer to contain lpsinfo */
298 static size_t	lpbufsize;
299 
300 /*
301  * This constant defines the sentinal number of process IDs below which we
302  * only examine individual entries in /proc rather than scanning through
303  * /proc. This optimization is a huge win in the common case.
304  */
305 #define	PTHRESHOLD	40
306 
307 #define	UCB_OPTS	"-aceglnrtuvwxSU"
308 
309 static	void	usage(void);
310 static	char	*getarg(char **);
311 static	char	*parse_format(char *);
312 static	char	*gettty(psinfo_t *);
313 static	int	prfind(int, psinfo_t *, char **);
314 static	void	prcom(psinfo_t *, char *);
315 static	void	prtpct(ushort_t, int);
316 static	void	print_time(time_t, int);
317 static	void	print_field(psinfo_t *, struct field *, const char *);
318 static	void	print_zombie_field(psinfo_t *, struct field *, const char *);
319 static	void	pr_fields(psinfo_t *, const char *,
320 		void (*print_fld)(psinfo_t *, struct field *, const char *));
321 static	int	search(pid_t *, int, pid_t);
322 static	void	add_ugentry(struct ughead *, char *);
323 static	int	uconv(struct ughead *);
324 static	int	gconv(struct ughead *);
325 static	int	ugfind(id_t, struct ughead *);
326 static	void	prtime(timestruc_t, int, int);
327 static	void	przom(psinfo_t *);
328 static	int	namencnt(char *, int, int);
329 static	char	*err_string(int);
330 static	int	print_proc(char *pname);
331 static	time_t	delta_secs(const timestruc_t *);
332 static	int	str2id(const char *, pid_t *, long, long);
333 static	int	str2uid(const char *,  uid_t *, unsigned long, unsigned long);
334 static	void	*Realloc(void *, size_t);
335 static	int	pidcmp(const void *p1, const void *p2);
336 
337 extern	int	ucbmain(int, char **);
338 static	int	stdmain(int, char **);
339 
340 int
341 main(int argc, char **argv)
342 {
343 	const char *me;
344 
345 	/*
346 	 * The original two ps'es are linked in a single binary;
347 	 * their main()s are renamed to stdmain for /usr/bin/ps and
348 	 * ucbmain for /usr/ucb/ps.
349 	 * We try to figure out which instance of ps the user wants to run.
350 	 * Traditionally, the UCB variant doesn't require the flag argument
351 	 * start with a "-".  If the first argument doesn't start with a
352 	 * "-", we call "ucbmain".
353 	 * If there's a first argument and it starts with a "-", we check
354 	 * whether any of the options isn't acceptable to "ucbmain"; in that
355 	 * case we run "stdmain".
356 	 * If we can't tell from the options which main to call, we check
357 	 * the binary we are running.  We default to "stdmain" but
358 	 * any mention in the executable name of "ucb" causes us to call
359 	 * ucbmain.
360 	 */
361 	if (argv[1] != NULL) {
362 		if (argv[1][0] != '-')
363 			return (ucbmain(argc, argv));
364 		else if (argv[1][strspn(argv[1], UCB_OPTS)] != '\0')
365 			return (stdmain(argc, argv));
366 	}
367 
368 	me = getexecname();
369 
370 	if (me != NULL && strstr(me, "ucb") != NULL)
371 		return (ucbmain(argc, argv));
372 	else
373 		return (stdmain(argc, argv));
374 }
375 
376 static int
377 stdmain(int argc, char **argv)
378 {
379 	char	*p;
380 	char	*p1;
381 	char	*parg;
382 	int	c;
383 	int	i;
384 	int	pgerrflg = 0;	/* err flg: non-numeric arg w/p & g options */
385 	size_t	size, len;
386 	DIR	*dirp;
387 	struct dirent *dentp;
388 	pid_t	maxpid;
389 	pid_t	id;
390 	int	ret;
391 	char	loc_stime_str[32];
392 
393 	(void) setlocale(LC_ALL, "");
394 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
395 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
396 #endif
397 	(void) textdomain(TEXT_DOMAIN);
398 
399 	(void) memset(&euid_tbl, 0, sizeof (euid_tbl));
400 	(void) memset(&ruid_tbl, 0, sizeof (ruid_tbl));
401 	(void) memset(&egid_tbl, 0, sizeof (egid_tbl));
402 	(void) memset(&rgid_tbl, 0, sizeof (rgid_tbl));
403 
404 	kbytes_per_page = sysconf(_SC_PAGESIZE) / 1024;
405 
406 	(void) gettimeofday(&now, NULL);
407 
408 	/*
409 	 * calculate width of pid fields based on configured MAXPID
410 	 * (must be at least 5 to retain output format compatibility)
411 	 */
412 	id = maxpid = (pid_t)sysconf(_SC_MAXPID);
413 	pidwidth = 1;
414 	while ((id /= 10) > 0)
415 		++pidwidth;
416 	pidwidth = pidwidth < 5 ? 5 : pidwidth;
417 
418 	fname[F_PID].width = fname[F_PPID].width = pidwidth;
419 	fname[F_PGID].width = fname[F_SID].width = pidwidth;
420 
421 	/*
422 	 * TRANSLATION_NOTE
423 	 * Specify the printf format with width and precision for
424 	 * the STIME field.
425 	 */
426 	len = snprintf(loc_stime_str, sizeof (loc_stime_str),
427 	    dcgettext(NULL, "%8.8s", LC_TIME), "STIME");
428 	if (len >= sizeof (loc_stime_str))
429 		len = sizeof (loc_stime_str) - 1;
430 
431 	fname[F_STIME].width = fname[F_STIME].minwidth = len;
432 
433 	while ((c = getopt(argc, argv, "jlfceAadLPWyZHh:t:p:g:u:U:G:n:s:o:z:"))
434 	    != EOF)
435 		switch (c) {
436 		case 'H':		/* Show home lgroups */
437 			Hflg++;
438 			break;
439 		case 'h':
440 			/*
441 			 * Show processes/threads with given home lgroups
442 			 */
443 			hflg++;
444 			p1 = optarg;
445 			do {
446 				int id;
447 
448 				/*
449 				 * Get all IDs in the list, verify for
450 				 * correctness and place in lgrps array.
451 				 */
452 				parg = getarg(&p1);
453 				/* Convert string to integer */
454 				ret = str2id(parg, (pid_t *)&id, 0,
455 				    MAX_LGRP_ID);
456 				/* Complain if ID didn't parse correctly */
457 				if (ret != 0) {
458 					pgerrflg++;
459 					(void) fprintf(stderr,
460 					    gettext("ps: %s "), parg);
461 					if (ret == EINVAL)
462 						(void) fprintf(stderr,
463 						    gettext("is an invalid "
464 						    "non-numeric argument"));
465 					else
466 						(void) fprintf(stderr,
467 						    gettext("exceeds valid "
468 						    "range"));
469 					(void) fprintf(stderr,
470 					    gettext(" for -h option\n"));
471 					continue;
472 				}
473 
474 				/* Extend lgrps array if needed */
475 				if (nlgrps == lgrps_size) {
476 					/* Double the size of the lgrps array */
477 					if (lgrps_size == 0)
478 						lgrps_size = SIZ;
479 					lgrps_size *= 2;
480 					lgrps = Realloc(lgrps,
481 					    lgrps_size * sizeof (int));
482 				}
483 				/* place the id in the lgrps table */
484 				lgrps[nlgrps++] = id;
485 			} while (*p1);
486 			break;
487 		case 'l':		/* long listing */
488 			lflg++;
489 			break;
490 		case 'f':		/* full listing */
491 			fflg++;
492 			break;
493 		case 'j':
494 			jflg++;
495 			break;
496 		case 'c':
497 			/*
498 			 * Format output to reflect scheduler changes:
499 			 * high numbers for high priorities and don't
500 			 * print nice or p_cpu values.  'c' option only
501 			 * effective when used with 'l' or 'f' options.
502 			 */
503 			cflg++;
504 			break;
505 		case 'A':		/* list every process */
506 		case 'e':		/* (obsolete) list every process */
507 			Aflg++;
508 			tflg = Gflg = Uflg = uflg = pflg = gflg = sflg = 0;
509 			zflg = hflg = 0;
510 			break;
511 		case 'a':
512 			/*
513 			 * Same as 'e' except no session group leaders
514 			 * and no non-terminal processes.
515 			 */
516 			aflg++;
517 			break;
518 		case 'd':	/* same as e except no session leaders */
519 			dflg++;
520 			break;
521 		case 'L':	/* show lwps */
522 			Lflg++;
523 			break;
524 		case 'P':	/* show bound processor */
525 			Pflg++;
526 			break;
527 		case 'W':	/* truncate long names */
528 			Wflg++;
529 			break;
530 		case 'y':	/* omit F & ADDR, report RSS & SZ in Kby */
531 			yflg++;
532 			break;
533 		case 'n':	/* no longer needed; retain as no-op */
534 			(void) fprintf(stderr,
535 			    gettext("ps: warning: -n option ignored\n"));
536 			break;
537 		case 't':		/* terminals */
538 #define	TSZ	30
539 			tflg++;
540 			p1 = optarg;
541 			do {
542 				char nambuf[TSZ+6];	/* for "/dev/" + '\0' */
543 				struct stat64 s;
544 				parg = getarg(&p1);
545 				p = Realloc(NULL, TSZ+1);	/* for '\0' */
546 				/* zero the buffer before using it */
547 				p[0] = '\0';
548 				size = TSZ;
549 				if (isdigit(*parg)) {
550 					(void) strcpy(p, "tty");
551 					size -= 3;
552 				}
553 				(void) strncat(p, parg, size);
554 				if (ntty == ttysz) {
555 					if ((ttysz *= 2) == 0)
556 						ttysz = NTTYS;
557 					tty = Realloc(tty,
558 					    (ttysz + 1) * sizeof (struct tty));
559 				}
560 				tty[ntty].tdev = PRNODEV;
561 				(void) strcpy(nambuf, "/dev/");
562 				(void) strcat(nambuf, p);
563 				if (stat64(nambuf, &s) == 0)
564 					tty[ntty].tdev = s.st_rdev;
565 				tty[ntty++].tname = p;
566 			} while (*p1);
567 			break;
568 		case 'p':		/* proc ids */
569 			pflg++;
570 			p1 = optarg;
571 			do {
572 				pid_t id;
573 
574 				parg = getarg(&p1);
575 				if ((ret = str2id(parg, &id, 0, maxpid)) != 0) {
576 					pgerrflg++;
577 					(void) fprintf(stderr,
578 					    gettext("ps: %s "), parg);
579 					if (ret == EINVAL)
580 						(void) fprintf(stderr,
581 						    gettext("is an invalid "
582 						    "non-numeric argument"));
583 					else
584 						(void) fprintf(stderr,
585 						    gettext("exceeds valid "
586 						    "range"));
587 					(void) fprintf(stderr,
588 					    gettext(" for -p option\n"));
589 					continue;
590 				}
591 
592 				if (npid == pidsz) {
593 					if ((pidsz *= 2) == 0)
594 						pidsz = SIZ;
595 					pid = Realloc(pid,
596 					    pidsz * sizeof (pid_t));
597 				}
598 				pid[npid++] = id;
599 			} while (*p1);
600 			break;
601 		case 's':		/* session */
602 			sflg++;
603 			p1 = optarg;
604 			do {
605 				pid_t id;
606 
607 				parg = getarg(&p1);
608 				if ((ret = str2id(parg, &id, 0, maxpid)) != 0) {
609 					pgerrflg++;
610 					(void) fprintf(stderr,
611 					    gettext("ps: %s "), parg);
612 					if (ret == EINVAL)
613 						(void) fprintf(stderr,
614 						    gettext("is an invalid "
615 						    "non-numeric argument"));
616 					else
617 						(void) fprintf(stderr,
618 						    gettext("exceeds valid "
619 						    "range"));
620 					(void) fprintf(stderr,
621 					    gettext(" for -s option\n"));
622 					continue;
623 				}
624 
625 				if (nsessid == sessidsz) {
626 					if ((sessidsz *= 2) == 0)
627 						sessidsz = SIZ;
628 					sessid = Realloc(sessid,
629 					    sessidsz * sizeof (pid_t));
630 				}
631 				sessid[nsessid++] = id;
632 			} while (*p1);
633 			break;
634 		case 'g':		/* proc group */
635 			gflg++;
636 			p1 = optarg;
637 			do {
638 				pid_t id;
639 
640 				parg = getarg(&p1);
641 				if ((ret = str2id(parg, &id, 0, maxpid)) != 0) {
642 					pgerrflg++;
643 					(void) fprintf(stderr,
644 					    gettext("ps: %s "), parg);
645 					if (ret == EINVAL)
646 						(void) fprintf(stderr,
647 						    gettext("is an invalid "
648 						    "non-numeric argument"));
649 					else
650 						(void) fprintf(stderr,
651 						    gettext("exceeds valid "
652 						    "range"));
653 					(void) fprintf(stderr,
654 					    gettext(" for -g option\n"));
655 					continue;
656 				}
657 
658 				if (ngrpid == grpidsz) {
659 					if ((grpidsz *= 2) == 0)
660 						grpidsz = SIZ;
661 					grpid = Realloc(grpid,
662 					    grpidsz * sizeof (pid_t));
663 				}
664 				grpid[ngrpid++] = id;
665 			} while (*p1);
666 			break;
667 		case 'u':		/* effective user name or number */
668 			uflg++;
669 			p1 = optarg;
670 			do {
671 				parg = getarg(&p1);
672 				add_ugentry(&euid_tbl, parg);
673 			} while (*p1);
674 			break;
675 		case 'U':		/* real user name or number */
676 			Uflg++;
677 			p1 = optarg;
678 			do {
679 				parg = getarg(&p1);
680 				add_ugentry(&ruid_tbl, parg);
681 			} while (*p1);
682 			break;
683 		case 'G':		/* real group name or number */
684 			Gflg++;
685 			p1 = optarg;
686 			do {
687 				parg = getarg(&p1);
688 				add_ugentry(&rgid_tbl, parg);
689 			} while (*p1);
690 			break;
691 		case 'o':		/* output format */
692 			p = optarg;
693 			while ((p = parse_format(p)) != NULL)
694 				;
695 			break;
696 		case 'z':		/* zone name or number */
697 			zflg++;
698 			p1 = optarg;
699 			do {
700 				zoneid_t id;
701 
702 				parg = getarg(&p1);
703 				if (zone_get_id(parg, &id) != 0) {
704 					pgerrflg++;
705 					(void) fprintf(stderr,
706 					    gettext("ps: unknown zone %s\n"),
707 					    parg);
708 					continue;
709 				}
710 
711 				if (nzoneid == zoneidsz) {
712 					if ((zoneidsz *= 2) == 0)
713 						zoneidsz = SIZ;
714 					zoneid = Realloc(zoneid,
715 					    zoneidsz * sizeof (zoneid_t));
716 				}
717 				zoneid[nzoneid++] = id;
718 			} while (*p1);
719 			break;
720 		case 'Z':		/* show zone name */
721 			Zflg++;
722 			break;
723 		default:			/* error on ? */
724 			errflg++;
725 			break;
726 		}
727 
728 	if (errflg || optind < argc || pgerrflg)
729 		usage();
730 
731 	if (tflg)
732 		tty[ntty].tname = NULL;
733 	/*
734 	 * If an appropriate option has not been specified, use the
735 	 * current terminal and effective uid as the default.
736 	 */
737 	if (!(aflg|Aflg|dflg|Gflg|hflg|Uflg|uflg|tflg|pflg|gflg|sflg|zflg)) {
738 		psinfo_t info;
739 		int procfd;
740 		char *name;
741 		char pname[100];
742 
743 		/* get our own controlling tty name using /proc */
744 		(void) snprintf(pname, sizeof (pname),
745 		    "%s/self/psinfo", procdir);
746 		if ((procfd = open(pname, O_RDONLY)) < 0 ||
747 		    read(procfd, (char *)&info, sizeof (info)) < 0 ||
748 		    info.pr_ttydev == PRNODEV) {
749 			(void) fprintf(stderr,
750 			    gettext("ps: no controlling terminal\n"));
751 			exit(1);
752 		}
753 		(void) close(procfd);
754 
755 		i = 0;
756 		name = gettty(&info);
757 		if (*name == '?') {
758 			(void) fprintf(stderr,
759 			    gettext("ps: can't find controlling terminal\n"));
760 			exit(1);
761 		}
762 		if (ntty == ttysz) {
763 			if ((ttysz *= 2) == 0)
764 				ttysz = NTTYS;
765 			tty = Realloc(tty, (ttysz + 1) * sizeof (struct tty));
766 		}
767 		tty[ntty].tdev = info.pr_ttydev;
768 		tty[ntty++].tname = name;
769 		tty[ntty].tname = NULL;
770 		tflg++;
771 		tuid = getuid();
772 	}
773 	if (Aflg) {
774 		Gflg = Uflg = uflg = pflg = sflg = gflg = aflg = dflg = 0;
775 		zflg = hflg = 0;
776 	}
777 	if (Aflg | aflg | dflg)
778 		tflg = 0;
779 
780 	i = 0;		/* prepare to exit on name lookup errors */
781 	i += uconv(&euid_tbl);
782 	i += uconv(&ruid_tbl);
783 	i += gconv(&egid_tbl);
784 	i += gconv(&rgid_tbl);
785 	if (i)
786 		exit(1);
787 
788 	/* allocate a buffer for lwpsinfo structures */
789 	lpbufsize = 4096;
790 	if (Lflg && (lpsinfobuf = malloc(lpbufsize)) == NULL) {
791 		(void) fprintf(stderr,
792 		    gettext("ps: no memory\n"));
793 		exit(1);
794 	}
795 
796 	if (fields) {	/* print user-specified header */
797 		if (do_header) {
798 			struct field *f;
799 
800 			for (f = fields; f != NULL; f = f->next) {
801 				if (f != fields)
802 					(void) printf(" ");
803 				switch (f->fname) {
804 				case F_TTY:
805 					(void) printf("%-*s",
806 					    f->width, f->header);
807 					break;
808 				case F_LWPNAME:
809 				case F_FNAME:
810 				case F_COMM:
811 				case F_ARGS:
812 					/*
813 					 * Print these headers full width
814 					 * unless they appear at the end.
815 					 */
816 					if (f->next != NULL) {
817 						(void) printf("%-*s",
818 						    f->width, f->header);
819 					} else {
820 						(void) printf("%s",
821 						    f->header);
822 					}
823 					break;
824 				default:
825 					(void) printf("%*s",
826 					    f->width, f->header);
827 					break;
828 				}
829 			}
830 			(void) printf("\n");
831 		}
832 	} else {	/* print standard header */
833 		/*
834 		 * All fields before 'PID' are printed with a trailing space
835 		 * as a separator and that is how we print the headers too.
836 		 */
837 		if (lflg) {
838 			if (yflg)
839 				(void) printf("S ");
840 			else
841 				(void) printf(" F S ");
842 		}
843 		if (Zflg)
844 			(void) printf("    ZONE ");
845 		if (fflg) {
846 			(void) printf("     UID ");
847 		} else if (lflg)
848 			(void) printf("   UID ");
849 
850 		(void) printf("%*s", pidwidth,  "PID");
851 		if (lflg || fflg)
852 			(void) printf(" %*s", pidwidth, "PPID");
853 		if (jflg)
854 			(void) printf(" %*s %*s", pidwidth, "PGID",
855 			    pidwidth, "SID");
856 		if (Lflg)
857 			(void) printf("   LWP");
858 		if (Pflg)
859 			(void) printf(" PSR");
860 		if (Lflg && fflg)
861 			(void) printf("  NLWP");
862 		if (cflg)
863 			(void) printf("  CLS PRI");
864 		else if (lflg || fflg) {
865 			(void) printf("   C");
866 			if (lflg)
867 				(void) printf(" PRI NI");
868 		}
869 		if (lflg) {
870 			if (yflg)
871 				(void) printf("   RSS     SZ    WCHAN");
872 			else
873 				(void) printf("     ADDR     SZ    WCHAN");
874 		}
875 		if (fflg)
876 			(void) printf(" %s", loc_stime_str);
877 		if (Hflg)
878 			(void) printf(" LGRP");
879 		if (Lflg)
880 			(void) printf(" TTY        LTIME CMD\n");
881 		else
882 			(void) printf(" TTY         TIME CMD\n");
883 	}
884 
885 
886 	if (pflg && !(aflg|Aflg|dflg|Gflg|Uflg|uflg|hflg|tflg|gflg|sflg|zflg) &&
887 	    npid <= PTHRESHOLD) {
888 		/*
889 		 * If we are looking at specific processes go straight
890 		 * to their /proc entries and don't scan /proc.
891 		 */
892 		int i;
893 
894 		(void) qsort(pid, npid, sizeof (pid_t), pidcmp);
895 		for (i = 0; i < npid; i++) {
896 			char pname[12];
897 
898 			if (i >= 1 && pid[i] == pid[i - 1])
899 				continue;
900 			(void) sprintf(pname, "%d", (int)pid[i]);
901 			if (print_proc(pname) == 0)
902 				retcode = 0;
903 		}
904 	} else {
905 		/*
906 		 * Determine which processes to print info about by searching
907 		 * the /proc directory and looking at each process.
908 		 */
909 		if ((dirp = opendir(procdir)) == NULL) {
910 			(void) fprintf(stderr,
911 			    gettext("ps: cannot open PROC directory %s\n"),
912 			    procdir);
913 			exit(1);
914 		}
915 
916 		/* for each active process --- */
917 		while ((dentp = readdir(dirp)) != NULL) {
918 			if (dentp->d_name[0] == '.')    /* skip . and .. */
919 				continue;
920 			if (print_proc(dentp->d_name) == 0)
921 				retcode = 0;
922 		}
923 
924 		(void) closedir(dirp);
925 	}
926 	return (retcode);
927 }
928 
929 
930 int
931 print_proc(char *pid_name)
932 {
933 	char	pname[PATH_MAX];
934 	int	pdlen;
935 	int	found;
936 	int	procfd; /* filedescriptor for /proc/nnnnn/psinfo */
937 	char	*tp;    /* ptr to ttyname,  if any */
938 	psinfo_t info;  /* process information from /proc */
939 
940 	pdlen = snprintf(pname, sizeof (pname), "%s/%s/", procdir, pid_name);
941 	if (pdlen >= sizeof (pname) - 10)
942 		return (1);
943 retry:
944 	(void) strcpy(&pname[pdlen], "psinfo");
945 	if ((procfd = open(pname, O_RDONLY)) == -1) {
946 		/* Process may have exited meanwhile. */
947 		return (1);
948 	}
949 	/*
950 	 * Get the info structure for the process and close quickly.
951 	 */
952 	if (read(procfd, (char *)&info, sizeof (info)) < 0) {
953 		int	saverr = errno;
954 
955 		(void) close(procfd);
956 		if (saverr == EAGAIN)
957 			goto retry;
958 		if (saverr != ENOENT)
959 			(void) fprintf(stderr,
960 			    gettext("ps: read() on %s: %s\n"),
961 			    pname, err_string(saverr));
962 		return (1);
963 	}
964 	(void) close(procfd);
965 
966 	found = 0;
967 	if (info.pr_lwp.pr_state == 0)	/* can't happen? */
968 		return (1);
969 
970 	/*
971 	 * Omit session group leaders for 'a' and 'd' options.
972 	 */
973 	if ((info.pr_pid == info.pr_sid) && (dflg || aflg))
974 		return (1);
975 	if (Aflg || dflg)
976 		found++;
977 	else if (pflg && search(pid, npid, info.pr_pid))
978 		found++;	/* ppid in p option arg list */
979 	else if (uflg && ugfind((id_t)info.pr_euid, &euid_tbl))
980 		found++;	/* puid in u option arg list */
981 	else if (Uflg && ugfind((id_t)info.pr_uid, &ruid_tbl))
982 		found++;	/* puid in U option arg list */
983 #ifdef NOT_YET
984 	else if (gflg && ugfind((id_t)info.pr_egid, &egid_tbl))
985 		found++;	/* pgid in g option arg list */
986 #endif	/* NOT_YET */
987 	else if (Gflg && ugfind((id_t)info.pr_gid, &rgid_tbl))
988 		found++;	/* pgid in G option arg list */
989 	else if (gflg && search(grpid, ngrpid, info.pr_pgid))
990 		found++;	/* grpid in g option arg list */
991 	else if (sflg && search(sessid, nsessid, info.pr_sid))
992 		found++;	/* sessid in s option arg list */
993 	else if (zflg && search(zoneid, nzoneid, info.pr_zoneid))
994 		found++;	/* zoneid in z option arg list */
995 	else if (hflg && search((pid_t *)lgrps, nlgrps, info.pr_lwp.pr_lgrp))
996 		found++;	/* home lgroup in h option arg list */
997 	if (!found && !tflg && !aflg)
998 		return (1);
999 	if (!prfind(found, &info, &tp))
1000 		return (1);
1001 	if (Lflg && (info.pr_nlwp + info.pr_nzomb) > 1) {
1002 		ssize_t prsz;
1003 		long nlwp = 0;
1004 		lwpsinfo_t *lwpsinfo;   /* array of lwpsinfo structs */
1005 
1006 		(void) strcpy(&pname[pdlen], "lpsinfo");
1007 		if ((procfd = open(pname, O_RDONLY)) == -1)
1008 			return (1);
1009 		/*
1010 		 * Get the info structures for the lwps.
1011 		 */
1012 		prsz = read(procfd, lpsinfobuf, lpbufsize);
1013 		if (prsz == -1) {
1014 			int	saverr = errno;
1015 
1016 			(void) close(procfd);
1017 			if (saverr == EAGAIN)
1018 				goto retry;
1019 			if (saverr != ENOENT)
1020 				(void) fprintf(stderr,
1021 				    gettext("ps: read() on %s: %s\n"),
1022 				    pname, err_string(saverr));
1023 			return (1);
1024 		}
1025 		(void) close(procfd);
1026 		if (prsz == lpbufsize) {
1027 			/*
1028 			 * buffer overflow. Realloc new buffer.
1029 			 * Error handling is done in Realloc().
1030 			 */
1031 			lpbufsize *= 2;
1032 			lpsinfobuf = Realloc(lpsinfobuf, lpbufsize);
1033 			goto retry;
1034 		}
1035 		if (lpsinfobuf->pr_nent != (info.pr_nlwp + info.pr_nzomb))
1036 			goto retry;
1037 		lwpsinfo = (lwpsinfo_t *)(lpsinfobuf + 1);
1038 		do {
1039 			info.pr_lwp = *lwpsinfo;
1040 			prcom(&info, tp);
1041 			lwpsinfo = (lwpsinfo_t *)((char *)lwpsinfo +
1042 			    lpsinfobuf->pr_entsize);
1043 		} while (++nlwp < lpsinfobuf->pr_nent);
1044 	} else {
1045 		prcom(&info, tp);
1046 	}
1047 	return (0);
1048 }
1049 
1050 static int
1051 field_cmp(const void *l, const void *r)
1052 {
1053 	struct def_field *lhs = *((struct def_field **)l);
1054 	struct def_field *rhs = *((struct def_field **)r);
1055 
1056 	return (strcmp(lhs->fname, rhs->fname));
1057 }
1058 
1059 static void
1060 usage(void)		/* print usage message and quit */
1061 {
1062 	struct def_field *df, *sorted[NFIELDS];
1063 	int pos = 80, i = 0;
1064 
1065 	static char usage1[] =
1066 	    "ps [ -aAdefHlcjLPWyZ ] [ -o format ] [ -t termlist ]";
1067 	static char usage2[] =
1068 	    "\t[ -u userlist ] [ -U userlist ] [ -G grouplist ]";
1069 	static char usage3[] =
1070 	    "\t[ -p proclist ] [ -g pgrplist ] [ -s sidlist ]";
1071 	static char usage4[] =
1072 	    "\t[ -z zonelist ] [-h lgrplist]";
1073 	static char usage5[] =
1074 	    "  'format' is one or more of:";
1075 
1076 	(void) fprintf(stderr,
1077 	    gettext("usage: %s\n%s\n%s\n%s\n%s"),
1078 	    gettext(usage1), gettext(usage2), gettext(usage3),
1079 	    gettext(usage4), gettext(usage5));
1080 
1081 	/*
1082 	 * Now print out the possible output formats such that they neatly fit
1083 	 * into eighty columns.  Note that the fact that we are determining
1084 	 * this output programmatically means that a gettext() is impossible --
1085 	 * but it would be a mistake to localize the output formats anyway as
1086 	 * they are tokens for input, not output themselves.
1087 	 */
1088 	for (df = &fname[0]; df < &fname[NFIELDS]; df++)
1089 		sorted[i++] = df;
1090 
1091 	(void) qsort(sorted, NFIELDS, sizeof (void *), field_cmp);
1092 
1093 	for (i = 0; i < NFIELDS; i++) {
1094 		if (pos + strlen((df = sorted[i])->fname) + 1 >= 80) {
1095 			(void) fprintf(stderr, "\n\t");
1096 			pos = 8;
1097 		}
1098 
1099 		(void) fprintf(stderr, "%s%s", pos > 8 ? " " : "", df->fname);
1100 		pos += strlen(df->fname) + 1;
1101 	}
1102 
1103 	(void) fprintf(stderr, "\n");
1104 
1105 	exit(1);
1106 }
1107 
1108 /*
1109  * getarg() finds the next argument in list and copies arg into argbuf.
1110  * p1 first pts to arg passed back from getopt routine.  p1 is then
1111  * bumped to next character that is not a comma or blank -- p1 NULL
1112  * indicates end of list.
1113  */
1114 static char *
1115 getarg(char **pp1)
1116 {
1117 	static char argbuf[ARGSIZ];
1118 	char *p1 = *pp1;
1119 	char *parga = argbuf;
1120 	int c;
1121 
1122 	while ((c = *p1) != '\0' && (c == ',' || isspace(c)))
1123 		p1++;
1124 
1125 	while ((c = *p1) != '\0' && c != ',' && !isspace(c)) {
1126 		if (parga < argbuf + ARGSIZ - 1)
1127 			*parga++ = c;
1128 		p1++;
1129 	}
1130 	*parga = '\0';
1131 
1132 	while ((c = *p1) != '\0' && (c == ',' || isspace(c)))
1133 		p1++;
1134 
1135 	*pp1 = p1;
1136 
1137 	return (argbuf);
1138 }
1139 
1140 /*
1141  * parse_format() takes the argument to the -o option,
1142  * sets up the next output field structure, and returns
1143  * a pointer to any further output field specifier(s).
1144  * As a side-effect, it increments errflg if encounters a format error.
1145  */
1146 static char *
1147 parse_format(char *arg)
1148 {
1149 	int c;
1150 	char *name;
1151 	char *header = NULL;
1152 	int width = 0;
1153 	struct def_field *df;
1154 	struct field *f;
1155 
1156 	while ((c = *arg) != '\0' && (c == ',' || isspace(c)))
1157 		arg++;
1158 	if (c == '\0')
1159 		return (NULL);
1160 	name = arg;
1161 	arg = strpbrk(arg, " \t\r\v\f\n,=");
1162 	if (arg != NULL) {
1163 		c = *arg;
1164 		*arg++ = '\0';
1165 		if (c == '=') {
1166 			char *s;
1167 
1168 			header = arg;
1169 			arg = NULL;
1170 			width = strlen(header);
1171 			s = header + width;
1172 			while (s > header && isspace(*--s))
1173 				*s = '\0';
1174 			while (isspace(*header))
1175 				header++;
1176 		}
1177 	}
1178 	for (df = &fname[0]; df < &fname[NFIELDS]; df++)
1179 		if (strcmp(name, df->fname) == 0) {
1180 			if (strcmp(name, "lwp") == 0 ||
1181 			    strcmp(name, "lwpname") == 0)
1182 				Lflg++;
1183 			break;
1184 		}
1185 	if (df >= &fname[NFIELDS]) {
1186 		(void) fprintf(stderr,
1187 		    gettext("ps: unknown output format: -o %s\n"),
1188 		    name);
1189 		errflg++;
1190 		return (arg);
1191 	}
1192 	if ((f = malloc(sizeof (*f))) == NULL) {
1193 		(void) fprintf(stderr,
1194 		    gettext("ps: malloc() for output format failed, %s\n"),
1195 		    err_string(errno));
1196 		exit(1);
1197 	}
1198 	f->next = NULL;
1199 	f->fname = df - &fname[0];
1200 	f->header = header? header : df->header;
1201 	if (width == 0)
1202 		width = df->width;
1203 	if (*f->header != '\0')
1204 		do_header = 1;
1205 	f->width = max(width, df->minwidth);
1206 
1207 	if (fields == NULL)
1208 		fields = last_field = f;
1209 	else {
1210 		last_field->next = f;
1211 		last_field = f;
1212 	}
1213 
1214 	return (arg);
1215 }
1216 
1217 static char *
1218 devlookup(dev_t ddev)
1219 {
1220 	struct devl *dp;
1221 	int i;
1222 
1223 	for (dp = devl, i = 0; i < ndev; dp++, i++) {
1224 		if (dp->ddev == ddev)
1225 			return (dp->dname);
1226 	}
1227 	return (NULL);
1228 }
1229 
1230 static char *
1231 devadd(char *name, dev_t ddev)
1232 {
1233 	struct devl *dp;
1234 	int leng, start, i;
1235 
1236 	if (ndev == maxdev) {
1237 		maxdev += DNINCR;
1238 		devl = Realloc(devl, maxdev * sizeof (struct devl));
1239 	}
1240 	dp = &devl[ndev++];
1241 
1242 	dp->ddev = ddev;
1243 	if (name == NULL) {
1244 		(void) strcpy(dp->dname, "??");
1245 		return (dp->dname);
1246 	}
1247 
1248 	leng = strlen(name);
1249 	/* Strip off /dev/ */
1250 	if (leng < DNSIZE + 4)
1251 		(void) strcpy(dp->dname, &name[5]);
1252 	else {
1253 		start = leng - DNSIZE - 1;
1254 
1255 		for (i = start; i < leng && name[i] != '/'; i++)
1256 				;
1257 		if (i == leng)
1258 			(void) strncpy(dp->dname, &name[start], DNSIZE);
1259 		else
1260 			(void) strncpy(dp->dname, &name[i+1], DNSIZE);
1261 	}
1262 	return (dp->dname);
1263 }
1264 
1265 /*
1266  * gettty returns the user's tty number or ? if none.
1267  */
1268 static char *
1269 gettty(psinfo_t *psinfo)
1270 {
1271 	extern char *_ttyname_dev(dev_t, char *, size_t);
1272 	static zoneid_t zid = -1;
1273 	char devname[TTYNAME_MAX];
1274 	char *retval;
1275 
1276 	if (zid == -1)
1277 		zid = getzoneid();
1278 
1279 	if (psinfo->pr_ttydev == PRNODEV || psinfo->pr_zoneid != zid)
1280 		return ("?");
1281 
1282 	if ((retval = devlookup(psinfo->pr_ttydev)) != NULL)
1283 		return (retval);
1284 
1285 	retval = _ttyname_dev(psinfo->pr_ttydev, devname, sizeof (devname));
1286 
1287 	return (devadd(retval, psinfo->pr_ttydev));
1288 }
1289 
1290 /*
1291  * Find the process's tty and return 1 if process is to be printed.
1292  */
1293 static int
1294 prfind(int found, psinfo_t *psinfo, char **tpp)
1295 {
1296 	char	*tp;
1297 	struct tty *ttyp;
1298 
1299 	if (psinfo->pr_nlwp == 0) {
1300 		/* process is a zombie */
1301 		*tpp = "?";
1302 		if (tflg && !found)
1303 			return (0);
1304 		return (1);
1305 	}
1306 
1307 	/*
1308 	 * Get current terminal.  If none ("?") and 'a' is set, don't print
1309 	 * info.  If 't' is set, check if term is in list of desired terminals
1310 	 * and print it if it is.
1311 	 */
1312 	tp = gettty(psinfo);
1313 	if (aflg && *tp == '?') {
1314 		*tpp = tp;
1315 		return (0);
1316 	}
1317 	if (tflg && !found) {
1318 		int match = 0;
1319 		char *other = NULL;
1320 		for (ttyp = tty; ttyp->tname != NULL; ttyp++) {
1321 			/*
1322 			 * Look for a name match
1323 			 */
1324 			if (strcmp(tp, ttyp->tname) == 0) {
1325 				match = 1;
1326 				break;
1327 			}
1328 			/*
1329 			 * Look for same device under different names.
1330 			 */
1331 			if ((other == NULL) &&
1332 			    (ttyp->tdev != PRNODEV) &&
1333 			    (psinfo->pr_ttydev == ttyp->tdev))
1334 				other = ttyp->tname;
1335 		}
1336 		if (!match && (other != NULL)) {
1337 			/*
1338 			 * found under a different name
1339 			 */
1340 			match = 1;
1341 			tp = other;
1342 		}
1343 		if (!match || (tuid != (uid_t)-1 && tuid != psinfo->pr_euid)) {
1344 			/*
1345 			 * not found OR not matching euid
1346 			 */
1347 			*tpp = tp;
1348 			return (0);
1349 		}
1350 	}
1351 	*tpp = tp;
1352 	return (1);
1353 }
1354 
1355 /*
1356  * Print info about the process.
1357  */
1358 static void
1359 prcom(psinfo_t *psinfo, char *ttyp)
1360 {
1361 	char	*cp;
1362 	long	tm;
1363 	int	bytesleft;
1364 	int	wcnt, length;
1365 	wchar_t	wchar;
1366 	struct passwd *pwd;
1367 	int	zombie_lwp;
1368 	char	zonename[ZONENAME_MAX];
1369 
1370 	/*
1371 	 * If process is zombie, call zombie print routine and return.
1372 	 */
1373 	if (psinfo->pr_nlwp == 0) {
1374 		if (fields != NULL)
1375 			pr_fields(psinfo, ttyp, print_zombie_field);
1376 		else
1377 			przom(psinfo);
1378 		return;
1379 	}
1380 
1381 	zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z');
1382 
1383 	/*
1384 	 * If user specified '-o format', print requested fields and return.
1385 	 */
1386 	if (fields != NULL) {
1387 		pr_fields(psinfo, ttyp, print_field);
1388 		return;
1389 	}
1390 
1391 	/*
1392 	 * All fields before 'PID' are printed with a trailing space as a
1393 	 * separator, rather than keeping track of which column is first.  All
1394 	 * other fields are printed with a leading space.
1395 	 */
1396 	if (lflg) {
1397 		if (!yflg)
1398 			(void) printf("%2x ", psinfo->pr_flag & 0377); /* F */
1399 		(void) printf("%c ", psinfo->pr_lwp.pr_sname);	/* S */
1400 	}
1401 
1402 	if (Zflg) {						/* ZONE */
1403 		if (getzonenamebyid(psinfo->pr_zoneid, zonename,
1404 		    sizeof (zonename)) < 0) {
1405 			if (snprintf(NULL, 0, "%d",
1406 			    ((int)psinfo->pr_zoneid)) > 7)
1407 				(void) printf(" %6.6d%c ",
1408 				    ((int)psinfo->pr_zoneid), '*');
1409 			else
1410 				(void) printf(" %7.7d ",
1411 				    ((int)psinfo->pr_zoneid));
1412 		} else {
1413 			size_t nw;
1414 
1415 			nw = mbstowcs(NULL, zonename, 0);
1416 			if (nw == (size_t)-1)
1417 				(void) printf("%8.8s ", "ERROR");
1418 			else if (nw > 8)
1419 				(void) wprintf(L"%7.7s%c ", zonename, '*');
1420 			else
1421 				(void) wprintf(L"%8.8s ", zonename);
1422 		}
1423 	}
1424 
1425 	if (fflg) {						/* UID */
1426 		if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) {
1427 			size_t nw;
1428 
1429 			nw = mbstowcs(NULL, pwd->pw_name, 0);
1430 			if (nw == (size_t)-1)
1431 				(void) printf("%8.8s ", "ERROR");
1432 			else if (nw > 8)
1433 				(void) wprintf(L"%7.7s%c ", pwd->pw_name, '*');
1434 			else
1435 				(void) wprintf(L"%8.8s ", pwd->pw_name);
1436 		} else {
1437 			if (snprintf(NULL, 0, "%u",
1438 			    (psinfo->pr_euid)) > 7)
1439 				(void) printf(" %6.6u%c ", psinfo->pr_euid,
1440 				    '*');
1441 			else
1442 				(void) printf(" %7.7u ", psinfo->pr_euid);
1443 		}
1444 	} else if (lflg) {
1445 		if (snprintf(NULL, 0, "%u", (psinfo->pr_euid)) > 6)
1446 			(void) printf("%5.5u%c ", psinfo->pr_euid, '*');
1447 		else
1448 			(void) printf("%6u ", psinfo->pr_euid);
1449 	}
1450 	(void) printf("%*d", pidwidth, (int)psinfo->pr_pid); /* PID */
1451 	if (lflg || fflg)
1452 		(void) printf(" %*d", pidwidth,
1453 		    (int)psinfo->pr_ppid); /* PPID */
1454 	if (jflg) {
1455 		(void) printf(" %*d", pidwidth,
1456 		    (int)psinfo->pr_pgid);	/* PGID */
1457 		(void) printf(" %*d", pidwidth,
1458 		    (int)psinfo->pr_sid);	/* SID  */
1459 	}
1460 	if (Lflg)
1461 		(void) printf(" %5d", (int)psinfo->pr_lwp.pr_lwpid); /* LWP */
1462 	if (Pflg) {
1463 		if (psinfo->pr_lwp.pr_bindpro == PBIND_NONE)	/* PSR */
1464 			(void) printf("   -");
1465 		else
1466 			(void) printf(" %3d", psinfo->pr_lwp.pr_bindpro);
1467 	}
1468 	if (Lflg && fflg)					/* NLWP */
1469 		(void) printf(" %5d", psinfo->pr_nlwp + psinfo->pr_nzomb);
1470 	if (cflg) {
1471 		if (zombie_lwp)					/* CLS */
1472 			(void) printf("     ");
1473 		else
1474 			(void) printf(" %4s", psinfo->pr_lwp.pr_clname);
1475 		(void) printf(" %3d", psinfo->pr_lwp.pr_pri);	/* PRI */
1476 	} else if (lflg || fflg) {
1477 		(void) printf(" %3d", psinfo->pr_lwp.pr_cpu & 0377); /* C   */
1478 		if (lflg) {					    /* PRI NI */
1479 			/*
1480 			 * Print priorities the old way (lower numbers
1481 			 * mean higher priority) and print nice value
1482 			 * for time sharing procs.
1483 			 */
1484 			(void) printf(" %3d", psinfo->pr_lwp.pr_oldpri);
1485 			if (psinfo->pr_lwp.pr_oldpri != 0)
1486 				(void) printf(" %2d", psinfo->pr_lwp.pr_nice);
1487 			else
1488 				(void) printf(" %2.2s",
1489 				    psinfo->pr_lwp.pr_clname);
1490 		}
1491 	}
1492 	if (lflg) {
1493 		if (yflg) {
1494 			if (psinfo->pr_flag & SSYS)		/* RSS */
1495 				(void) printf("     0");
1496 			else if (psinfo->pr_rssize)
1497 				(void) printf(" %5lu",
1498 				    (ulong_t)psinfo->pr_rssize);
1499 			else
1500 				(void) printf("     ?");
1501 			if (psinfo->pr_flag & SSYS)		/* SZ */
1502 				(void) printf("      0");
1503 			else if (psinfo->pr_size)
1504 				(void) printf(" %6lu",
1505 				    (ulong_t)psinfo->pr_size);
1506 			else
1507 				(void) printf("      ?");
1508 		} else {
1509 #ifndef _LP64
1510 			if (psinfo->pr_addr)			/* ADDR */
1511 				(void) printf(" %8lx",
1512 				    (ulong_t)psinfo->pr_addr);
1513 			else
1514 #endif
1515 				(void) printf("        ?");
1516 			if (psinfo->pr_flag & SSYS)		/* SZ */
1517 				(void) printf("      0");
1518 			else if (psinfo->pr_size)
1519 				(void) printf(" %6lu",
1520 				    (ulong_t)psinfo->pr_size / kbytes_per_page);
1521 			else
1522 				(void) printf("      ?");
1523 		}
1524 		if (psinfo->pr_lwp.pr_sname != 'S')		/* WCHAN */
1525 			(void) printf("         ");
1526 #ifndef _LP64
1527 		else if (psinfo->pr_lwp.pr_wchan)
1528 			(void) printf(" %8lx",
1529 			    (ulong_t)psinfo->pr_lwp.pr_wchan);
1530 #endif
1531 		else
1532 			(void) printf("        ?");
1533 	}
1534 	if (fflg) {						/* STIME */
1535 		int width = fname[F_STIME].width;
1536 		if (Lflg)
1537 			prtime(psinfo->pr_lwp.pr_start, width + 1, 1);
1538 		else
1539 			prtime(psinfo->pr_start, width + 1, 1);
1540 	}
1541 
1542 	if (Hflg) {
1543 		/* Display home lgroup */
1544 		(void) printf(" %4d", (int)psinfo->pr_lwp.pr_lgrp);
1545 	}
1546 
1547 	(void) printf(" %-8.14s", ttyp);			/* TTY */
1548 	if (Lflg) {
1549 		tm = psinfo->pr_lwp.pr_time.tv_sec;
1550 		if (psinfo->pr_lwp.pr_time.tv_nsec > 500000000)
1551 			tm++;
1552 	} else {
1553 		tm = psinfo->pr_time.tv_sec;
1554 		if (psinfo->pr_time.tv_nsec > 500000000)
1555 			tm++;
1556 	}
1557 	(void) printf(" %4ld:%.2ld", tm / 60, tm % 60);		/* [L]TIME */
1558 
1559 	if (zombie_lwp) {
1560 		(void) printf(" <defunct>\n");
1561 		return;
1562 	}
1563 
1564 	if (!fflg) {						/* CMD */
1565 		wcnt = namencnt(psinfo->pr_fname, 16, 8);
1566 		(void) printf(" %.*s\n", wcnt, psinfo->pr_fname);
1567 		return;
1568 	}
1569 
1570 
1571 	/*
1572 	 * PRARGSZ == length of cmd arg string.
1573 	 */
1574 	psinfo->pr_psargs[PRARGSZ-1] = '\0';
1575 	bytesleft = PRARGSZ;
1576 	for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) {
1577 		length = mbtowc(&wchar, cp, MB_LEN_MAX);
1578 		if (length == 0)
1579 			break;
1580 		if (length < 0 || !iswprint(wchar)) {
1581 			if (length < 0)
1582 				length = 1;
1583 			if (bytesleft <= length) {
1584 				*cp = '\0';
1585 				break;
1586 			}
1587 			/* omit the unprintable character */
1588 			(void) memmove(cp, cp+length, bytesleft-length);
1589 			length = 0;
1590 		}
1591 		bytesleft -= length;
1592 	}
1593 	wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, lflg ? 35 : PRARGSZ);
1594 	(void) printf(" %.*s\n", wcnt, psinfo->pr_psargs);
1595 }
1596 
1597 /*
1598  * Print percent from 16-bit binary fraction [0 .. 1]
1599  * Round up .01 to .1 to indicate some small percentage (the 0x7000 below).
1600  */
1601 static void
1602 prtpct(ushort_t pct, int width)
1603 {
1604 	uint_t value = pct;	/* need 32 bits to compute with */
1605 
1606 	value = ((value * 1000) + 0x7000) >> 15;	/* [0 .. 1000] */
1607 	if (value >= 1000)
1608 		value = 999;
1609 	if ((width -= 2) < 2)
1610 		width = 2;
1611 	(void) printf("%*u.%u", width, value / 10, value % 10);
1612 }
1613 
1614 static void
1615 print_time(time_t tim, int width)
1616 {
1617 	char buf[30];
1618 	time_t seconds;
1619 	time_t minutes;
1620 	time_t hours;
1621 	time_t days;
1622 
1623 	if (tim < 0) {
1624 		(void) printf("%*s", width, "-");
1625 		return;
1626 	}
1627 
1628 	seconds = tim % 60;
1629 	tim /= 60;
1630 	minutes = tim % 60;
1631 	tim /= 60;
1632 	hours = tim % 24;
1633 	days = tim / 24;
1634 
1635 	if (days > 0) {
1636 		(void) snprintf(buf, sizeof (buf), "%ld-%2.2ld:%2.2ld:%2.2ld",
1637 		    days, hours, minutes, seconds);
1638 	} else if (hours > 0) {
1639 		(void) snprintf(buf, sizeof (buf), "%2.2ld:%2.2ld:%2.2ld",
1640 		    hours, minutes, seconds);
1641 	} else {
1642 		(void) snprintf(buf, sizeof (buf), "%2.2ld:%2.2ld",
1643 		    minutes, seconds);
1644 	}
1645 
1646 	(void) printf("%*s", width, buf);
1647 }
1648 
1649 static void
1650 print_field(psinfo_t *psinfo, struct field *f, const char *ttyp)
1651 {
1652 	int width = f->width;
1653 	struct passwd *pwd;
1654 	struct group *grp;
1655 	time_t cputime;
1656 	int bytesleft;
1657 	int wcnt;
1658 	wchar_t	wchar;
1659 	char *cp;
1660 	int length;
1661 	ulong_t mask;
1662 	char c = '\0', *csave = NULL;
1663 	int zombie_lwp;
1664 
1665 	zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z');
1666 
1667 	switch (f->fname) {
1668 	case F_RUSER:
1669 		if ((pwd = getpwuid(psinfo->pr_uid)) != NULL) {
1670 			size_t nw;
1671 
1672 			nw = mbstowcs(NULL, pwd->pw_name, 0);
1673 			if (nw == (size_t)-1)
1674 				(void) printf("%*s ", width, "ERROR");
1675 			else if (Wflg && nw > width)
1676 				(void) wprintf(L"%.*s%c", width - 1,
1677 				    pwd->pw_name, '*');
1678 			else
1679 				(void) wprintf(L"%*s", width, pwd->pw_name);
1680 		} else {
1681 			if (Wflg && snprintf(NULL, 0, "%u",
1682 			    (psinfo->pr_uid)) > width)
1683 
1684 				(void) printf("%*u%c", width - 1,
1685 				    psinfo->pr_uid, '*');
1686 			else
1687 				(void) printf("%*u", width, psinfo->pr_uid);
1688 		}
1689 		break;
1690 	case F_USER:
1691 		if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) {
1692 			size_t nw;
1693 
1694 			nw = mbstowcs(NULL, pwd->pw_name, 0);
1695 			if (nw == (size_t)-1)
1696 				(void) printf("%*s ", width, "ERROR");
1697 			else if (Wflg && nw > width)
1698 				(void) wprintf(L"%.*s%c", width - 1,
1699 				    pwd->pw_name, '*');
1700 			else
1701 				(void) wprintf(L"%*s", width, pwd->pw_name);
1702 		} else {
1703 			if (Wflg && snprintf(NULL, 0, "%u",
1704 			    (psinfo->pr_euid)) > width)
1705 
1706 				(void) printf("%*u%c", width - 1,
1707 				    psinfo->pr_euid, '*');
1708 			else
1709 				(void) printf("%*u", width, psinfo->pr_euid);
1710 		}
1711 		break;
1712 	case F_RGROUP:
1713 		if ((grp = getgrgid(psinfo->pr_gid)) != NULL)
1714 			(void) printf("%*s", width, grp->gr_name);
1715 		else
1716 			(void) printf("%*u", width, psinfo->pr_gid);
1717 		break;
1718 	case F_GROUP:
1719 		if ((grp = getgrgid(psinfo->pr_egid)) != NULL)
1720 			(void) printf("%*s", width, grp->gr_name);
1721 		else
1722 			(void) printf("%*u", width, psinfo->pr_egid);
1723 		break;
1724 	case F_RUID:
1725 		(void) printf("%*u", width, psinfo->pr_uid);
1726 		break;
1727 	case F_UID:
1728 		(void) printf("%*u", width, psinfo->pr_euid);
1729 		break;
1730 	case F_RGID:
1731 		(void) printf("%*u", width, psinfo->pr_gid);
1732 		break;
1733 	case F_GID:
1734 		(void) printf("%*u", width, psinfo->pr_egid);
1735 		break;
1736 	case F_PID:
1737 		(void) printf("%*d", width, (int)psinfo->pr_pid);
1738 		break;
1739 	case F_PPID:
1740 		(void) printf("%*d", width, (int)psinfo->pr_ppid);
1741 		break;
1742 	case F_PGID:
1743 		(void) printf("%*d", width, (int)psinfo->pr_pgid);
1744 		break;
1745 	case F_SID:
1746 		(void) printf("%*d", width, (int)psinfo->pr_sid);
1747 		break;
1748 	case F_PSR:
1749 		if (zombie_lwp || psinfo->pr_lwp.pr_bindpro == PBIND_NONE)
1750 			(void) printf("%*s", width, "-");
1751 		else
1752 			(void) printf("%*d", width, psinfo->pr_lwp.pr_bindpro);
1753 		break;
1754 	case F_LWP:
1755 		(void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lwpid);
1756 		break;
1757 	case F_LWPNAME: {
1758 		char lwpname[THREAD_NAME_MAX] = "";
1759 		char *path = NULL;
1760 		int fd;
1761 
1762 		if (asprintf(&path, "%s/%d/lwp/%d/lwpname", procdir,
1763 		    (int)psinfo->pr_pid, (int)psinfo->pr_lwp.pr_lwpid) != -1 &&
1764 		    (fd = open(path, O_RDONLY)) != -1) {
1765 			(void) read(fd, lwpname, sizeof (lwpname));
1766 			lwpname[THREAD_NAME_MAX - 1] = '\0';
1767 			(void) close(fd);
1768 		}
1769 
1770 		free(path);
1771 
1772 		if (f->next != NULL)
1773 			(void) printf("%-*s", width, lwpname);
1774 		else
1775 			(void) printf("%s", lwpname);
1776 		break;
1777 	}
1778 	case F_NLWP:
1779 		(void) printf("%*d", width, psinfo->pr_nlwp + psinfo->pr_nzomb);
1780 		break;
1781 	case F_OPRI:
1782 		if (zombie_lwp)
1783 			(void) printf("%*s", width, "-");
1784 		else
1785 			(void) printf("%*d", width, psinfo->pr_lwp.pr_oldpri);
1786 		break;
1787 	case F_PRI:
1788 		if (zombie_lwp)
1789 			(void) printf("%*s", width, "-");
1790 		else
1791 			(void) printf("%*d", width, psinfo->pr_lwp.pr_pri);
1792 		break;
1793 	case F_F:
1794 		mask = 0xffffffffUL;
1795 		if (width < 8)
1796 			mask >>= (8 - width) * 4;
1797 		(void) printf("%*lx", width, psinfo->pr_flag & mask);
1798 		break;
1799 	case F_S:
1800 		(void) printf("%*c", width, psinfo->pr_lwp.pr_sname);
1801 		break;
1802 	case F_C:
1803 		if (zombie_lwp)
1804 			(void) printf("%*s", width, "-");
1805 		else
1806 			(void) printf("%*d", width, psinfo->pr_lwp.pr_cpu);
1807 		break;
1808 	case F_PCPU:
1809 		if (zombie_lwp)
1810 			(void) printf("%*s", width, "-");
1811 		else if (Lflg)
1812 			prtpct(psinfo->pr_lwp.pr_pctcpu, width);
1813 		else
1814 			prtpct(psinfo->pr_pctcpu, width);
1815 		break;
1816 	case F_PMEM:
1817 		prtpct(psinfo->pr_pctmem, width);
1818 		break;
1819 	case F_OSZ:
1820 		(void) printf("%*lu", width,
1821 		    (ulong_t)psinfo->pr_size / kbytes_per_page);
1822 		break;
1823 	case F_VSZ:
1824 		(void) printf("%*lu", width, (ulong_t)psinfo->pr_size);
1825 		break;
1826 	case F_RSS:
1827 		(void) printf("%*lu", width, (ulong_t)psinfo->pr_rssize);
1828 		break;
1829 	case F_NICE:
1830 		/* if pr_oldpri is zero, then this class has no nice */
1831 		if (zombie_lwp)
1832 			(void) printf("%*s", width, "-");
1833 		else if (psinfo->pr_lwp.pr_oldpri != 0)
1834 			(void) printf("%*d", width, psinfo->pr_lwp.pr_nice);
1835 		else
1836 			(void) printf("%*.*s", width, width,
1837 			    psinfo->pr_lwp.pr_clname);
1838 		break;
1839 	case F_CLASS:
1840 		if (zombie_lwp)
1841 			(void) printf("%*s", width, "-");
1842 		else
1843 			(void) printf("%*.*s", width, width,
1844 			    psinfo->pr_lwp.pr_clname);
1845 		break;
1846 	case F_STIME:
1847 		if (Lflg)
1848 			prtime(psinfo->pr_lwp.pr_start, width, 0);
1849 		else
1850 			prtime(psinfo->pr_start, width, 0);
1851 		break;
1852 	case F_ETIME:
1853 		if (Lflg)
1854 			print_time(delta_secs(&psinfo->pr_lwp.pr_start),
1855 			    width);
1856 		else
1857 			print_time(delta_secs(&psinfo->pr_start), width);
1858 		break;
1859 	case F_TIME:
1860 		if (Lflg) {
1861 			cputime = psinfo->pr_lwp.pr_time.tv_sec;
1862 			if (psinfo->pr_lwp.pr_time.tv_nsec > 500000000)
1863 				cputime++;
1864 		} else {
1865 			cputime = psinfo->pr_time.tv_sec;
1866 			if (psinfo->pr_time.tv_nsec > 500000000)
1867 				cputime++;
1868 		}
1869 		print_time(cputime, width);
1870 		break;
1871 	case F_TTY:
1872 		(void) printf("%-*s", width, ttyp);
1873 		break;
1874 	case F_ADDR:
1875 		if (zombie_lwp)
1876 			(void) printf("%*s", width, "-");
1877 		else if (Lflg)
1878 			(void) printf("%*lx", width,
1879 			    (long)psinfo->pr_lwp.pr_addr);
1880 		else
1881 			(void) printf("%*lx", width, (long)psinfo->pr_addr);
1882 		break;
1883 	case F_WCHAN:
1884 		if (!zombie_lwp && psinfo->pr_lwp.pr_wchan)
1885 			(void) printf("%*lx", width,
1886 			    (long)psinfo->pr_lwp.pr_wchan);
1887 		else
1888 			(void) printf("%*.*s", width, width, "-");
1889 		break;
1890 	case F_FNAME:
1891 		/*
1892 		 * Print full width unless this is the last output format.
1893 		 */
1894 		if (zombie_lwp) {
1895 			if (f->next != NULL)
1896 				(void) printf("%-*s", width, "<defunct>");
1897 			else
1898 				(void) printf("%s", "<defunct>");
1899 			break;
1900 		}
1901 		wcnt = namencnt(psinfo->pr_fname, 16, width);
1902 		if (f->next != NULL)
1903 			(void) printf("%-*.*s", width, wcnt, psinfo->pr_fname);
1904 		else
1905 			(void) printf("%-.*s", wcnt, psinfo->pr_fname);
1906 		break;
1907 	case F_COMM:
1908 		if (zombie_lwp) {
1909 			if (f->next != NULL)
1910 				(void) printf("%-*s", width, "<defunct>");
1911 			else
1912 				(void) printf("%s", "<defunct>");
1913 			break;
1914 		}
1915 		csave = strpbrk(psinfo->pr_psargs, " \t\r\v\f\n");
1916 		if (csave) {
1917 			c = *csave;
1918 			*csave = '\0';
1919 		}
1920 		/* FALLTHROUGH */
1921 	case F_ARGS:
1922 		/*
1923 		 * PRARGSZ == length of cmd arg string.
1924 		 */
1925 		if (zombie_lwp) {
1926 			(void) printf("%-*s", width, "<defunct>");
1927 			break;
1928 		}
1929 		psinfo->pr_psargs[PRARGSZ-1] = '\0';
1930 		bytesleft = PRARGSZ;
1931 		for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) {
1932 			length = mbtowc(&wchar, cp, MB_LEN_MAX);
1933 			if (length == 0)
1934 				break;
1935 			if (length < 0 || !iswprint(wchar)) {
1936 				if (length < 0)
1937 					length = 1;
1938 				if (bytesleft <= length) {
1939 					*cp = '\0';
1940 					break;
1941 				}
1942 				/* omit the unprintable character */
1943 				(void) memmove(cp, cp+length, bytesleft-length);
1944 				length = 0;
1945 			}
1946 			bytesleft -= length;
1947 		}
1948 		wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, width);
1949 		/*
1950 		 * Print full width unless this is the last format.
1951 		 */
1952 		if (f->next != NULL)
1953 			(void) printf("%-*.*s", width, wcnt,
1954 			    psinfo->pr_psargs);
1955 		else
1956 			(void) printf("%-.*s", wcnt,
1957 			    psinfo->pr_psargs);
1958 		if (f->fname == F_COMM && csave)
1959 			*csave = c;
1960 		break;
1961 	case F_TASKID:
1962 		(void) printf("%*d", width, (int)psinfo->pr_taskid);
1963 		break;
1964 	case F_PROJID:
1965 		(void) printf("%*d", width, (int)psinfo->pr_projid);
1966 		break;
1967 	case F_PROJECT:
1968 		{
1969 			struct project cproj;
1970 			char proj_buf[PROJECT_BUFSZ];
1971 
1972 			if ((getprojbyid(psinfo->pr_projid, &cproj,
1973 			    (void *)&proj_buf, PROJECT_BUFSZ)) == NULL) {
1974 				if (Wflg && snprintf(NULL, 0, "%d",
1975 				    ((int)psinfo->pr_projid)) > width)
1976 					(void) printf("%.*d%c", width - 1,
1977 					    ((int)psinfo->pr_projid), '*');
1978 				else
1979 					(void) printf("%*d", width,
1980 					    (int)psinfo->pr_projid);
1981 			} else {
1982 				size_t nw;
1983 
1984 				if (cproj.pj_name != NULL)
1985 					nw = mbstowcs(NULL, cproj.pj_name, 0);
1986 				if (cproj.pj_name == NULL)
1987 					(void) printf("%*s ", width, "---");
1988 				else if (nw == (size_t)-1)
1989 					(void) printf("%*s ", width, "ERROR");
1990 				else if (Wflg && nw > width)
1991 					(void) wprintf(L"%.*s%c", width - 1,
1992 					    cproj.pj_name, '*');
1993 				else
1994 					(void) wprintf(L"%*s", width,
1995 					    cproj.pj_name);
1996 			}
1997 		}
1998 		break;
1999 	case F_PSET:
2000 		if (zombie_lwp || psinfo->pr_lwp.pr_bindpset == PS_NONE)
2001 			(void) printf("%*s", width, "-");
2002 		else
2003 			(void) printf("%*d", width, psinfo->pr_lwp.pr_bindpset);
2004 		break;
2005 	case F_ZONEID:
2006 		(void) printf("%*d", width, (int)psinfo->pr_zoneid);
2007 		break;
2008 	case F_ZONE:
2009 		{
2010 			char zonename[ZONENAME_MAX];
2011 
2012 			if (getzonenamebyid(psinfo->pr_zoneid, zonename,
2013 			    sizeof (zonename)) < 0) {
2014 				if (Wflg && snprintf(NULL, 0, "%d",
2015 				    ((int)psinfo->pr_zoneid)) > width)
2016 					(void) printf("%.*d%c", width - 1,
2017 					    ((int)psinfo->pr_zoneid), '*');
2018 				else
2019 					(void) printf("%*d", width,
2020 					    (int)psinfo->pr_zoneid);
2021 			} else {
2022 				size_t nw;
2023 
2024 				nw = mbstowcs(NULL, zonename, 0);
2025 				if (nw == (size_t)-1)
2026 					(void) printf("%*s ", width, "ERROR");
2027 				else if (Wflg && nw > width)
2028 					(void) wprintf(L"%.*s%c", width - 1,
2029 					    zonename, '*');
2030 				else
2031 					(void) wprintf(L"%*s", width, zonename);
2032 			}
2033 		}
2034 		break;
2035 	case F_CTID:
2036 		if (psinfo->pr_contract == -1)
2037 			(void) printf("%*s", width, "-");
2038 		else
2039 			(void) printf("%*ld", width, (long)psinfo->pr_contract);
2040 		break;
2041 	case F_LGRP:
2042 		/* Display home lgroup */
2043 		(void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lgrp);
2044 		break;
2045 
2046 	case F_DMODEL:
2047 		(void) printf("%*s", width,
2048 		    psinfo->pr_dmodel == PR_MODEL_LP64 ? "_LP64" : "_ILP32");
2049 		break;
2050 	}
2051 }
2052 
2053 static void
2054 print_zombie_field(psinfo_t *psinfo, struct field *f, const char *ttyp)
2055 {
2056 	int wcnt;
2057 	int width = f->width;
2058 
2059 	switch (f->fname) {
2060 	case F_FNAME:
2061 	case F_COMM:
2062 	case F_ARGS:
2063 		/*
2064 		 * Print full width unless this is the last output format.
2065 		 */
2066 		wcnt = min(width, sizeof ("<defunct>"));
2067 		if (f->next != NULL)
2068 			(void) printf("%-*.*s", width, wcnt, "<defunct>");
2069 		else
2070 			(void) printf("%-.*s", wcnt, "<defunct>");
2071 		break;
2072 
2073 	case F_PSR:
2074 	case F_PCPU:
2075 	case F_PMEM:
2076 	case F_NICE:
2077 	case F_CLASS:
2078 	case F_STIME:
2079 	case F_ETIME:
2080 	case F_WCHAN:
2081 	case F_PSET:
2082 		(void) printf("%*s", width, "-");
2083 		break;
2084 
2085 	case F_OPRI:
2086 	case F_PRI:
2087 	case F_OSZ:
2088 	case F_VSZ:
2089 	case F_RSS:
2090 		(void) printf("%*d", width, 0);
2091 		break;
2092 
2093 	default:
2094 		print_field(psinfo, f, ttyp);
2095 		break;
2096 	}
2097 }
2098 
2099 static void
2100 pr_fields(psinfo_t *psinfo, const char *ttyp,
2101     void (*print_fld)(psinfo_t *, struct field *, const char *))
2102 {
2103 	struct field *f;
2104 
2105 	for (f = fields; f != NULL; f = f->next) {
2106 		print_fld(psinfo, f, ttyp);
2107 		if (f->next != NULL)
2108 			(void) printf(" ");
2109 	}
2110 	(void) printf("\n");
2111 }
2112 
2113 /*
2114  * Returns 1 if arg is found in array arr, of length num; 0 otherwise.
2115  */
2116 static int
2117 search(pid_t *arr, int number, pid_t arg)
2118 {
2119 	int i;
2120 
2121 	for (i = 0; i < number; i++)
2122 		if (arg == arr[i])
2123 			return (1);
2124 	return (0);
2125 }
2126 
2127 /*
2128  * Add an entry (user, group) to the specified table.
2129  */
2130 static void
2131 add_ugentry(struct ughead *tbl, char *name)
2132 {
2133 	struct ugdata *entp;
2134 
2135 	if (tbl->size == tbl->nent) {	/* reallocate the table entries */
2136 		if ((tbl->size *= 2) == 0)
2137 			tbl->size = 32;		/* first time */
2138 		tbl->ent = Realloc(tbl->ent, tbl->size*sizeof (struct ugdata));
2139 	}
2140 	entp = &tbl->ent[tbl->nent++];
2141 	entp->id = 0;
2142 	(void) strncpy(entp->name, name, MAXUGNAME);
2143 	entp->name[MAXUGNAME] = '\0';
2144 }
2145 
2146 static int
2147 uconv(struct ughead *uhead)
2148 {
2149 	struct ugdata *utbl = uhead->ent;
2150 	int n = uhead->nent;
2151 	struct passwd *pwd;
2152 	int i;
2153 	int fnd = 0;
2154 	uid_t uid;
2155 
2156 	/*
2157 	 * Ask the name service for names.
2158 	 */
2159 	for (i = 0; i < n; i++) {
2160 		/*
2161 		 * If name is numeric, ask for numeric id
2162 		 */
2163 		if (str2uid(utbl[i].name, &uid, 0, MAXEPHUID) == 0)
2164 			pwd = getpwuid(uid);
2165 		else
2166 			pwd = getpwnam(utbl[i].name);
2167 
2168 		/*
2169 		 * If found, enter found index into tbl array.
2170 		 */
2171 		if (pwd == NULL) {
2172 			(void) fprintf(stderr,
2173 			    gettext("ps: unknown user %s\n"), utbl[i].name);
2174 			continue;
2175 		}
2176 
2177 		utbl[fnd].id = pwd->pw_uid;
2178 		(void) strncpy(utbl[fnd].name, pwd->pw_name, MAXUGNAME);
2179 		fnd++;
2180 	}
2181 
2182 	uhead->nent = fnd;	/* in case it changed */
2183 	return (n - fnd);
2184 }
2185 
2186 static int
2187 gconv(struct ughead *ghead)
2188 {
2189 	struct ugdata *gtbl = ghead->ent;
2190 	int n = ghead->nent;
2191 	struct group *grp;
2192 	gid_t gid;
2193 	int i;
2194 	int fnd = 0;
2195 
2196 	/*
2197 	 * Ask the name service for names.
2198 	 */
2199 	for (i = 0; i < n; i++) {
2200 		/*
2201 		 * If name is numeric, ask for numeric id
2202 		 */
2203 		if (str2uid(gtbl[i].name, (uid_t *)&gid, 0, MAXEPHUID) == 0)
2204 			grp = getgrgid(gid);
2205 		else
2206 			grp = getgrnam(gtbl[i].name);
2207 		/*
2208 		 * If found, enter found index into tbl array.
2209 		 */
2210 		if (grp == NULL) {
2211 			(void) fprintf(stderr,
2212 			    gettext("ps: unknown group %s\n"), gtbl[i].name);
2213 			continue;
2214 		}
2215 
2216 		gtbl[fnd].id = grp->gr_gid;
2217 		(void) strncpy(gtbl[fnd].name, grp->gr_name, MAXUGNAME);
2218 		fnd++;
2219 	}
2220 
2221 	ghead->nent = fnd;	/* in case it changed */
2222 	return (n - fnd);
2223 }
2224 
2225 /*
2226  * Return 1 if puid is in table, otherwise 0.
2227  */
2228 static int
2229 ugfind(id_t id, struct ughead *ughead)
2230 {
2231 	struct ugdata *utbl = ughead->ent;
2232 	int n = ughead->nent;
2233 	int i;
2234 
2235 	for (i = 0; i < n; i++)
2236 		if (utbl[i].id == id)
2237 			return (1);
2238 	return (0);
2239 }
2240 
2241 /*
2242  * Print starting time of process unless process started more than 24 hours
2243  * ago, in which case the date is printed.  The date is printed in the form
2244  * "MMM dd" if old format, else the blank is replaced with an '_' so
2245  * it appears as a single word (for parseability).
2246  */
2247 static void
2248 prtime(timestruc_t st, int width, int old)
2249 {
2250 	char sttim[26];
2251 	time_t starttime;
2252 
2253 	starttime = st.tv_sec;
2254 	if (st.tv_nsec > 500000000)
2255 		starttime++;
2256 	if ((now.tv_sec - starttime) >= 24*60*60) {
2257 		(void) strftime(sttim, sizeof (sttim), old?
2258 		/*
2259 		 * TRANSLATION_NOTE
2260 		 * This time format is used by STIME field when -f option
2261 		 * is specified.  Used for processes that begun more than
2262 		 * 24 hours.
2263 		 */
2264 		    dcgettext(NULL, "%b %d", LC_TIME) :
2265 		/*
2266 		 * TRANSLATION_NOTE
2267 		 * This time format is used by STIME field when -o option
2268 		 * is specified.  Used for processes that begun more than
2269 		 * 24 hours.
2270 		 */
2271 		    dcgettext(NULL, "%b_%d", LC_TIME), localtime(&starttime));
2272 	} else {
2273 		/*
2274 		 * TRANSLATION_NOTE
2275 		 * This time format is used by STIME field when -f or -o option
2276 		 * is specified.  Used for processes that begun less than
2277 		 * 24 hours.
2278 		 */
2279 		(void) strftime(sttim, sizeof (sttim),
2280 		    dcgettext(NULL, "%H:%M:%S", LC_TIME),
2281 		    localtime(&starttime));
2282 	}
2283 	(void) printf("%*.*s", width, width, sttim);
2284 }
2285 
2286 static void
2287 przom(psinfo_t *psinfo)
2288 {
2289 	long	tm;
2290 	struct passwd *pwd;
2291 	char zonename[ZONENAME_MAX];
2292 
2293 	/*
2294 	 * All fields before 'PID' are printed with a trailing space as a
2295 	 * spearator, rather than keeping track of which column is first.  All
2296 	 * other fields are printed with a leading space.
2297 	 */
2298 	if (lflg) {	/* F S */
2299 		if (!yflg)
2300 			(void) printf("%2x ", psinfo->pr_flag & 0377); /* F */
2301 		(void) printf("%c ", psinfo->pr_lwp.pr_sname);	/* S */
2302 	}
2303 	if (Zflg) {
2304 		if (getzonenamebyid(psinfo->pr_zoneid, zonename,
2305 		    sizeof (zonename)) < 0) {
2306 			if (snprintf(NULL, 0, "%d",
2307 			    ((int)psinfo->pr_zoneid)) > 7)
2308 				(void) printf(" %6.6d%c ",
2309 				    ((int)psinfo->pr_zoneid), '*');
2310 			else
2311 				(void) printf(" %7.7d ",
2312 				    ((int)psinfo->pr_zoneid));
2313 		} else {
2314 			size_t nw;
2315 
2316 			nw = mbstowcs(NULL, zonename, 0);
2317 			if (nw == (size_t)-1)
2318 				(void) printf("%8.8s ", "ERROR");
2319 			else if (nw > 8)
2320 				(void) wprintf(L"%7.7s%c ", zonename, '*');
2321 			else
2322 				(void) wprintf(L"%8.8s ", zonename);
2323 		}
2324 	}
2325 	if (Hflg) {
2326 		/* Display home lgroup */
2327 		(void) printf(" %6d", (int)psinfo->pr_lwp.pr_lgrp); /* LGRP */
2328 	}
2329 	if (fflg) {
2330 		if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) {
2331 			size_t nw;
2332 
2333 			nw = mbstowcs(NULL, pwd->pw_name, 0);
2334 			if (nw == (size_t)-1)
2335 				(void) printf("%8.8s ", "ERROR");
2336 			else if (nw > 8)
2337 				(void) wprintf(L"%7.7s%c ", pwd->pw_name, '*');
2338 			else
2339 				(void) wprintf(L"%8.8s ", pwd->pw_name);
2340 		} else {
2341 			if (snprintf(NULL, 0, "%u",
2342 			    (psinfo->pr_euid)) > 7)
2343 				(void) printf(" %6.6u%c ", psinfo->pr_euid,
2344 				    '*');
2345 			else
2346 				(void) printf(" %7.7u ", psinfo->pr_euid);
2347 		}
2348 	} else if (lflg) {
2349 		if (snprintf(NULL, 0, "%u", (psinfo->pr_euid)) > 6)
2350 			(void) printf("%5.5u%c ", psinfo->pr_euid, '*');
2351 		else
2352 			(void) printf("%6u ", psinfo->pr_euid);
2353 	}
2354 
2355 	(void) printf("%*d", pidwidth, (int)psinfo->pr_pid);	/* PID */
2356 	if (lflg || fflg)
2357 		(void) printf(" %*d", pidwidth,
2358 		    (int)psinfo->pr_ppid);			/* PPID */
2359 
2360 	if (jflg) {
2361 		(void) printf(" %*d", pidwidth,
2362 		    (int)psinfo->pr_pgid);			/* PGID */
2363 		(void) printf(" %*d", pidwidth,
2364 		    (int)psinfo->pr_sid);			/* SID  */
2365 	}
2366 
2367 	if (Lflg)
2368 		(void) printf(" %5d", 0);			/* LWP */
2369 	if (Pflg)
2370 		(void) printf("   -");				/* PSR */
2371 	if (Lflg && fflg)
2372 		(void) printf(" %5d", 0);			/* NLWP */
2373 
2374 	if (cflg) {
2375 		(void) printf(" %4s", "-");	/* zombies have no class */
2376 		(void) printf(" %3d", psinfo->pr_lwp.pr_pri);	/* PRI	*/
2377 	} else if (lflg || fflg) {
2378 		(void) printf(" %3d", psinfo->pr_lwp.pr_cpu & 0377); /* C   */
2379 		if (lflg)
2380 			(void) printf(" %3d %2s",
2381 			    psinfo->pr_lwp.pr_oldpri, "-");	/* PRI NI */
2382 	}
2383 	if (lflg) {
2384 		if (yflg)				/* RSS SZ WCHAN */
2385 			(void) printf(" %5d %6d %8s", 0, 0, "-");
2386 		else					/* ADDR SZ WCHAN */
2387 			(void) printf(" %8s %6d %8s", "-", 0, "-");
2388 	}
2389 	if (fflg) {
2390 		int width = fname[F_STIME].width;
2391 		(void) printf(" %*.*s", width, width, "-");	/* STIME */
2392 	}
2393 	(void) printf(" %-8.14s", "?");				/* TTY */
2394 
2395 	tm = psinfo->pr_time.tv_sec;
2396 	if (psinfo->pr_time.tv_nsec > 500000000)
2397 		tm++;
2398 	(void) printf(" %4ld:%.2ld", tm / 60, tm % 60);	/* TIME */
2399 	(void) printf(" <defunct>\n");
2400 }
2401 
2402 /*
2403  * Function to compute the number of printable bytes in a multibyte
2404  * command string ("internationalization").
2405  */
2406 static int
2407 namencnt(char *cmd, int csisize, int scrsize)
2408 {
2409 	int csiwcnt = 0, scrwcnt = 0;
2410 	int ncsisz, nscrsz;
2411 	wchar_t  wchar;
2412 	int	 len;
2413 
2414 	while (*cmd != '\0') {
2415 		if ((len = csisize - csiwcnt) > (int)MB_CUR_MAX)
2416 			len = MB_CUR_MAX;
2417 		if ((ncsisz = mbtowc(&wchar, cmd, len)) < 0)
2418 			return (8); /* default to use for illegal chars */
2419 		if ((nscrsz = wcwidth(wchar)) <= 0)
2420 			return (8);
2421 		if (csiwcnt + ncsisz > csisize || scrwcnt + nscrsz > scrsize)
2422 			break;
2423 		csiwcnt += ncsisz;
2424 		scrwcnt += nscrsz;
2425 		cmd += ncsisz;
2426 	}
2427 	return (csiwcnt);
2428 }
2429 
2430 static char *
2431 err_string(int err)
2432 {
2433 	static char buf[32];
2434 	char *str = strerror(err);
2435 
2436 	if (str == NULL)
2437 		(void) snprintf(str = buf, sizeof (buf), "Errno #%d", err);
2438 
2439 	return (str);
2440 }
2441 
2442 /* If allocation fails, die */
2443 static void *
2444 Realloc(void *ptr, size_t size)
2445 {
2446 	ptr = realloc(ptr, size);
2447 	if (ptr == NULL) {
2448 		(void) fprintf(stderr, gettext("ps: no memory\n"));
2449 		exit(1);
2450 	}
2451 	return (ptr);
2452 }
2453 
2454 static time_t
2455 delta_secs(const timestruc_t *start)
2456 {
2457 	time_t seconds = now.tv_sec - start->tv_sec;
2458 	long nanosecs = now.tv_usec * 1000 - start->tv_nsec;
2459 
2460 	if (nanosecs >= (NANOSEC / 2))
2461 		seconds++;
2462 	else if (nanosecs < -(NANOSEC / 2))
2463 		seconds--;
2464 
2465 	return (seconds);
2466 }
2467 
2468 /*
2469  * Returns the following:
2470  *
2471  *	0	No error
2472  *	EINVAL	Invalid number
2473  *	ERANGE	Value exceeds (min, max) range
2474  */
2475 static int
2476 str2id(const char *p, pid_t *val, long min, long max)
2477 {
2478 	char *q;
2479 	long number;
2480 	int error;
2481 
2482 	errno = 0;
2483 	number = strtol(p, &q, 10);
2484 
2485 	if (errno != 0 || q == p || *q != '\0') {
2486 		if ((error = errno) == 0) {
2487 			/*
2488 			 * strtol() can fail without setting errno, or it can
2489 			 * set it to EINVAL or ERANGE.  In the case errno is
2490 			 * still zero, return EINVAL.
2491 			 */
2492 			error = EINVAL;
2493 		}
2494 	} else if (number < min || number > max) {
2495 		error = ERANGE;
2496 	} else {
2497 		error = 0;
2498 	}
2499 
2500 	*val = number;
2501 
2502 	return (error);
2503 }
2504 
2505 /*
2506  * Returns the following:
2507  *
2508  *	0	No error
2509  *	EINVAL	Invalid number
2510  *	ERANGE	Value exceeds (min, max) range
2511  */
2512 static int
2513 str2uid(const char *p, uid_t *val, unsigned long min, unsigned long max)
2514 {
2515 	char *q;
2516 	unsigned long number;
2517 	int error;
2518 
2519 	errno = 0;
2520 	number = strtoul(p, &q, 10);
2521 
2522 	if (errno != 0 || q == p || *q != '\0') {
2523 		if ((error = errno) == 0) {
2524 			/*
2525 			 * strtoul() can fail without setting errno, or it can
2526 			 * set it to EINVAL or ERANGE.  In the case errno is
2527 			 * still zero, return EINVAL.
2528 			 */
2529 			error = EINVAL;
2530 		}
2531 	} else if (number < min || number > max) {
2532 		error = ERANGE;
2533 	} else {
2534 		error = 0;
2535 	}
2536 
2537 	*val = number;
2538 
2539 	return (error);
2540 }
2541 
2542 static int
2543 pidcmp(const void *p1, const void *p2)
2544 {
2545 	pid_t i = *((pid_t *)p1);
2546 	pid_t j = *((pid_t *)p2);
2547 
2548 	return (i - j);
2549 }
2550