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]
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.
46 #include <sys/types.h>
48 #include <sys/mkdev.h>
53 #include <sys/signal.h>
54 #include <sys/fault.h>
55 #include <sys/syscall.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 */
86 id_t id
; /* numeric user-id or group-id */
87 char name
[MAXUGNAME
+1]; /* user/group name, null terminated */
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 */
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 */
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 */
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 },
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 },
198 { "addr", "ADDR", 16, 8 },
199 { "wchan", "WCHAN", 16, 8 },
201 { "addr", "ADDR", 8, 8 },
202 { "wchan", "WCHAN", 8, 8 },
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;
243 static uid_t tuid
= (uid_t
)-1;
246 static int ndev
; /* number of devices */
247 static int maxdev
; /* number of devl structures allocated */
251 static struct devl
{ /* device list */
252 char dname
[DNSIZE
]; /* device name */
253 dev_t ddev
; /* device number */
259 } *tty
= NULL
; /* for t option */
260 static size_t ttysz
= 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
;
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 #define UCB_OPTS "-aceglnrtuvwxSU"
307 static void usage(void);
308 static char *getarg(char **);
309 static char *parse_format(char *);
310 static char *gettty(psinfo_t
*);
311 static int prfind(int, psinfo_t
*, char **);
312 static void prcom(psinfo_t
*, char *);
313 static void prtpct(ushort_t
, int);
314 static void print_time(time_t, int);
315 static void print_field(psinfo_t
*, struct field
*, const char *);
316 static void print_zombie_field(psinfo_t
*, struct field
*, const char *);
317 static void pr_fields(psinfo_t
*, const char *,
318 void (*print_fld
)(psinfo_t
*, struct field
*, const char *));
319 static int search(pid_t
*, int, pid_t
);
320 static void add_ugentry(struct ughead
*, char *);
321 static int uconv(struct ughead
*);
322 static int gconv(struct ughead
*);
323 static int ugfind(id_t
, struct ughead
*);
324 static void prtime(timestruc_t
, int, int);
325 static void przom(psinfo_t
*);
326 static int namencnt(char *, int, int);
327 static char *err_string(int);
328 static int print_proc(char *pname
);
329 static time_t delta_secs(const timestruc_t
*);
330 static int str2id(const char *, pid_t
*, long, long);
331 static int str2uid(const char *, uid_t
*, unsigned long, unsigned long);
332 static void *Realloc(void *, size_t);
333 static int pidcmp(const void *p1
, const void *p2
);
335 extern int ucbmain(int, char **);
336 static int stdmain(int, char **);
339 main(int argc
, char **argv
)
344 * The original two ps'es are linked in a single binary;
345 * their main()s are renamed to stdmain for /usr/bin/ps and
346 * ucbmain for /usr/ucb/ps.
347 * We try to figure out which instance of ps the user wants to run.
348 * Traditionally, the UCB variant doesn't require the flag argument
349 * start with a "-". If the first argument doesn't start with a
350 * "-", we call "ucbmain".
351 * If there's a first argument and it starts with a "-", we check
352 * whether any of the options isn't acceptable to "ucbmain"; in that
353 * case we run "stdmain".
354 * If we can't tell from the options which main to call, we check
355 * the binary we are running. We default to "stdmain" but
356 * any mention in the executable name of "ucb" causes us to call
359 if (argv
[1] != NULL
) {
360 if (argv
[1][0] != '-')
361 return (ucbmain(argc
, argv
));
362 else if (argv
[1][strspn(argv
[1], UCB_OPTS
)] != '\0')
363 return (stdmain(argc
, argv
));
368 if (me
!= NULL
&& strstr(me
, "ucb") != NULL
)
369 return (ucbmain(argc
, argv
));
371 return (stdmain(argc
, argv
));
375 stdmain(int argc
, char **argv
)
382 int pgerrflg
= 0; /* err flg: non-numeric arg w/p & g options */
385 struct dirent
*dentp
;
389 char loc_stime_str
[32];
391 (void) setlocale(LC_ALL
, "");
392 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
393 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
395 (void) textdomain(TEXT_DOMAIN
);
397 (void) memset(&euid_tbl
, 0, sizeof (euid_tbl
));
398 (void) memset(&ruid_tbl
, 0, sizeof (ruid_tbl
));
399 (void) memset(&egid_tbl
, 0, sizeof (egid_tbl
));
400 (void) memset(&rgid_tbl
, 0, sizeof (rgid_tbl
));
402 kbytes_per_page
= sysconf(_SC_PAGESIZE
) / 1024;
404 (void) gettimeofday(&now
, NULL
);
407 * calculate width of pid fields based on configured MAXPID
408 * (must be at least 5 to retain output format compatibility)
410 id
= maxpid
= (pid_t
)sysconf(_SC_MAXPID
);
412 while ((id
/= 10) > 0)
414 pidwidth
= pidwidth
< 5 ? 5 : pidwidth
;
416 fname
[F_PID
].width
= fname
[F_PPID
].width
= pidwidth
;
417 fname
[F_PGID
].width
= fname
[F_SID
].width
= pidwidth
;
421 * Specify the printf format with width and precision for
424 len
= snprintf(loc_stime_str
, sizeof (loc_stime_str
),
425 dcgettext(NULL
, "%8.8s", LC_TIME
), "STIME");
426 if (len
>= sizeof (loc_stime_str
))
427 len
= sizeof (loc_stime_str
) - 1;
429 fname
[F_STIME
].width
= fname
[F_STIME
].minwidth
= len
;
431 while ((c
= getopt(argc
, argv
, "jlfceAadLPWyZHh:t:p:g:u:U:G:n:s:o:z:"))
434 case 'H': /* Show home lgroups */
439 * Show processes/threads with given home lgroups
447 * Get all IDs in the list, verify for
448 * correctness and place in lgrps array.
451 /* Convert string to integer */
452 ret
= str2id(parg
, (pid_t
*)&id
, 0,
454 /* Complain if ID didn't parse correctly */
457 (void) fprintf(stderr
,
458 gettext("ps: %s "), parg
);
460 (void) fprintf(stderr
,
461 gettext("is an invalid "
462 "non-numeric argument"));
464 (void) fprintf(stderr
,
465 gettext("exceeds valid "
467 (void) fprintf(stderr
,
468 gettext(" for -h option\n"));
472 /* Extend lgrps array if needed */
473 if (nlgrps
== lgrps_size
) {
474 /* Double the size of the lgrps array */
478 lgrps
= Realloc(lgrps
,
479 lgrps_size
* sizeof (int));
481 /* place the id in the lgrps table */
482 lgrps
[nlgrps
++] = id
;
485 case 'l': /* long listing */
488 case 'f': /* full listing */
496 * Format output to reflect scheduler changes:
497 * high numbers for high priorities and don't
498 * print nice or p_cpu values. 'c' option only
499 * effective when used with 'l' or 'f' options.
503 case 'A': /* list every process */
504 case 'e': /* (obsolete) list every process */
506 tflg
= Gflg
= Uflg
= uflg
= pflg
= gflg
= sflg
= 0;
511 * Same as 'e' except no session group leaders
512 * and no non-terminal processes.
516 case 'd': /* same as e except no session leaders */
519 case 'L': /* show lwps */
522 case 'P': /* show bound processor */
525 case 'W': /* truncate long names */
528 case 'y': /* omit F & ADDR, report RSS & SZ in Kby */
531 case 'n': /* no longer needed; retain as no-op */
532 (void) fprintf(stderr
,
533 gettext("ps: warning: -n option ignored\n"));
535 case 't': /* terminals */
540 char nambuf
[TSZ
+6]; /* for "/dev/" + '\0' */
543 p
= Realloc(NULL
, TSZ
+1); /* for '\0' */
544 /* zero the buffer before using it */
547 if (isdigit(*parg
)) {
548 (void) strcpy(p
, "tty");
551 (void) strncat(p
, parg
, size
);
553 if ((ttysz
*= 2) == 0)
556 (ttysz
+ 1) * sizeof (struct tty
));
558 tty
[ntty
].tdev
= PRNODEV
;
559 (void) strcpy(nambuf
, "/dev/");
560 (void) strcat(nambuf
, p
);
561 if (stat64(nambuf
, &s
) == 0)
562 tty
[ntty
].tdev
= s
.st_rdev
;
563 tty
[ntty
++].tname
= p
;
566 case 'p': /* proc ids */
573 if ((ret
= str2id(parg
, &id
, 0, maxpid
)) != 0) {
575 (void) fprintf(stderr
,
576 gettext("ps: %s "), parg
);
578 (void) fprintf(stderr
,
579 gettext("is an invalid "
580 "non-numeric argument"));
582 (void) fprintf(stderr
,
583 gettext("exceeds valid "
585 (void) fprintf(stderr
,
586 gettext(" for -p option\n"));
591 if ((pidsz
*= 2) == 0)
594 pidsz
* sizeof (pid_t
));
599 case 's': /* session */
606 if ((ret
= str2id(parg
, &id
, 0, maxpid
)) != 0) {
608 (void) fprintf(stderr
,
609 gettext("ps: %s "), parg
);
611 (void) fprintf(stderr
,
612 gettext("is an invalid "
613 "non-numeric argument"));
615 (void) fprintf(stderr
,
616 gettext("exceeds valid "
618 (void) fprintf(stderr
,
619 gettext(" for -s option\n"));
623 if (nsessid
== sessidsz
) {
624 if ((sessidsz
*= 2) == 0)
626 sessid
= Realloc(sessid
,
627 sessidsz
* sizeof (pid_t
));
629 sessid
[nsessid
++] = id
;
632 case 'g': /* proc group */
639 if ((ret
= str2id(parg
, &id
, 0, maxpid
)) != 0) {
641 (void) fprintf(stderr
,
642 gettext("ps: %s "), parg
);
644 (void) fprintf(stderr
,
645 gettext("is an invalid "
646 "non-numeric argument"));
648 (void) fprintf(stderr
,
649 gettext("exceeds valid "
651 (void) fprintf(stderr
,
652 gettext(" for -g option\n"));
656 if (ngrpid
== grpidsz
) {
657 if ((grpidsz
*= 2) == 0)
659 grpid
= Realloc(grpid
,
660 grpidsz
* sizeof (pid_t
));
662 grpid
[ngrpid
++] = id
;
665 case 'u': /* effective user name or number */
670 add_ugentry(&euid_tbl
, parg
);
673 case 'U': /* real user name or number */
678 add_ugentry(&ruid_tbl
, parg
);
681 case 'G': /* real group name or number */
686 add_ugentry(&rgid_tbl
, parg
);
689 case 'o': /* output format */
691 while ((p
= parse_format(p
)) != NULL
)
694 case 'z': /* zone name or number */
701 if (zone_get_id(parg
, &id
) != 0) {
703 (void) fprintf(stderr
,
704 gettext("ps: unknown zone %s\n"),
709 if (nzoneid
== zoneidsz
) {
710 if ((zoneidsz
*= 2) == 0)
712 zoneid
= Realloc(zoneid
,
713 zoneidsz
* sizeof (zoneid_t
));
715 zoneid
[nzoneid
++] = id
;
718 case 'Z': /* show zone name */
721 default: /* error on ? */
726 if (errflg
|| optind
< argc
|| pgerrflg
)
730 tty
[ntty
].tname
= NULL
;
732 * If an appropriate option has not been specified, use the
733 * current terminal and effective uid as the default.
735 if (!(aflg
|Aflg
|dflg
|Gflg
|hflg
|Uflg
|uflg
|tflg
|pflg
|gflg
|sflg
|zflg
)) {
741 /* get our own controlling tty name using /proc */
742 (void) snprintf(pname
, sizeof (pname
),
743 "%s/self/psinfo", procdir
);
744 if ((procfd
= open(pname
, O_RDONLY
)) < 0 ||
745 read(procfd
, (char *)&info
, sizeof (info
)) < 0 ||
746 info
.pr_ttydev
== PRNODEV
) {
747 (void) fprintf(stderr
,
748 gettext("ps: no controlling terminal\n"));
751 (void) close(procfd
);
754 name
= gettty(&info
);
756 (void) fprintf(stderr
,
757 gettext("ps: can't find controlling terminal\n"));
761 if ((ttysz
*= 2) == 0)
763 tty
= Realloc(tty
, (ttysz
+ 1) * sizeof (struct tty
));
765 tty
[ntty
].tdev
= info
.pr_ttydev
;
766 tty
[ntty
++].tname
= name
;
767 tty
[ntty
].tname
= NULL
;
772 Gflg
= Uflg
= uflg
= pflg
= sflg
= gflg
= aflg
= dflg
= 0;
775 if (Aflg
| aflg
| dflg
)
778 i
= 0; /* prepare to exit on name lookup errors */
779 i
+= uconv(&euid_tbl
);
780 i
+= uconv(&ruid_tbl
);
781 i
+= gconv(&egid_tbl
);
782 i
+= gconv(&rgid_tbl
);
786 /* allocate a buffer for lwpsinfo structures */
788 if (Lflg
&& (lpsinfobuf
= malloc(lpbufsize
)) == NULL
) {
789 (void) fprintf(stderr
,
790 gettext("ps: no memory\n"));
794 if (fields
) { /* print user-specified header */
798 for (f
= fields
; f
!= NULL
; f
= f
->next
) {
803 (void) printf("%-*s",
804 f
->width
, f
->header
);
810 * Print these headers full width
811 * unless they appear at the end.
813 if (f
->next
!= NULL
) {
814 (void) printf("%-*s",
815 f
->width
, f
->header
);
823 f
->width
, f
->header
);
829 } else { /* print standard header */
831 * All fields before 'PID' are printed with a trailing space
832 * as a separator and that is how we print the headers too.
838 (void) printf(" F S ");
841 (void) printf(" ZONE ");
843 (void) printf(" UID ");
845 (void) printf(" UID ");
847 (void) printf("%*s", pidwidth
, "PID");
849 (void) printf(" %*s", pidwidth
, "PPID");
851 (void) printf(" %*s %*s", pidwidth
, "PGID",
854 (void) printf(" LWP");
856 (void) printf(" PSR");
858 (void) printf(" NLWP");
860 (void) printf(" CLS PRI");
861 else if (lflg
|| fflg
) {
864 (void) printf(" PRI NI");
868 (void) printf(" RSS SZ WCHAN");
870 (void) printf(" ADDR SZ WCHAN");
873 (void) printf(" %s", loc_stime_str
);
875 (void) printf(" LGRP");
877 (void) printf(" TTY LTIME CMD\n");
879 (void) printf(" TTY TIME CMD\n");
883 if (pflg
&& !(aflg
|Aflg
|dflg
|Gflg
|Uflg
|uflg
|hflg
|tflg
|gflg
|sflg
|zflg
) &&
884 npid
<= PTHRESHOLD
) {
886 * If we are looking at specific processes go straight
887 * to their /proc entries and don't scan /proc.
891 (void) qsort(pid
, npid
, sizeof (pid_t
), pidcmp
);
892 for (i
= 0; i
< npid
; i
++) {
895 if (i
>= 1 && pid
[i
] == pid
[i
- 1])
897 (void) sprintf(pname
, "%d", (int)pid
[i
]);
898 if (print_proc(pname
) == 0)
903 * Determine which processes to print info about by searching
904 * the /proc directory and looking at each process.
906 if ((dirp
= opendir(procdir
)) == NULL
) {
907 (void) fprintf(stderr
,
908 gettext("ps: cannot open PROC directory %s\n"),
913 /* for each active process --- */
914 while ((dentp
= readdir(dirp
)) != NULL
) {
915 if (dentp
->d_name
[0] == '.') /* skip . and .. */
917 if (print_proc(dentp
->d_name
) == 0)
921 (void) closedir(dirp
);
928 print_proc(char *pid_name
)
930 char pname
[PATH_MAX
];
933 int procfd
; /* filedescriptor for /proc/nnnnn/psinfo */
934 char *tp
; /* ptr to ttyname, if any */
935 psinfo_t info
; /* process information from /proc */
936 lwpsinfo_t
*lwpsinfo
; /* array of lwpsinfo structs */
938 pdlen
= snprintf(pname
, sizeof (pname
), "%s/%s/", procdir
, pid_name
);
939 if (pdlen
>= sizeof (pname
) - 10)
942 (void) strcpy(&pname
[pdlen
], "psinfo");
943 if ((procfd
= open(pname
, O_RDONLY
)) == -1) {
944 /* Process may have exited meanwhile. */
948 * Get the info structure for the process and close quickly.
950 if (read(procfd
, (char *)&info
, sizeof (info
)) < 0) {
953 (void) close(procfd
);
954 if (saverr
== EAGAIN
)
956 if (saverr
!= ENOENT
)
957 (void) fprintf(stderr
,
958 gettext("ps: read() on %s: %s\n"),
959 pname
, err_string(saverr
));
962 (void) close(procfd
);
965 if (info
.pr_lwp
.pr_state
== 0) /* can't happen? */
969 * Omit session group leaders for 'a' and 'd' options.
971 if ((info
.pr_pid
== info
.pr_sid
) && (dflg
|| aflg
))
975 else if (pflg
&& search(pid
, npid
, info
.pr_pid
))
976 found
++; /* ppid in p option arg list */
977 else if (uflg
&& ugfind((id_t
)info
.pr_euid
, &euid_tbl
))
978 found
++; /* puid in u option arg list */
979 else if (Uflg
&& ugfind((id_t
)info
.pr_uid
, &ruid_tbl
))
980 found
++; /* puid in U option arg list */
982 else if (gflg
&& ugfind((id_t
)info
.pr_egid
, &egid_tbl
))
983 found
++; /* pgid in g option arg list */
985 else if (Gflg
&& ugfind((id_t
)info
.pr_gid
, &rgid_tbl
))
986 found
++; /* pgid in G option arg list */
987 else if (gflg
&& search(grpid
, ngrpid
, info
.pr_pgid
))
988 found
++; /* grpid in g option arg list */
989 else if (sflg
&& search(sessid
, nsessid
, info
.pr_sid
))
990 found
++; /* sessid in s option arg list */
991 else if (zflg
&& search(zoneid
, nzoneid
, info
.pr_zoneid
))
992 found
++; /* zoneid in z option arg list */
993 else if (hflg
&& search((pid_t
*)lgrps
, nlgrps
, info
.pr_lwp
.pr_lgrp
))
994 found
++; /* home lgroup in h option arg list */
995 if (!found
&& !tflg
&& !aflg
)
997 if (!prfind(found
, &info
, &tp
))
999 if (Lflg
&& (info
.pr_nlwp
+ info
.pr_nzomb
) > 1) {
1002 (void) strcpy(&pname
[pdlen
], "lpsinfo");
1003 if ((procfd
= open(pname
, O_RDONLY
)) == -1)
1006 * Get the info structures for the lwps.
1008 prsz
= read(procfd
, lpsinfobuf
, lpbufsize
);
1012 (void) close(procfd
);
1013 if (saverr
== EAGAIN
)
1015 if (saverr
!= ENOENT
)
1016 (void) fprintf(stderr
,
1017 gettext("ps: read() on %s: %s\n"),
1018 pname
, err_string(saverr
));
1021 (void) close(procfd
);
1022 if (prsz
== lpbufsize
) {
1024 * buffer overflow. Realloc new buffer.
1025 * Error handling is done in Realloc().
1028 lpsinfobuf
= Realloc(lpsinfobuf
, lpbufsize
);
1031 if (lpsinfobuf
->pr_nent
!= (info
.pr_nlwp
+ info
.pr_nzomb
))
1033 lwpsinfo
= (lwpsinfo_t
*)(lpsinfobuf
+ 1);
1035 if (!Lflg
|| (info
.pr_nlwp
+ info
.pr_nzomb
) <= 1) {
1041 info
.pr_lwp
= *lwpsinfo
;
1043 /* LINTED improper alignment */
1044 lwpsinfo
= (lwpsinfo_t
*)((char *)lwpsinfo
+
1045 lpsinfobuf
->pr_entsize
);
1046 } while (++nlwp
< lpsinfobuf
->pr_nent
);
1052 field_cmp(const void *l
, const void *r
)
1054 struct def_field
*lhs
= *((struct def_field
**)l
);
1055 struct def_field
*rhs
= *((struct def_field
**)r
);
1057 return (strcmp(lhs
->fname
, rhs
->fname
));
1061 usage(void) /* print usage message and quit */
1063 struct def_field
*df
, *sorted
[NFIELDS
];
1064 int pos
= 80, i
= 0;
1066 static char usage1
[] =
1067 "ps [ -aAdefHlcjLPWyZ ] [ -o format ] [ -t termlist ]";
1068 static char usage2
[] =
1069 "\t[ -u userlist ] [ -U userlist ] [ -G grouplist ]";
1070 static char usage3
[] =
1071 "\t[ -p proclist ] [ -g pgrplist ] [ -s sidlist ]";
1072 static char usage4
[] =
1073 "\t[ -z zonelist ] [-h lgrplist]";
1074 static char usage5
[] =
1075 " 'format' is one or more of:";
1077 (void) fprintf(stderr
,
1078 gettext("usage: %s\n%s\n%s\n%s\n%s"),
1079 gettext(usage1
), gettext(usage2
), gettext(usage3
),
1080 gettext(usage4
), gettext(usage5
));
1083 * Now print out the possible output formats such that they neatly fit
1084 * into eighty columns. Note that the fact that we are determining
1085 * this output programmatically means that a gettext() is impossible --
1086 * but it would be a mistake to localize the output formats anyway as
1087 * they are tokens for input, not output themselves.
1089 for (df
= &fname
[0]; df
< &fname
[NFIELDS
]; df
++)
1092 (void) qsort(sorted
, NFIELDS
, sizeof (void *), field_cmp
);
1094 for (i
= 0; i
< NFIELDS
; i
++) {
1095 if (pos
+ strlen((df
= sorted
[i
])->fname
) + 1 >= 80) {
1096 (void) fprintf(stderr
, "\n\t");
1100 (void) fprintf(stderr
, "%s%s", pos
> 8 ? " " : "", df
->fname
);
1101 pos
+= strlen(df
->fname
) + 1;
1104 (void) fprintf(stderr
, "\n");
1110 * getarg() finds the next argument in list and copies arg into argbuf.
1111 * p1 first pts to arg passed back from getopt routine. p1 is then
1112 * bumped to next character that is not a comma or blank -- p1 NULL
1113 * indicates end of list.
1118 static char argbuf
[ARGSIZ
];
1120 char *parga
= argbuf
;
1123 while ((c
= *p1
) != '\0' && (c
== ',' || isspace(c
)))
1126 while ((c
= *p1
) != '\0' && c
!= ',' && !isspace(c
)) {
1127 if (parga
< argbuf
+ ARGSIZ
- 1)
1133 while ((c
= *p1
) != '\0' && (c
== ',' || isspace(c
)))
1142 * parse_format() takes the argument to the -o option,
1143 * sets up the next output field structure, and returns
1144 * a pointer to any further output field specifier(s).
1145 * As a side-effect, it increments errflg if encounters a format error.
1148 parse_format(char *arg
)
1152 char *header
= NULL
;
1154 struct def_field
*df
;
1157 while ((c
= *arg
) != '\0' && (c
== ',' || isspace(c
)))
1162 arg
= strpbrk(arg
, " \t\r\v\f\n,=");
1171 width
= strlen(header
);
1173 while (s
> header
&& isspace(*--s
))
1175 while (isspace(*header
))
1179 for (df
= &fname
[0]; df
< &fname
[NFIELDS
]; df
++)
1180 if (strcmp(name
, df
->fname
) == 0) {
1181 if (strcmp(name
, "lwp") == 0)
1185 if (df
>= &fname
[NFIELDS
]) {
1186 (void) fprintf(stderr
,
1187 gettext("ps: unknown output format: -o %s\n"),
1192 if ((f
= malloc(sizeof (*f
))) == NULL
) {
1193 (void) fprintf(stderr
,
1194 gettext("ps: malloc() for output format failed, %s\n"),
1199 f
->fname
= df
- &fname
[0];
1200 f
->header
= header
? header
: df
->header
;
1203 if (*f
->header
!= '\0')
1205 f
->width
= max(width
, df
->minwidth
);
1208 fields
= last_field
= f
;
1210 last_field
->next
= f
;
1218 devlookup(dev_t ddev
)
1223 for (dp
= devl
, i
= 0; i
< ndev
; dp
++, i
++) {
1224 if (dp
->ddev
== ddev
)
1231 devadd(char *name
, dev_t ddev
)
1236 if (ndev
== maxdev
) {
1238 devl
= Realloc(devl
, maxdev
* sizeof (struct devl
));
1244 (void) strcpy(dp
->dname
, "??");
1248 leng
= strlen(name
);
1249 /* Strip off /dev/ */
1250 if (leng
< DNSIZE
+ 4)
1251 (void) strcpy(dp
->dname
, &name
[5]);
1253 start
= leng
- DNSIZE
- 1;
1255 for (i
= start
; i
< leng
&& name
[i
] != '/'; i
++)
1258 (void) strncpy(dp
->dname
, &name
[start
], DNSIZE
);
1260 (void) strncpy(dp
->dname
, &name
[i
+1], DNSIZE
);
1266 * gettty returns the user's tty number or ? if none.
1269 gettty(psinfo_t
*psinfo
)
1271 extern char *_ttyname_dev(dev_t
, char *, size_t);
1272 static zoneid_t zid
= -1;
1273 char devname
[TTYNAME_MAX
];
1279 if (psinfo
->pr_ttydev
== PRNODEV
|| psinfo
->pr_zoneid
!= zid
)
1282 if ((retval
= devlookup(psinfo
->pr_ttydev
)) != NULL
)
1285 retval
= _ttyname_dev(psinfo
->pr_ttydev
, devname
, sizeof (devname
));
1287 return (devadd(retval
, psinfo
->pr_ttydev
));
1291 * Find the process's tty and return 1 if process is to be printed.
1294 prfind(int found
, psinfo_t
*psinfo
, char **tpp
)
1299 if (psinfo
->pr_nlwp
== 0) {
1300 /* process is a zombie */
1308 * Get current terminal. If none ("?") and 'a' is set, don't print
1309 * info. If 't' is set, check if term is in list of desired terminals
1310 * and print it if it is.
1312 tp
= gettty(psinfo
);
1313 if (aflg
&& *tp
== '?') {
1317 if (tflg
&& !found
) {
1320 for (ttyp
= tty
; ttyp
->tname
!= NULL
; ttyp
++) {
1322 * Look for a name match
1324 if (strcmp(tp
, ttyp
->tname
) == 0) {
1329 * Look for same device under different names.
1331 if ((other
== NULL
) &&
1332 (ttyp
->tdev
!= PRNODEV
) &&
1333 (psinfo
->pr_ttydev
== ttyp
->tdev
))
1334 other
= ttyp
->tname
;
1336 if (!match
&& (other
!= NULL
)) {
1338 * found under a different name
1343 if (!match
|| (tuid
!= (uid_t
)-1 && tuid
!= psinfo
->pr_euid
)) {
1345 * not found OR not matching euid
1356 * Print info about the process.
1359 prcom(psinfo_t
*psinfo
, char *ttyp
)
1368 char zonename
[ZONENAME_MAX
];
1371 * If process is zombie, call zombie print routine and return.
1373 if (psinfo
->pr_nlwp
== 0) {
1375 pr_fields(psinfo
, ttyp
, print_zombie_field
);
1381 zombie_lwp
= (Lflg
&& psinfo
->pr_lwp
.pr_sname
== 'Z');
1384 * If user specified '-o format', print requested fields and return.
1386 if (fields
!= NULL
) {
1387 pr_fields(psinfo
, ttyp
, print_field
);
1392 * All fields before 'PID' are printed with a trailing space as a
1393 * separator, rather than keeping track of which column is first. All
1394 * other fields are printed with a leading space.
1398 (void) printf("%2x ", psinfo
->pr_flag
& 0377); /* F */
1399 (void) printf("%c ", psinfo
->pr_lwp
.pr_sname
); /* S */
1402 if (Zflg
) { /* ZONE */
1403 if (getzonenamebyid(psinfo
->pr_zoneid
, zonename
,
1404 sizeof (zonename
)) < 0) {
1405 if (snprintf(NULL
, 0, "%d",
1406 ((int)psinfo
->pr_zoneid
)) > 7)
1407 (void) printf(" %6.6d%c ",
1408 ((int)psinfo
->pr_zoneid
), '*');
1410 (void) printf(" %7.7d ",
1411 ((int)psinfo
->pr_zoneid
));
1415 nw
= mbstowcs(NULL
, zonename
, 0);
1416 if (nw
== (size_t)-1)
1417 (void) printf("%8.8s ", "ERROR");
1419 (void) wprintf(L
"%7.7s%c ", zonename
, '*');
1421 (void) wprintf(L
"%8.8s ", zonename
);
1425 if (fflg
) { /* UID */
1426 if ((pwd
= getpwuid(psinfo
->pr_euid
)) != NULL
) {
1429 nw
= mbstowcs(NULL
, pwd
->pw_name
, 0);
1430 if (nw
== (size_t)-1)
1431 (void) printf("%8.8s ", "ERROR");
1433 (void) wprintf(L
"%7.7s%c ", pwd
->pw_name
, '*');
1435 (void) wprintf(L
"%8.8s ", pwd
->pw_name
);
1437 if (snprintf(NULL
, 0, "%u",
1438 (psinfo
->pr_euid
)) > 7)
1439 (void) printf(" %6.6u%c ", psinfo
->pr_euid
,
1442 (void) printf(" %7.7u ", psinfo
->pr_euid
);
1445 if (snprintf(NULL
, 0, "%u", (psinfo
->pr_euid
)) > 6)
1446 (void) printf("%5.5u%c ", psinfo
->pr_euid
, '*');
1448 (void) printf("%6u ", psinfo
->pr_euid
);
1450 (void) printf("%*d", pidwidth
, (int)psinfo
->pr_pid
); /* PID */
1452 (void) printf(" %*d", pidwidth
,
1453 (int)psinfo
->pr_ppid
); /* PPID */
1455 (void) printf(" %*d", pidwidth
,
1456 (int)psinfo
->pr_pgid
); /* PGID */
1457 (void) printf(" %*d", pidwidth
,
1458 (int)psinfo
->pr_sid
); /* SID */
1461 (void) printf(" %5d", (int)psinfo
->pr_lwp
.pr_lwpid
); /* LWP */
1463 if (psinfo
->pr_lwp
.pr_bindpro
== PBIND_NONE
) /* PSR */
1464 (void) printf(" -");
1466 (void) printf(" %3d", psinfo
->pr_lwp
.pr_bindpro
);
1468 if (Lflg
&& fflg
) /* NLWP */
1469 (void) printf(" %5d", psinfo
->pr_nlwp
+ psinfo
->pr_nzomb
);
1471 if (zombie_lwp
) /* CLS */
1474 (void) printf(" %4s", psinfo
->pr_lwp
.pr_clname
);
1475 (void) printf(" %3d", psinfo
->pr_lwp
.pr_pri
); /* PRI */
1476 } else if (lflg
|| fflg
) {
1477 (void) printf(" %3d", psinfo
->pr_lwp
.pr_cpu
& 0377); /* C */
1478 if (lflg
) { /* PRI NI */
1480 * Print priorities the old way (lower numbers
1481 * mean higher priority) and print nice value
1482 * for time sharing procs.
1484 (void) printf(" %3d", psinfo
->pr_lwp
.pr_oldpri
);
1485 if (psinfo
->pr_lwp
.pr_oldpri
!= 0)
1486 (void) printf(" %2d", psinfo
->pr_lwp
.pr_nice
);
1488 (void) printf(" %2.2s",
1489 psinfo
->pr_lwp
.pr_clname
);
1494 if (psinfo
->pr_flag
& SSYS
) /* RSS */
1495 (void) printf(" 0");
1496 else if (psinfo
->pr_rssize
)
1497 (void) printf(" %5lu",
1498 (ulong_t
)psinfo
->pr_rssize
);
1500 (void) printf(" ?");
1501 if (psinfo
->pr_flag
& SSYS
) /* SZ */
1502 (void) printf(" 0");
1503 else if (psinfo
->pr_size
)
1504 (void) printf(" %6lu",
1505 (ulong_t
)psinfo
->pr_size
);
1507 (void) printf(" ?");
1510 if (psinfo
->pr_addr
) /* ADDR */
1511 (void) printf(" %8lx",
1512 (ulong_t
)psinfo
->pr_addr
);
1515 (void) printf(" ?");
1516 if (psinfo
->pr_flag
& SSYS
) /* SZ */
1517 (void) printf(" 0");
1518 else if (psinfo
->pr_size
)
1519 (void) printf(" %6lu",
1520 (ulong_t
)psinfo
->pr_size
/ kbytes_per_page
);
1522 (void) printf(" ?");
1524 if (psinfo
->pr_lwp
.pr_sname
!= 'S') /* WCHAN */
1527 else if (psinfo
->pr_lwp
.pr_wchan
)
1528 (void) printf(" %8lx",
1529 (ulong_t
)psinfo
->pr_lwp
.pr_wchan
);
1532 (void) printf(" ?");
1534 if (fflg
) { /* STIME */
1535 int width
= fname
[F_STIME
].width
;
1537 prtime(psinfo
->pr_lwp
.pr_start
, width
+ 1, 1);
1539 prtime(psinfo
->pr_start
, width
+ 1, 1);
1543 /* Display home lgroup */
1544 (void) printf(" %4d", (int)psinfo
->pr_lwp
.pr_lgrp
);
1547 (void) printf(" %-8.14s", ttyp
); /* TTY */
1549 tm
= psinfo
->pr_lwp
.pr_time
.tv_sec
;
1550 if (psinfo
->pr_lwp
.pr_time
.tv_nsec
> 500000000)
1553 tm
= psinfo
->pr_time
.tv_sec
;
1554 if (psinfo
->pr_time
.tv_nsec
> 500000000)
1557 (void) printf(" %4ld:%.2ld", tm
/ 60, tm
% 60); /* [L]TIME */
1560 (void) printf(" <defunct>\n");
1564 if (!fflg
) { /* CMD */
1565 wcnt
= namencnt(psinfo
->pr_fname
, 16, 8);
1566 (void) printf(" %.*s\n", wcnt
, psinfo
->pr_fname
);
1572 * PRARGSZ == length of cmd arg string.
1574 psinfo
->pr_psargs
[PRARGSZ
-1] = '\0';
1575 bytesleft
= PRARGSZ
;
1576 for (cp
= psinfo
->pr_psargs
; *cp
!= '\0'; cp
+= length
) {
1577 length
= mbtowc(&wchar
, cp
, MB_LEN_MAX
);
1580 if (length
< 0 || !iswprint(wchar
)) {
1583 if (bytesleft
<= length
) {
1587 /* omit the unprintable character */
1588 (void) memmove(cp
, cp
+length
, bytesleft
-length
);
1591 bytesleft
-= length
;
1593 wcnt
= namencnt(psinfo
->pr_psargs
, PRARGSZ
, lflg
? 35 : PRARGSZ
);
1594 (void) printf(" %.*s\n", wcnt
, psinfo
->pr_psargs
);
1598 * Print percent from 16-bit binary fraction [0 .. 1]
1599 * Round up .01 to .1 to indicate some small percentage (the 0x7000 below).
1602 prtpct(ushort_t pct
, int width
)
1604 uint_t value
= pct
; /* need 32 bits to compute with */
1606 value
= ((value
* 1000) + 0x7000) >> 15; /* [0 .. 1000] */
1609 if ((width
-= 2) < 2)
1611 (void) printf("%*u.%u", width
, value
/ 10, value
% 10);
1615 print_time(time_t tim
, int width
)
1624 (void) printf("%*s", width
, "-");
1636 (void) snprintf(buf
, sizeof (buf
), "%ld-%2.2ld:%2.2ld:%2.2ld",
1637 days
, hours
, minutes
, seconds
);
1638 } else if (hours
> 0) {
1639 (void) snprintf(buf
, sizeof (buf
), "%2.2ld:%2.2ld:%2.2ld",
1640 hours
, minutes
, seconds
);
1642 (void) snprintf(buf
, sizeof (buf
), "%2.2ld:%2.2ld",
1646 (void) printf("%*s", width
, buf
);
1650 print_field(psinfo_t
*psinfo
, struct field
*f
, const char *ttyp
)
1652 int width
= f
->width
;
1662 char c
= '\0', *csave
= NULL
;
1665 zombie_lwp
= (Lflg
&& psinfo
->pr_lwp
.pr_sname
== 'Z');
1669 if ((pwd
= getpwuid(psinfo
->pr_uid
)) != NULL
) {
1672 nw
= mbstowcs(NULL
, pwd
->pw_name
, 0);
1673 if (nw
== (size_t)-1)
1674 (void) printf("%*s ", width
, "ERROR");
1675 else if (Wflg
&& nw
> width
)
1676 (void) wprintf(L
"%.*s%c", width
- 1,
1679 (void) wprintf(L
"%*s", width
, pwd
->pw_name
);
1681 if (Wflg
&& snprintf(NULL
, 0, "%u",
1682 (psinfo
->pr_uid
)) > width
)
1684 (void) printf("%*u%c", width
- 1,
1685 psinfo
->pr_uid
, '*');
1687 (void) printf("%*u", width
, psinfo
->pr_uid
);
1691 if ((pwd
= getpwuid(psinfo
->pr_euid
)) != NULL
) {
1694 nw
= mbstowcs(NULL
, pwd
->pw_name
, 0);
1695 if (nw
== (size_t)-1)
1696 (void) printf("%*s ", width
, "ERROR");
1697 else if (Wflg
&& nw
> width
)
1698 (void) wprintf(L
"%.*s%c", width
- 1,
1701 (void) wprintf(L
"%*s", width
, pwd
->pw_name
);
1703 if (Wflg
&& snprintf(NULL
, 0, "%u",
1704 (psinfo
->pr_euid
)) > width
)
1706 (void) printf("%*u%c", width
- 1,
1707 psinfo
->pr_euid
, '*');
1709 (void) printf("%*u", width
, psinfo
->pr_euid
);
1713 if ((grp
= getgrgid(psinfo
->pr_gid
)) != NULL
)
1714 (void) printf("%*s", width
, grp
->gr_name
);
1716 (void) printf("%*u", width
, psinfo
->pr_gid
);
1719 if ((grp
= getgrgid(psinfo
->pr_egid
)) != NULL
)
1720 (void) printf("%*s", width
, grp
->gr_name
);
1722 (void) printf("%*u", width
, psinfo
->pr_egid
);
1725 (void) printf("%*u", width
, psinfo
->pr_uid
);
1728 (void) printf("%*u", width
, psinfo
->pr_euid
);
1731 (void) printf("%*u", width
, psinfo
->pr_gid
);
1734 (void) printf("%*u", width
, psinfo
->pr_egid
);
1737 (void) printf("%*d", width
, (int)psinfo
->pr_pid
);
1740 (void) printf("%*d", width
, (int)psinfo
->pr_ppid
);
1743 (void) printf("%*d", width
, (int)psinfo
->pr_pgid
);
1746 (void) printf("%*d", width
, (int)psinfo
->pr_sid
);
1749 if (zombie_lwp
|| psinfo
->pr_lwp
.pr_bindpro
== PBIND_NONE
)
1750 (void) printf("%*s", width
, "-");
1752 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_bindpro
);
1755 (void) printf("%*d", width
, (int)psinfo
->pr_lwp
.pr_lwpid
);
1758 (void) printf("%*d", width
, psinfo
->pr_nlwp
+ psinfo
->pr_nzomb
);
1762 (void) printf("%*s", width
, "-");
1764 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_oldpri
);
1768 (void) printf("%*s", width
, "-");
1770 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_pri
);
1773 mask
= 0xffffffffUL
;
1775 mask
>>= (8 - width
) * 4;
1776 (void) printf("%*lx", width
, psinfo
->pr_flag
& mask
);
1779 (void) printf("%*c", width
, psinfo
->pr_lwp
.pr_sname
);
1783 (void) printf("%*s", width
, "-");
1785 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_cpu
);
1789 (void) printf("%*s", width
, "-");
1791 prtpct(psinfo
->pr_lwp
.pr_pctcpu
, width
);
1793 prtpct(psinfo
->pr_pctcpu
, width
);
1796 prtpct(psinfo
->pr_pctmem
, width
);
1799 (void) printf("%*lu", width
,
1800 (ulong_t
)psinfo
->pr_size
/ kbytes_per_page
);
1803 (void) printf("%*lu", width
, (ulong_t
)psinfo
->pr_size
);
1806 (void) printf("%*lu", width
, (ulong_t
)psinfo
->pr_rssize
);
1809 /* if pr_oldpri is zero, then this class has no nice */
1811 (void) printf("%*s", width
, "-");
1812 else if (psinfo
->pr_lwp
.pr_oldpri
!= 0)
1813 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_nice
);
1815 (void) printf("%*.*s", width
, width
,
1816 psinfo
->pr_lwp
.pr_clname
);
1820 (void) printf("%*s", width
, "-");
1822 (void) printf("%*.*s", width
, width
,
1823 psinfo
->pr_lwp
.pr_clname
);
1827 prtime(psinfo
->pr_lwp
.pr_start
, width
, 0);
1829 prtime(psinfo
->pr_start
, width
, 0);
1833 print_time(delta_secs(&psinfo
->pr_lwp
.pr_start
),
1836 print_time(delta_secs(&psinfo
->pr_start
), width
);
1840 cputime
= psinfo
->pr_lwp
.pr_time
.tv_sec
;
1841 if (psinfo
->pr_lwp
.pr_time
.tv_nsec
> 500000000)
1844 cputime
= psinfo
->pr_time
.tv_sec
;
1845 if (psinfo
->pr_time
.tv_nsec
> 500000000)
1848 print_time(cputime
, width
);
1851 (void) printf("%-*s", width
, ttyp
);
1855 (void) printf("%*s", width
, "-");
1857 (void) printf("%*lx", width
,
1858 (long)psinfo
->pr_lwp
.pr_addr
);
1860 (void) printf("%*lx", width
, (long)psinfo
->pr_addr
);
1863 if (!zombie_lwp
&& psinfo
->pr_lwp
.pr_wchan
)
1864 (void) printf("%*lx", width
,
1865 (long)psinfo
->pr_lwp
.pr_wchan
);
1867 (void) printf("%*.*s", width
, width
, "-");
1871 * Print full width unless this is the last output format.
1874 if (f
->next
!= NULL
)
1875 (void) printf("%-*s", width
, "<defunct>");
1877 (void) printf("%s", "<defunct>");
1880 wcnt
= namencnt(psinfo
->pr_fname
, 16, width
);
1881 if (f
->next
!= NULL
)
1882 (void) printf("%-*.*s", width
, wcnt
, psinfo
->pr_fname
);
1884 (void) printf("%-.*s", wcnt
, psinfo
->pr_fname
);
1888 if (f
->next
!= NULL
)
1889 (void) printf("%-*s", width
, "<defunct>");
1891 (void) printf("%s", "<defunct>");
1894 csave
= strpbrk(psinfo
->pr_psargs
, " \t\r\v\f\n");
1902 * PRARGSZ == length of cmd arg string.
1905 (void) printf("%-*s", width
, "<defunct>");
1908 psinfo
->pr_psargs
[PRARGSZ
-1] = '\0';
1909 bytesleft
= PRARGSZ
;
1910 for (cp
= psinfo
->pr_psargs
; *cp
!= '\0'; cp
+= length
) {
1911 length
= mbtowc(&wchar
, cp
, MB_LEN_MAX
);
1914 if (length
< 0 || !iswprint(wchar
)) {
1917 if (bytesleft
<= length
) {
1921 /* omit the unprintable character */
1922 (void) memmove(cp
, cp
+length
, bytesleft
-length
);
1925 bytesleft
-= length
;
1927 wcnt
= namencnt(psinfo
->pr_psargs
, PRARGSZ
, width
);
1929 * Print full width unless this is the last format.
1931 if (f
->next
!= NULL
)
1932 (void) printf("%-*.*s", width
, wcnt
,
1935 (void) printf("%-.*s", wcnt
,
1937 if (f
->fname
== F_COMM
&& csave
)
1941 (void) printf("%*d", width
, (int)psinfo
->pr_taskid
);
1944 (void) printf("%*d", width
, (int)psinfo
->pr_projid
);
1948 struct project cproj
;
1949 char proj_buf
[PROJECT_BUFSZ
];
1951 if ((getprojbyid(psinfo
->pr_projid
, &cproj
,
1952 (void *)&proj_buf
, PROJECT_BUFSZ
)) == NULL
) {
1953 if (Wflg
&& snprintf(NULL
, 0, "%d",
1954 ((int)psinfo
->pr_projid
)) > width
)
1955 (void) printf("%.*d%c", width
- 1,
1956 ((int)psinfo
->pr_projid
), '*');
1958 (void) printf("%*d", width
,
1959 (int)psinfo
->pr_projid
);
1963 if (cproj
.pj_name
!= NULL
)
1964 nw
= mbstowcs(NULL
, cproj
.pj_name
, 0);
1965 if (cproj
.pj_name
== NULL
)
1966 (void) printf("%*s ", width
, "---");
1967 else if (nw
== (size_t)-1)
1968 (void) printf("%*s ", width
, "ERROR");
1969 else if (Wflg
&& nw
> width
)
1970 (void) wprintf(L
"%.*s%c", width
- 1,
1971 cproj
.pj_name
, '*');
1973 (void) wprintf(L
"%*s", width
,
1979 if (zombie_lwp
|| psinfo
->pr_lwp
.pr_bindpset
== PS_NONE
)
1980 (void) printf("%*s", width
, "-");
1982 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_bindpset
);
1985 (void) printf("%*d", width
, (int)psinfo
->pr_zoneid
);
1989 char zonename
[ZONENAME_MAX
];
1991 if (getzonenamebyid(psinfo
->pr_zoneid
, zonename
,
1992 sizeof (zonename
)) < 0) {
1993 if (Wflg
&& snprintf(NULL
, 0, "%d",
1994 ((int)psinfo
->pr_zoneid
)) > width
)
1995 (void) printf("%.*d%c", width
- 1,
1996 ((int)psinfo
->pr_zoneid
), '*');
1998 (void) printf("%*d", width
,
1999 (int)psinfo
->pr_zoneid
);
2003 nw
= mbstowcs(NULL
, zonename
, 0);
2004 if (nw
== (size_t)-1)
2005 (void) printf("%*s ", width
, "ERROR");
2006 else if (Wflg
&& nw
> width
)
2007 (void) wprintf(L
"%.*s%c", width
- 1,
2010 (void) wprintf(L
"%*s", width
, zonename
);
2015 if (psinfo
->pr_contract
== -1)
2016 (void) printf("%*s", width
, "-");
2018 (void) printf("%*ld", width
, (long)psinfo
->pr_contract
);
2021 /* Display home lgroup */
2022 (void) printf("%*d", width
, (int)psinfo
->pr_lwp
.pr_lgrp
);
2026 (void) printf("%*s", width
,
2027 psinfo
->pr_dmodel
== PR_MODEL_LP64
? "_LP64" : "_ILP32");
2033 print_zombie_field(psinfo_t
*psinfo
, struct field
*f
, const char *ttyp
)
2036 int width
= f
->width
;
2043 * Print full width unless this is the last output format.
2045 wcnt
= min(width
, sizeof ("<defunct>"));
2046 if (f
->next
!= NULL
)
2047 (void) printf("%-*.*s", width
, wcnt
, "<defunct>");
2049 (void) printf("%-.*s", wcnt
, "<defunct>");
2061 (void) printf("%*s", width
, "-");
2069 (void) printf("%*d", width
, 0);
2073 print_field(psinfo
, f
, ttyp
);
2079 pr_fields(psinfo_t
*psinfo
, const char *ttyp
,
2080 void (*print_fld
)(psinfo_t
*, struct field
*, const char *))
2084 for (f
= fields
; f
!= NULL
; f
= f
->next
) {
2085 print_fld(psinfo
, f
, ttyp
);
2086 if (f
->next
!= NULL
)
2089 (void) printf("\n");
2093 * Returns 1 if arg is found in array arr, of length num; 0 otherwise.
2096 search(pid_t
*arr
, int number
, pid_t arg
)
2100 for (i
= 0; i
< number
; i
++)
2107 * Add an entry (user, group) to the specified table.
2110 add_ugentry(struct ughead
*tbl
, char *name
)
2112 struct ugdata
*entp
;
2114 if (tbl
->size
== tbl
->nent
) { /* reallocate the table entries */
2115 if ((tbl
->size
*= 2) == 0)
2116 tbl
->size
= 32; /* first time */
2117 tbl
->ent
= Realloc(tbl
->ent
, tbl
->size
*sizeof (struct ugdata
));
2119 entp
= &tbl
->ent
[tbl
->nent
++];
2121 (void) strncpy(entp
->name
, name
, MAXUGNAME
);
2122 entp
->name
[MAXUGNAME
] = '\0';
2126 uconv(struct ughead
*uhead
)
2128 struct ugdata
*utbl
= uhead
->ent
;
2129 int n
= uhead
->nent
;
2136 * Ask the name service for names.
2138 for (i
= 0; i
< n
; i
++) {
2140 * If name is numeric, ask for numeric id
2142 if (str2uid(utbl
[i
].name
, &uid
, 0, MAXEPHUID
) == 0)
2143 pwd
= getpwuid(uid
);
2145 pwd
= getpwnam(utbl
[i
].name
);
2148 * If found, enter found index into tbl array.
2151 (void) fprintf(stderr
,
2152 gettext("ps: unknown user %s\n"), utbl
[i
].name
);
2156 utbl
[fnd
].id
= pwd
->pw_uid
;
2157 (void) strncpy(utbl
[fnd
].name
, pwd
->pw_name
, MAXUGNAME
);
2161 uhead
->nent
= fnd
; /* in case it changed */
2166 gconv(struct ughead
*ghead
)
2168 struct ugdata
*gtbl
= ghead
->ent
;
2169 int n
= ghead
->nent
;
2176 * Ask the name service for names.
2178 for (i
= 0; i
< n
; i
++) {
2180 * If name is numeric, ask for numeric id
2182 if (str2uid(gtbl
[i
].name
, (uid_t
*)&gid
, 0, MAXEPHUID
) == 0)
2183 grp
= getgrgid(gid
);
2185 grp
= getgrnam(gtbl
[i
].name
);
2187 * If found, enter found index into tbl array.
2190 (void) fprintf(stderr
,
2191 gettext("ps: unknown group %s\n"), gtbl
[i
].name
);
2195 gtbl
[fnd
].id
= grp
->gr_gid
;
2196 (void) strncpy(gtbl
[fnd
].name
, grp
->gr_name
, MAXUGNAME
);
2200 ghead
->nent
= fnd
; /* in case it changed */
2205 * Return 1 if puid is in table, otherwise 0.
2208 ugfind(id_t id
, struct ughead
*ughead
)
2210 struct ugdata
*utbl
= ughead
->ent
;
2211 int n
= ughead
->nent
;
2214 for (i
= 0; i
< n
; i
++)
2215 if (utbl
[i
].id
== id
)
2221 * Print starting time of process unless process started more than 24 hours
2222 * ago, in which case the date is printed. The date is printed in the form
2223 * "MMM dd" if old format, else the blank is replaced with an '_' so
2224 * it appears as a single word (for parseability).
2227 prtime(timestruc_t st
, int width
, int old
)
2232 starttime
= st
.tv_sec
;
2233 if (st
.tv_nsec
> 500000000)
2235 if ((now
.tv_sec
- starttime
) >= 24*60*60) {
2236 (void) strftime(sttim
, sizeof (sttim
), old
?
2239 * This time format is used by STIME field when -f option
2240 * is specified. Used for processes that begun more than
2243 dcgettext(NULL
, "%b %d", LC_TIME
) :
2246 * This time format is used by STIME field when -o option
2247 * is specified. Used for processes that begun more than
2250 dcgettext(NULL
, "%b_%d", LC_TIME
), localtime(&starttime
));
2254 * This time format is used by STIME field when -f or -o option
2255 * is specified. Used for processes that begun less than
2258 (void) strftime(sttim
, sizeof (sttim
),
2259 dcgettext(NULL
, "%H:%M:%S", LC_TIME
),
2260 localtime(&starttime
));
2262 (void) printf("%*.*s", width
, width
, sttim
);
2266 przom(psinfo_t
*psinfo
)
2270 char zonename
[ZONENAME_MAX
];
2273 * All fields before 'PID' are printed with a trailing space as a
2274 * spearator, rather than keeping track of which column is first. All
2275 * other fields are printed with a leading space.
2277 if (lflg
) { /* F S */
2279 (void) printf("%2x ", psinfo
->pr_flag
& 0377); /* F */
2280 (void) printf("%c ", psinfo
->pr_lwp
.pr_sname
); /* S */
2283 if (getzonenamebyid(psinfo
->pr_zoneid
, zonename
,
2284 sizeof (zonename
)) < 0) {
2285 if (snprintf(NULL
, 0, "%d",
2286 ((int)psinfo
->pr_zoneid
)) > 7)
2287 (void) printf(" %6.6d%c ",
2288 ((int)psinfo
->pr_zoneid
), '*');
2290 (void) printf(" %7.7d ",
2291 ((int)psinfo
->pr_zoneid
));
2295 nw
= mbstowcs(NULL
, zonename
, 0);
2296 if (nw
== (size_t)-1)
2297 (void) printf("%8.8s ", "ERROR");
2299 (void) wprintf(L
"%7.7s%c ", zonename
, '*');
2301 (void) wprintf(L
"%8.8s ", zonename
);
2305 /* Display home lgroup */
2306 (void) printf(" %6d", (int)psinfo
->pr_lwp
.pr_lgrp
); /* LGRP */
2309 if ((pwd
= getpwuid(psinfo
->pr_euid
)) != NULL
) {
2312 nw
= mbstowcs(NULL
, pwd
->pw_name
, 0);
2313 if (nw
== (size_t)-1)
2314 (void) printf("%8.8s ", "ERROR");
2316 (void) wprintf(L
"%7.7s%c ", pwd
->pw_name
, '*');
2318 (void) wprintf(L
"%8.8s ", pwd
->pw_name
);
2320 if (snprintf(NULL
, 0, "%u",
2321 (psinfo
->pr_euid
)) > 7)
2322 (void) printf(" %6.6u%c ", psinfo
->pr_euid
,
2325 (void) printf(" %7.7u ", psinfo
->pr_euid
);
2328 if (snprintf(NULL
, 0, "%u", (psinfo
->pr_euid
)) > 6)
2329 (void) printf("%5.5u%c ", psinfo
->pr_euid
, '*');
2331 (void) printf("%6u ", psinfo
->pr_euid
);
2334 (void) printf("%*d", pidwidth
, (int)psinfo
->pr_pid
); /* PID */
2336 (void) printf(" %*d", pidwidth
,
2337 (int)psinfo
->pr_ppid
); /* PPID */
2340 (void) printf(" %*d", pidwidth
,
2341 (int)psinfo
->pr_pgid
); /* PGID */
2342 (void) printf(" %*d", pidwidth
,
2343 (int)psinfo
->pr_sid
); /* SID */
2347 (void) printf(" %5d", 0); /* LWP */
2349 (void) printf(" -"); /* PSR */
2351 (void) printf(" %5d", 0); /* NLWP */
2354 (void) printf(" %4s", "-"); /* zombies have no class */
2355 (void) printf(" %3d", psinfo
->pr_lwp
.pr_pri
); /* PRI */
2356 } else if (lflg
|| fflg
) {
2357 (void) printf(" %3d", psinfo
->pr_lwp
.pr_cpu
& 0377); /* C */
2359 (void) printf(" %3d %2s",
2360 psinfo
->pr_lwp
.pr_oldpri
, "-"); /* PRI NI */
2363 if (yflg
) /* RSS SZ WCHAN */
2364 (void) printf(" %5d %6d %8s", 0, 0, "-");
2365 else /* ADDR SZ WCHAN */
2366 (void) printf(" %8s %6d %8s", "-", 0, "-");
2369 int width
= fname
[F_STIME
].width
;
2370 (void) printf(" %*.*s", width
, width
, "-"); /* STIME */
2372 (void) printf(" %-8.14s", "?"); /* TTY */
2374 tm
= psinfo
->pr_time
.tv_sec
;
2375 if (psinfo
->pr_time
.tv_nsec
> 500000000)
2377 (void) printf(" %4ld:%.2ld", tm
/ 60, tm
% 60); /* TIME */
2378 (void) printf(" <defunct>\n");
2382 * Function to compute the number of printable bytes in a multibyte
2383 * command string ("internationalization").
2386 namencnt(char *cmd
, int csisize
, int scrsize
)
2388 int csiwcnt
= 0, scrwcnt
= 0;
2393 while (*cmd
!= '\0') {
2394 if ((len
= csisize
- csiwcnt
) > (int)MB_CUR_MAX
)
2396 if ((ncsisz
= mbtowc(&wchar
, cmd
, len
)) < 0)
2397 return (8); /* default to use for illegal chars */
2398 if ((nscrsz
= wcwidth(wchar
)) <= 0)
2400 if (csiwcnt
+ ncsisz
> csisize
|| scrwcnt
+ nscrsz
> scrsize
)
2412 static char buf
[32];
2413 char *str
= strerror(err
);
2416 (void) snprintf(str
= buf
, sizeof (buf
), "Errno #%d", err
);
2421 /* If allocation fails, die */
2423 Realloc(void *ptr
, size_t size
)
2425 ptr
= realloc(ptr
, size
);
2427 (void) fprintf(stderr
, gettext("ps: no memory\n"));
2434 delta_secs(const timestruc_t
*start
)
2436 time_t seconds
= now
.tv_sec
- start
->tv_sec
;
2437 long nanosecs
= now
.tv_usec
* 1000 - start
->tv_nsec
;
2439 if (nanosecs
>= (NANOSEC
/ 2))
2441 else if (nanosecs
< -(NANOSEC
/ 2))
2448 * Returns the following:
2451 * EINVAL Invalid number
2452 * ERANGE Value exceeds (min, max) range
2455 str2id(const char *p
, pid_t
*val
, long min
, long max
)
2462 number
= strtol(p
, &q
, 10);
2464 if (errno
!= 0 || q
== p
|| *q
!= '\0') {
2465 if ((error
= errno
) == 0) {
2467 * strtol() can fail without setting errno, or it can
2468 * set it to EINVAL or ERANGE. In the case errno is
2469 * still zero, return EINVAL.
2473 } else if (number
< min
|| number
> max
) {
2485 * Returns the following:
2488 * EINVAL Invalid number
2489 * ERANGE Value exceeds (min, max) range
2492 str2uid(const char *p
, uid_t
*val
, unsigned long min
, unsigned long max
)
2495 unsigned long number
;
2499 number
= strtoul(p
, &q
, 10);
2501 if (errno
!= 0 || q
== p
|| *q
!= '\0') {
2502 if ((error
= errno
) == 0) {
2504 * strtoul() can fail without setting errno, or it can
2505 * set it to EINVAL or ERANGE. In the case errno is
2506 * still zero, return EINVAL.
2510 } else if (number
< min
|| number
> max
) {
2522 pidcmp(const void *p1
, const void *p2
)
2524 pid_t i
= *((pid_t
*)p1
);
2525 pid_t j
= *((pid_t
*)p2
);