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