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