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