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