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