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