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