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