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