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