/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2013 Gary Mills * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * ps -- print things about processes. */ #include <stdio.h> #include <ctype.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <pwd.h> #include <grp.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mkdev.h> #include <unistd.h> #include <stdlib.h> #include <limits.h> #include <dirent.h> #include <sys/signal.h> #include <sys/fault.h> #include <sys/syscall.h> #include <sys/time.h> #include <procfs.h> #include <locale.h> #include <wctype.h> #include <wchar.h> #include <libw.h> #include <stdarg.h> #include <sys/proc.h> #include <sys/pset.h> #include <project.h> #include <zone.h> #define min(a, b) ((a) > (b) ? (b) : (a)) #define max(a, b) ((a) < (b) ? (b) : (a)) #define NTTYS 20 /* initial size of table for -t option */ #define SIZ 30 /* initial size of tables for -p, -s, -g, -h and -z */ /* * Size of buffer holding args for t, p, s, g, u, U, G, z options. * Set to ZONENAME_MAX, the minimum value needed to allow any * zone to be specified. */ #define ARGSIZ ZONENAME_MAX /* Max chars in a user/group name or printed u/g id */ #define MAXUGNAME (LOGNAME_MAX+2) /* Structure for storing user or group info */ struct ugdata { id_t id; /* numeric user-id or group-id */ char name[MAXUGNAME+1]; /* user/group name, null terminated */ }; struct ughead { size_t size; /* number of ugdata structs allocated */ size_t nent; /* number of active entries */ struct ugdata *ent; /* pointer to array of actual entries */ }; enum fname { /* enumeration of field names */ F_USER, /* effective user of the process */ F_RUSER, /* real user of the process */ F_GROUP, /* effective group of the process */ F_RGROUP, /* real group of the process */ F_UID, /* numeric effective uid of the process */ F_RUID, /* numeric real uid of the process */ F_GID, /* numeric effective gid of the process */ F_RGID, /* numeric real gid of the process */ F_PID, /* process id */ F_PPID, /* parent process id */ F_PGID, /* process group id */ F_SID, /* session id */ F_PSR, /* bound processor */ F_LWP, /* lwp-id */ F_NLWP, /* number of lwps */ F_OPRI, /* old priority (obsolete) */ F_PRI, /* new priority */ F_F, /* process flags */ F_S, /* letter indicating the state */ F_C, /* processor utilization (obsolete) */ F_PCPU, /* percent of recently used cpu time */ F_PMEM, /* percent of physical memory used (rss) */ F_OSZ, /* virtual size of the process in pages */ F_VSZ, /* virtual size of the process in kilobytes */ F_RSS, /* resident set size of the process in kilobytes */ F_NICE, /* "nice" value of the process */ F_CLASS, /* scheduler class */ F_STIME, /* start time of the process, hh:mm:ss or Month Day */ F_ETIME, /* elapsed time of the process, [[dd-]hh:]mm:ss */ F_TIME, /* cpu time of the process, [[dd-]hh:]mm:ss */ F_TTY, /* name of the controlling terminal */ F_ADDR, /* address of the process (obsolete) */ F_WCHAN, /* wait channel (sleep condition variable) */ F_FNAME, /* file name of command */ F_COMM, /* name of command (argv[0] value) */ F_ARGS, /* name of command plus all its arguments */ F_TASKID, /* task id */ F_PROJID, /* project id */ F_PROJECT, /* project name of the process */ F_PSET, /* bound processor set */ F_ZONE, /* zone name */ F_ZONEID, /* zone id */ F_CTID, /* process contract id */ F_LGRP, /* process home lgroup */ F_DMODEL /* process data model */ }; struct field { struct field *next; /* linked list */ int fname; /* field index */ const char *header; /* header to use */ int width; /* width of field */ }; static struct field *fields = NULL; /* fields selected via -o */ static struct field *last_field = NULL; static int do_header = 0; static struct timeval now; /* array of defined fields, in fname order */ struct def_field { const char *fname; const char *header; int width; int minwidth; }; static struct def_field fname[] = { /* fname header width minwidth */ { "user", "USER", 8, 8 }, { "ruser", "RUSER", 8, 8 }, { "group", "GROUP", 8, 8 }, { "rgroup", "RGROUP", 8, 8 }, { "uid", "UID", 5, 5 }, { "ruid", "RUID", 5, 5 }, { "gid", "GID", 5, 5 }, { "rgid", "RGID", 5, 5 }, { "pid", "PID", 5, 5 }, { "ppid", "PPID", 5, 5 }, { "pgid", "PGID", 5, 5 }, { "sid", "SID", 5, 5 }, { "psr", "PSR", 3, 2 }, { "lwp", "LWP", 6, 2 }, { "nlwp", "NLWP", 4, 2 }, { "opri", "PRI", 3, 2 }, { "pri", "PRI", 3, 2 }, { "f", "F", 2, 2 }, { "s", "S", 1, 1 }, { "c", "C", 2, 2 }, { "pcpu", "%CPU", 4, 4 }, { "pmem", "%MEM", 4, 4 }, { "osz", "SZ", 4, 4 }, { "vsz", "VSZ", 4, 4 }, { "rss", "RSS", 4, 4 }, { "nice", "NI", 2, 2 }, { "class", "CLS", 4, 2 }, { "stime", "STIME", 8, 8 }, { "etime", "ELAPSED", 11, 7 }, { "time", "TIME", 11, 5 }, { "tty", "TT", 7, 7 }, #ifdef _LP64 { "addr", "ADDR", 16, 8 }, { "wchan", "WCHAN", 16, 8 }, #else { "addr", "ADDR", 8, 8 }, { "wchan", "WCHAN", 8, 8 }, #endif { "fname", "COMMAND", 8, 8 }, { "comm", "COMMAND", 80, 8 }, { "args", "COMMAND", 80, 80 }, { "taskid", "TASKID", 5, 5 }, { "projid", "PROJID", 5, 5 }, { "project", "PROJECT", 8, 8 }, { "pset", "PSET", 3, 3 }, { "zone", "ZONE", 8, 8 }, { "zoneid", "ZONEID", 5, 5 }, { "ctid", "CTID", 5, 5 }, { "lgrp", "LGRP", 4, 2 }, { "dmodel", "DMODEL", 6, 6 }, }; #define NFIELDS (sizeof (fname) / sizeof (fname[0])) static int retcode = 1; static int lflg; static int Aflg; static int uflg; static int Uflg; static int Gflg; static int aflg; static int dflg; static int Lflg; static int Pflg; static int Wflg; static int yflg; static int pflg; static int fflg; static int cflg; static int jflg; static int gflg; static int sflg; static int tflg; static int zflg; static int Zflg; static int hflg; static int Hflg; static uid_t tuid = (uid_t)-1; static int errflg; static int ndev; /* number of devices */ static int maxdev; /* number of devl structures allocated */ #define DNINCR 100 #define DNSIZE 14 static struct devl { /* device list */ char dname[DNSIZE]; /* device name */ dev_t ddev; /* device number */ } *devl; static struct tty { char *tname; dev_t tdev; } *tty = NULL; /* for t option */ static size_t ttysz = 0; static int ntty = 0; static pid_t *pid = NULL; /* for p option */ static size_t pidsz = 0; static size_t npid = 0; static int *lgrps = NULL; /* list of lgroup IDs for for h option */ static size_t lgrps_size = 0; /* size of the lgrps list */ static size_t nlgrps = 0; /* number elements in the list */ /* Maximum possible lgroup ID value */ #define MAX_LGRP_ID 256 static pid_t *grpid = NULL; /* for g option */ static size_t grpidsz = 0; static int ngrpid = 0; static pid_t *sessid = NULL; /* for s option */ static size_t sessidsz = 0; static int nsessid = 0; static zoneid_t *zoneid = NULL; /* for z option */ static size_t zoneidsz = 0; static int nzoneid = 0; static int kbytes_per_page; static int pidwidth; static char *procdir = "/proc"; /* standard /proc directory */ static struct ughead euid_tbl; /* table to store selected euid's */ static struct ughead ruid_tbl; /* table to store selected real uid's */ static struct ughead egid_tbl; /* table to store selected egid's */ static struct ughead rgid_tbl; /* table to store selected real gid's */ static prheader_t *lpsinfobuf; /* buffer to contain lpsinfo */ static size_t lpbufsize; /* * This constant defines the sentinal number of process IDs below which we * only examine individual entries in /proc rather than scanning through * /proc. This optimization is a huge win in the common case. */ #define PTHRESHOLD 40 #define UCB_OPTS "-aceglnrtuvwxSU" static void usage(void); static char *getarg(char **); static char *parse_format(char *); static char *gettty(psinfo_t *); static int prfind(int, psinfo_t *, char **); static void prcom(psinfo_t *, char *); static void prtpct(ushort_t, int); static void print_time(time_t, int); static void print_field(psinfo_t *, struct field *, const char *); static void print_zombie_field(psinfo_t *, struct field *, const char *); static void pr_fields(psinfo_t *, const char *, void (*print_fld)(psinfo_t *, struct field *, const char *)); static int search(pid_t *, int, pid_t); static void add_ugentry(struct ughead *, char *); static int uconv(struct ughead *); static int gconv(struct ughead *); static int ugfind(id_t, struct ughead *); static void prtime(timestruc_t, int, int); static void przom(psinfo_t *); static int namencnt(char *, int, int); static char *err_string(int); static int print_proc(char *pname); static time_t delta_secs(const timestruc_t *); static int str2id(const char *, pid_t *, long, long); static int str2uid(const char *, uid_t *, unsigned long, unsigned long); static void *Realloc(void *, size_t); static int pidcmp(const void *p1, const void *p2); extern int ucbmain(int, char **); static int stdmain(int, char **); int main(int argc, char **argv) { const char *me; /* * The original two ps'es are linked in a single binary; * their main()s are renamed to stdmain for /usr/bin/ps and * ucbmain for /usr/ucb/ps. * We try to figure out which instance of ps the user wants to run. * Traditionally, the UCB variant doesn't require the flag argument * start with a "-". If the first argument doesn't start with a * "-", we call "ucbmain". * If there's a first argument and it starts with a "-", we check * whether any of the options isn't acceptable to "ucbmain"; in that * case we run "stdmain". * If we can't tell from the options which main to call, we check * the binary we are running. We default to "stdmain" but * any mention in the executable name of "ucb" causes us to call * ucbmain. */ if (argv[1] != NULL) { if (argv[1][0] != '-') return (ucbmain(argc, argv)); else if (argv[1][strspn(argv[1], UCB_OPTS)] != '\0') return (stdmain(argc, argv)); } me = getexecname(); if (me != NULL && strstr(me, "ucb") != NULL) return (ucbmain(argc, argv)); else return (stdmain(argc, argv)); } static int stdmain(int argc, char **argv) { char *p; char *p1; char *parg; int c; int i; int pgerrflg = 0; /* err flg: non-numeric arg w/p & g options */ size_t size, len; DIR *dirp; struct dirent *dentp; pid_t maxpid; pid_t id; int ret; char loc_stime_str[32]; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ #endif (void) textdomain(TEXT_DOMAIN); (void) memset(&euid_tbl, 0, sizeof (euid_tbl)); (void) memset(&ruid_tbl, 0, sizeof (ruid_tbl)); (void) memset(&egid_tbl, 0, sizeof (egid_tbl)); (void) memset(&rgid_tbl, 0, sizeof (rgid_tbl)); kbytes_per_page = sysconf(_SC_PAGESIZE) / 1024; (void) gettimeofday(&now, NULL); /* * calculate width of pid fields based on configured MAXPID * (must be at least 5 to retain output format compatibility) */ id = maxpid = (pid_t)sysconf(_SC_MAXPID); pidwidth = 1; while ((id /= 10) > 0) ++pidwidth; pidwidth = pidwidth < 5 ? 5 : pidwidth; fname[F_PID].width = fname[F_PPID].width = pidwidth; fname[F_PGID].width = fname[F_SID].width = pidwidth; /* * TRANSLATION_NOTE * Specify the printf format with width and precision for * the STIME field. */ len = snprintf(loc_stime_str, sizeof (loc_stime_str), dcgettext(NULL, "%8.8s", LC_TIME), "STIME"); if (len >= sizeof (loc_stime_str)) len = sizeof (loc_stime_str) - 1; fname[F_STIME].width = fname[F_STIME].minwidth = len; while ((c = getopt(argc, argv, "jlfceAadLPWyZHh:t:p:g:u:U:G:n:s:o:z:")) != EOF) switch (c) { case 'H': /* Show home lgroups */ Hflg++; break; case 'h': /* * Show processes/threads with given home lgroups */ hflg++; p1 = optarg; do { int id; /* * Get all IDs in the list, verify for * correctness and place in lgrps array. */ parg = getarg(&p1); /* Convert string to integer */ ret = str2id(parg, (pid_t *)&id, 0, MAX_LGRP_ID); /* Complain if ID didn't parse correctly */ if (ret != 0) { pgerrflg++; (void) fprintf(stderr, gettext("ps: %s "), parg); if (ret == EINVAL) (void) fprintf(stderr, gettext("is an invalid " "non-numeric argument")); else (void) fprintf(stderr, gettext("exceeds valid " "range")); (void) fprintf(stderr, gettext(" for -h option\n")); continue; } /* Extend lgrps array if needed */ if (nlgrps == lgrps_size) { /* Double the size of the lgrps array */ if (lgrps_size == 0) lgrps_size = SIZ; lgrps_size *= 2; lgrps = Realloc(lgrps, lgrps_size * sizeof (int)); } /* place the id in the lgrps table */ lgrps[nlgrps++] = id; } while (*p1); break; case 'l': /* long listing */ lflg++; break; case 'f': /* full listing */ fflg++; break; case 'j': jflg++; break; case 'c': /* * Format output to reflect scheduler changes: * high numbers for high priorities and don't * print nice or p_cpu values. 'c' option only * effective when used with 'l' or 'f' options. */ cflg++; break; case 'A': /* list every process */ case 'e': /* (obsolete) list every process */ Aflg++; tflg = Gflg = Uflg = uflg = pflg = gflg = sflg = 0; zflg = hflg = 0; break; case 'a': /* * Same as 'e' except no session group leaders * and no non-terminal processes. */ aflg++; break; case 'd': /* same as e except no session leaders */ dflg++; break; case 'L': /* show lwps */ Lflg++; break; case 'P': /* show bound processor */ Pflg++; break; case 'W': /* truncate long names */ Wflg++; break; case 'y': /* omit F & ADDR, report RSS & SZ in Kby */ yflg++; break; case 'n': /* no longer needed; retain as no-op */ (void) fprintf(stderr, gettext("ps: warning: -n option ignored\n")); break; case 't': /* terminals */ #define TSZ 30 tflg++; p1 = optarg; do { char nambuf[TSZ+6]; /* for "/dev/" + '\0' */ struct stat64 s; parg = getarg(&p1); p = Realloc(NULL, TSZ+1); /* for '\0' */ /* zero the buffer before using it */ p[0] = '\0'; size = TSZ; if (isdigit(*parg)) { (void) strcpy(p, "tty"); size -= 3; } (void) strncat(p, parg, size); if (ntty == ttysz) { if ((ttysz *= 2) == 0) ttysz = NTTYS; tty = Realloc(tty, (ttysz + 1) * sizeof (struct tty)); } tty[ntty].tdev = PRNODEV; (void) strcpy(nambuf, "/dev/"); (void) strcat(nambuf, p); if (stat64(nambuf, &s) == 0) tty[ntty].tdev = s.st_rdev; tty[ntty++].tname = p; } while (*p1); break; case 'p': /* proc ids */ pflg++; p1 = optarg; do { pid_t id; parg = getarg(&p1); if ((ret = str2id(parg, &id, 0, maxpid)) != 0) { pgerrflg++; (void) fprintf(stderr, gettext("ps: %s "), parg); if (ret == EINVAL) (void) fprintf(stderr, gettext("is an invalid " "non-numeric argument")); else (void) fprintf(stderr, gettext("exceeds valid " "range")); (void) fprintf(stderr, gettext(" for -p option\n")); continue; } if (npid == pidsz) { if ((pidsz *= 2) == 0) pidsz = SIZ; pid = Realloc(pid, pidsz * sizeof (pid_t)); } pid[npid++] = id; } while (*p1); break; case 's': /* session */ sflg++; p1 = optarg; do { pid_t id; parg = getarg(&p1); if ((ret = str2id(parg, &id, 0, maxpid)) != 0) { pgerrflg++; (void) fprintf(stderr, gettext("ps: %s "), parg); if (ret == EINVAL) (void) fprintf(stderr, gettext("is an invalid " "non-numeric argument")); else (void) fprintf(stderr, gettext("exceeds valid " "range")); (void) fprintf(stderr, gettext(" for -s option\n")); continue; } if (nsessid == sessidsz) { if ((sessidsz *= 2) == 0) sessidsz = SIZ; sessid = Realloc(sessid, sessidsz * sizeof (pid_t)); } sessid[nsessid++] = id; } while (*p1); break; case 'g': /* proc group */ gflg++; p1 = optarg; do { pid_t id; parg = getarg(&p1); if ((ret = str2id(parg, &id, 0, maxpid)) != 0) { pgerrflg++; (void) fprintf(stderr, gettext("ps: %s "), parg); if (ret == EINVAL) (void) fprintf(stderr, gettext("is an invalid " "non-numeric argument")); else (void) fprintf(stderr, gettext("exceeds valid " "range")); (void) fprintf(stderr, gettext(" for -g option\n")); continue; } if (ngrpid == grpidsz) { if ((grpidsz *= 2) == 0) grpidsz = SIZ; grpid = Realloc(grpid, grpidsz * sizeof (pid_t)); } grpid[ngrpid++] = id; } while (*p1); break; case 'u': /* effective user name or number */ uflg++; p1 = optarg; do { parg = getarg(&p1); add_ugentry(&euid_tbl, parg); } while (*p1); break; case 'U': /* real user name or number */ Uflg++; p1 = optarg; do { parg = getarg(&p1); add_ugentry(&ruid_tbl, parg); } while (*p1); break; case 'G': /* real group name or number */ Gflg++; p1 = optarg; do { parg = getarg(&p1); add_ugentry(&rgid_tbl, parg); } while (*p1); break; case 'o': /* output format */ p = optarg; while ((p = parse_format(p)) != NULL) ; break; case 'z': /* zone name or number */ zflg++; p1 = optarg; do { zoneid_t id; parg = getarg(&p1); if (zone_get_id(parg, &id) != 0) { pgerrflg++; (void) fprintf(stderr, gettext("ps: unknown zone %s\n"), parg); continue; } if (nzoneid == zoneidsz) { if ((zoneidsz *= 2) == 0) zoneidsz = SIZ; zoneid = Realloc(zoneid, zoneidsz * sizeof (zoneid_t)); } zoneid[nzoneid++] = id; } while (*p1); break; case 'Z': /* show zone name */ Zflg++; break; default: /* error on ? */ errflg++; break; } if (errflg || optind < argc || pgerrflg) usage(); if (tflg) tty[ntty].tname = NULL; /* * If an appropriate option has not been specified, use the * current terminal and effective uid as the default. */ if (!(aflg|Aflg|dflg|Gflg|hflg|Uflg|uflg|tflg|pflg|gflg|sflg|zflg)) { psinfo_t info; int procfd; char *name; char pname[100]; /* get our own controlling tty name using /proc */ (void) snprintf(pname, sizeof (pname), "%s/self/psinfo", procdir); if ((procfd = open(pname, O_RDONLY)) < 0 || read(procfd, (char *)&info, sizeof (info)) < 0 || info.pr_ttydev == PRNODEV) { (void) fprintf(stderr, gettext("ps: no controlling terminal\n")); exit(1); } (void) close(procfd); i = 0; name = gettty(&info); if (*name == '?') { (void) fprintf(stderr, gettext("ps: can't find controlling terminal\n")); exit(1); } if (ntty == ttysz) { if ((ttysz *= 2) == 0) ttysz = NTTYS; tty = Realloc(tty, (ttysz + 1) * sizeof (struct tty)); } tty[ntty].tdev = info.pr_ttydev; tty[ntty++].tname = name; tty[ntty].tname = NULL; tflg++; tuid = getuid(); } if (Aflg) { Gflg = Uflg = uflg = pflg = sflg = gflg = aflg = dflg = 0; zflg = hflg = 0; } if (Aflg | aflg | dflg) tflg = 0; i = 0; /* prepare to exit on name lookup errors */ i += uconv(&euid_tbl); i += uconv(&ruid_tbl); i += gconv(&egid_tbl); i += gconv(&rgid_tbl); if (i) exit(1); /* allocate a buffer for lwpsinfo structures */ lpbufsize = 4096; if (Lflg && (lpsinfobuf = malloc(lpbufsize)) == NULL) { (void) fprintf(stderr, gettext("ps: no memory\n")); exit(1); } if (fields) { /* print user-specified header */ if (do_header) { struct field *f; for (f = fields; f != NULL; f = f->next) { if (f != fields) (void) printf(" "); switch (f->fname) { case F_TTY: (void) printf("%-*s", f->width, f->header); break; case F_FNAME: case F_COMM: case F_ARGS: /* * Print these headers full width * unless they appear at the end. */ if (f->next != NULL) { (void) printf("%-*s", f->width, f->header); } else { (void) printf("%s", f->header); } break; default: (void) printf("%*s", f->width, f->header); break; } } (void) printf("\n"); } } else { /* print standard header */ /* * All fields before 'PID' are printed with a trailing space * as a separator and that is how we print the headers too. */ if (lflg) { if (yflg) (void) printf("S "); else (void) printf(" F S "); } if (Zflg) (void) printf(" ZONE "); if (fflg) { (void) printf(" UID "); } else if (lflg) (void) printf(" UID "); (void) printf("%*s", pidwidth, "PID"); if (lflg || fflg) (void) printf(" %*s", pidwidth, "PPID"); if (jflg) (void) printf(" %*s %*s", pidwidth, "PGID", pidwidth, "SID"); if (Lflg) (void) printf(" LWP"); if (Pflg) (void) printf(" PSR"); if (Lflg && fflg) (void) printf(" NLWP"); if (cflg) (void) printf(" CLS PRI"); else if (lflg || fflg) { (void) printf(" C"); if (lflg) (void) printf(" PRI NI"); } if (lflg) { if (yflg) (void) printf(" RSS SZ WCHAN"); else (void) printf(" ADDR SZ WCHAN"); } if (fflg) (void) printf(" %s", loc_stime_str); if (Hflg) (void) printf(" LGRP"); if (Lflg) (void) printf(" TTY LTIME CMD\n"); else (void) printf(" TTY TIME CMD\n"); } if (pflg && !(aflg|Aflg|dflg|Gflg|Uflg|uflg|hflg|tflg|gflg|sflg|zflg) && npid <= PTHRESHOLD) { /* * If we are looking at specific processes go straight * to their /proc entries and don't scan /proc. */ int i; (void) qsort(pid, npid, sizeof (pid_t), pidcmp); for (i = 0; i < npid; i++) { char pname[12]; if (i >= 1 && pid[i] == pid[i - 1]) continue; (void) sprintf(pname, "%d", (int)pid[i]); if (print_proc(pname) == 0) retcode = 0; } } else { /* * Determine which processes to print info about by searching * the /proc directory and looking at each process. */ if ((dirp = opendir(procdir)) == NULL) { (void) fprintf(stderr, gettext("ps: cannot open PROC directory %s\n"), procdir); exit(1); } /* for each active process --- */ while ((dentp = readdir(dirp)) != NULL) { if (dentp->d_name[0] == '.') /* skip . and .. */ continue; if (print_proc(dentp->d_name) == 0) retcode = 0; } (void) closedir(dirp); } return (retcode); } int print_proc(char *pid_name) { char pname[PATH_MAX]; int pdlen; int found; int procfd; /* filedescriptor for /proc/nnnnn/psinfo */ char *tp; /* ptr to ttyname, if any */ psinfo_t info; /* process information from /proc */ lwpsinfo_t *lwpsinfo; /* array of lwpsinfo structs */ pdlen = snprintf(pname, sizeof (pname), "%s/%s/", procdir, pid_name); if (pdlen >= sizeof (pname) - 10) return (1); retry: (void) strcpy(&pname[pdlen], "psinfo"); if ((procfd = open(pname, O_RDONLY)) == -1) { /* Process may have exited meanwhile. */ return (1); } /* * Get the info structure for the process and close quickly. */ if (read(procfd, (char *)&info, sizeof (info)) < 0) { int saverr = errno; (void) close(procfd); if (saverr == EAGAIN) goto retry; if (saverr != ENOENT) (void) fprintf(stderr, gettext("ps: read() on %s: %s\n"), pname, err_string(saverr)); return (1); } (void) close(procfd); found = 0; if (info.pr_lwp.pr_state == 0) /* can't happen? */ return (1); /* * Omit session group leaders for 'a' and 'd' options. */ if ((info.pr_pid == info.pr_sid) && (dflg || aflg)) return (1); if (Aflg || dflg) found++; else if (pflg && search(pid, npid, info.pr_pid)) found++; /* ppid in p option arg list */ else if (uflg && ugfind((id_t)info.pr_euid, &euid_tbl)) found++; /* puid in u option arg list */ else if (Uflg && ugfind((id_t)info.pr_uid, &ruid_tbl)) found++; /* puid in U option arg list */ #ifdef NOT_YET else if (gflg && ugfind((id_t)info.pr_egid, &egid_tbl)) found++; /* pgid in g option arg list */ #endif /* NOT_YET */ else if (Gflg && ugfind((id_t)info.pr_gid, &rgid_tbl)) found++; /* pgid in G option arg list */ else if (gflg && search(grpid, ngrpid, info.pr_pgid)) found++; /* grpid in g option arg list */ else if (sflg && search(sessid, nsessid, info.pr_sid)) found++; /* sessid in s option arg list */ else if (zflg && search(zoneid, nzoneid, info.pr_zoneid)) found++; /* zoneid in z option arg list */ else if (hflg && search((pid_t *)lgrps, nlgrps, info.pr_lwp.pr_lgrp)) found++; /* home lgroup in h option arg list */ if (!found && !tflg && !aflg) return (1); if (!prfind(found, &info, &tp)) return (1); if (Lflg && (info.pr_nlwp + info.pr_nzomb) > 1) { ssize_t prsz; (void) strcpy(&pname[pdlen], "lpsinfo"); if ((procfd = open(pname, O_RDONLY)) == -1) return (1); /* * Get the info structures for the lwps. */ prsz = read(procfd, lpsinfobuf, lpbufsize); if (prsz == -1) { int saverr = errno; (void) close(procfd); if (saverr == EAGAIN) goto retry; if (saverr != ENOENT) (void) fprintf(stderr, gettext("ps: read() on %s: %s\n"), pname, err_string(saverr)); return (1); } (void) close(procfd); if (prsz == lpbufsize) { /* * buffer overflow. Realloc new buffer. * Error handling is done in Realloc(). */ lpbufsize *= 2; lpsinfobuf = Realloc(lpsinfobuf, lpbufsize); goto retry; } if (lpsinfobuf->pr_nent != (info.pr_nlwp + info.pr_nzomb)) goto retry; lwpsinfo = (lwpsinfo_t *)(lpsinfobuf + 1); } if (!Lflg || (info.pr_nlwp + info.pr_nzomb) <= 1) { prcom(&info, tp); } else { int nlwp = 0; do { info.pr_lwp = *lwpsinfo; prcom(&info, tp); /* LINTED improper alignment */ lwpsinfo = (lwpsinfo_t *)((char *)lwpsinfo + lpsinfobuf->pr_entsize); } while (++nlwp < lpsinfobuf->pr_nent); } return (0); } static int field_cmp(const void *l, const void *r) { struct def_field *lhs = *((struct def_field **)l); struct def_field *rhs = *((struct def_field **)r); return (strcmp(lhs->fname, rhs->fname)); } static void usage(void) /* print usage message and quit */ { struct def_field *df, *sorted[NFIELDS]; int pos = 80, i = 0; static char usage1[] = "ps [ -aAdefHlcjLPWyZ ] [ -o format ] [ -t termlist ]"; static char usage2[] = "\t[ -u userlist ] [ -U userlist ] [ -G grouplist ]"; static char usage3[] = "\t[ -p proclist ] [ -g pgrplist ] [ -s sidlist ]"; static char usage4[] = "\t[ -z zonelist ] [-h lgrplist]"; static char usage5[] = " 'format' is one or more of:"; (void) fprintf(stderr, gettext("usage: %s\n%s\n%s\n%s\n%s"), gettext(usage1), gettext(usage2), gettext(usage3), gettext(usage4), gettext(usage5)); /* * Now print out the possible output formats such that they neatly fit * into eighty columns. Note that the fact that we are determining * this output programmatically means that a gettext() is impossible -- * but it would be a mistake to localize the output formats anyway as * they are tokens for input, not output themselves. */ for (df = &fname[0]; df < &fname[NFIELDS]; df++) sorted[i++] = df; (void) qsort(sorted, NFIELDS, sizeof (void *), field_cmp); for (i = 0; i < NFIELDS; i++) { if (pos + strlen((df = sorted[i])->fname) + 1 >= 80) { (void) fprintf(stderr, "\n\t"); pos = 8; } (void) fprintf(stderr, "%s%s", pos > 8 ? " " : "", df->fname); pos += strlen(df->fname) + 1; } (void) fprintf(stderr, "\n"); exit(1); } /* * getarg() finds the next argument in list and copies arg into argbuf. * p1 first pts to arg passed back from getopt routine. p1 is then * bumped to next character that is not a comma or blank -- p1 NULL * indicates end of list. */ static char * getarg(char **pp1) { static char argbuf[ARGSIZ]; char *p1 = *pp1; char *parga = argbuf; int c; while ((c = *p1) != '\0' && (c == ',' || isspace(c))) p1++; while ((c = *p1) != '\0' && c != ',' && !isspace(c)) { if (parga < argbuf + ARGSIZ - 1) *parga++ = c; p1++; } *parga = '\0'; while ((c = *p1) != '\0' && (c == ',' || isspace(c))) p1++; *pp1 = p1; return (argbuf); } /* * parse_format() takes the argument to the -o option, * sets up the next output field structure, and returns * a pointer to any further output field specifier(s). * As a side-effect, it increments errflg if encounters a format error. */ static char * parse_format(char *arg) { int c; char *name; char *header = NULL; int width = 0; struct def_field *df; struct field *f; while ((c = *arg) != '\0' && (c == ',' || isspace(c))) arg++; if (c == '\0') return (NULL); name = arg; arg = strpbrk(arg, " \t\r\v\f\n,="); if (arg != NULL) { c = *arg; *arg++ = '\0'; if (c == '=') { char *s; header = arg; arg = NULL; width = strlen(header); s = header + width; while (s > header && isspace(*--s)) *s = '\0'; while (isspace(*header)) header++; } } for (df = &fname[0]; df < &fname[NFIELDS]; df++) if (strcmp(name, df->fname) == 0) { if (strcmp(name, "lwp") == 0) Lflg++; break; } if (df >= &fname[NFIELDS]) { (void) fprintf(stderr, gettext("ps: unknown output format: -o %s\n"), name); errflg++; return (arg); } if ((f = malloc(sizeof (*f))) == NULL) { (void) fprintf(stderr, gettext("ps: malloc() for output format failed, %s\n"), err_string(errno)); exit(1); } f->next = NULL; f->fname = df - &fname[0]; f->header = header? header : df->header; if (width == 0) width = df->width; if (*f->header != '\0') do_header = 1; f->width = max(width, df->minwidth); if (fields == NULL) fields = last_field = f; else { last_field->next = f; last_field = f; } return (arg); } static char * devlookup(dev_t ddev) { struct devl *dp; int i; for (dp = devl, i = 0; i < ndev; dp++, i++) { if (dp->ddev == ddev) return (dp->dname); } return (NULL); } static char * devadd(char *name, dev_t ddev) { struct devl *dp; int leng, start, i; if (ndev == maxdev) { maxdev += DNINCR; devl = Realloc(devl, maxdev * sizeof (struct devl)); } dp = &devl[ndev++]; dp->ddev = ddev; if (name == NULL) { (void) strcpy(dp->dname, "??"); return (dp->dname); } leng = strlen(name); /* Strip off /dev/ */ if (leng < DNSIZE + 4) (void) strcpy(dp->dname, &name[5]); else { start = leng - DNSIZE - 1; for (i = start; i < leng && name[i] != '/'; i++) ; if (i == leng) (void) strncpy(dp->dname, &name[start], DNSIZE); else (void) strncpy(dp->dname, &name[i+1], DNSIZE); } return (dp->dname); } /* * gettty returns the user's tty number or ? if none. */ static char * gettty(psinfo_t *psinfo) { extern char *_ttyname_dev(dev_t, char *, size_t); static zoneid_t zid = -1; char devname[TTYNAME_MAX]; char *retval; if (zid == -1) zid = getzoneid(); if (psinfo->pr_ttydev == PRNODEV || psinfo->pr_zoneid != zid) return ("?"); if ((retval = devlookup(psinfo->pr_ttydev)) != NULL) return (retval); retval = _ttyname_dev(psinfo->pr_ttydev, devname, sizeof (devname)); return (devadd(retval, psinfo->pr_ttydev)); } /* * Find the process's tty and return 1 if process is to be printed. */ static int prfind(int found, psinfo_t *psinfo, char **tpp) { char *tp; struct tty *ttyp; if (psinfo->pr_nlwp == 0) { /* process is a zombie */ *tpp = "?"; if (tflg && !found) return (0); return (1); } /* * Get current terminal. If none ("?") and 'a' is set, don't print * info. If 't' is set, check if term is in list of desired terminals * and print it if it is. */ tp = gettty(psinfo); if (aflg && *tp == '?') { *tpp = tp; return (0); } if (tflg && !found) { int match = 0; char *other = NULL; for (ttyp = tty; ttyp->tname != NULL; ttyp++) { /* * Look for a name match */ if (strcmp(tp, ttyp->tname) == 0) { match = 1; break; } /* * Look for same device under different names. */ if ((other == NULL) && (ttyp->tdev != PRNODEV) && (psinfo->pr_ttydev == ttyp->tdev)) other = ttyp->tname; } if (!match && (other != NULL)) { /* * found under a different name */ match = 1; tp = other; } if (!match || (tuid != (uid_t)-1 && tuid != psinfo->pr_euid)) { /* * not found OR not matching euid */ *tpp = tp; return (0); } } *tpp = tp; return (1); } /* * Print info about the process. */ static void prcom(psinfo_t *psinfo, char *ttyp) { char *cp; long tm; int bytesleft; int wcnt, length; wchar_t wchar; struct passwd *pwd; int zombie_lwp; char zonename[ZONENAME_MAX]; /* * If process is zombie, call zombie print routine and return. */ if (psinfo->pr_nlwp == 0) { if (fields != NULL) pr_fields(psinfo, ttyp, print_zombie_field); else przom(psinfo); return; } zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z'); /* * If user specified '-o format', print requested fields and return. */ if (fields != NULL) { pr_fields(psinfo, ttyp, print_field); return; } /* * All fields before 'PID' are printed with a trailing space as a * separator, rather than keeping track of which column is first. All * other fields are printed with a leading space. */ if (lflg) { if (!yflg) (void) printf("%2x ", psinfo->pr_flag & 0377); /* F */ (void) printf("%c ", psinfo->pr_lwp.pr_sname); /* S */ } if (Zflg) { /* ZONE */ if (getzonenamebyid(psinfo->pr_zoneid, zonename, sizeof (zonename)) < 0) { if (snprintf(NULL, 0, "%d", ((int)psinfo->pr_zoneid)) > 7) (void) printf(" %6.6d%c ", ((int)psinfo->pr_zoneid), '*'); else (void) printf(" %7.7d ", ((int)psinfo->pr_zoneid)); } else { size_t nw; nw = mbstowcs(NULL, zonename, 0); if (nw == (size_t)-1) (void) printf("%8.8s ", "ERROR"); else if (nw > 8) (void) wprintf(L"%7.7s%c ", zonename, '*'); else (void) wprintf(L"%8.8s ", zonename); } } if (fflg) { /* UID */ if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) { size_t nw; nw = mbstowcs(NULL, pwd->pw_name, 0); if (nw == (size_t)-1) (void) printf("%8.8s ", "ERROR"); else if (nw > 8) (void) wprintf(L"%7.7s%c ", pwd->pw_name, '*'); else (void) wprintf(L"%8.8s ", pwd->pw_name); } else { if (snprintf(NULL, 0, "%u", (psinfo->pr_euid)) > 7) (void) printf(" %6.6u%c ", psinfo->pr_euid, '*'); else (void) printf(" %7.7u ", psinfo->pr_euid); } } else if (lflg) { if (snprintf(NULL, 0, "%u", (psinfo->pr_euid)) > 6) (void) printf("%5.5u%c ", psinfo->pr_euid, '*'); else (void) printf("%6u ", psinfo->pr_euid); } (void) printf("%*d", pidwidth, (int)psinfo->pr_pid); /* PID */ if (lflg || fflg) (void) printf(" %*d", pidwidth, (int)psinfo->pr_ppid); /* PPID */ if (jflg) { (void) printf(" %*d", pidwidth, (int)psinfo->pr_pgid); /* PGID */ (void) printf(" %*d", pidwidth, (int)psinfo->pr_sid); /* SID */ } if (Lflg) (void) printf(" %5d", (int)psinfo->pr_lwp.pr_lwpid); /* LWP */ if (Pflg) { if (psinfo->pr_lwp.pr_bindpro == PBIND_NONE) /* PSR */ (void) printf(" -"); else (void) printf(" %3d", psinfo->pr_lwp.pr_bindpro); } if (Lflg && fflg) /* NLWP */ (void) printf(" %5d", psinfo->pr_nlwp + psinfo->pr_nzomb); if (cflg) { if (zombie_lwp) /* CLS */ (void) printf(" "); else (void) printf(" %4s", psinfo->pr_lwp.pr_clname); (void) printf(" %3d", psinfo->pr_lwp.pr_pri); /* PRI */ } else if (lflg || fflg) { (void) printf(" %3d", psinfo->pr_lwp.pr_cpu & 0377); /* C */ if (lflg) { /* PRI NI */ /* * Print priorities the old way (lower numbers * mean higher priority) and print nice value * for time sharing procs. */ (void) printf(" %3d", psinfo->pr_lwp.pr_oldpri); if (psinfo->pr_lwp.pr_oldpri != 0) (void) printf(" %2d", psinfo->pr_lwp.pr_nice); else (void) printf(" %2.2s", psinfo->pr_lwp.pr_clname); } } if (lflg) { if (yflg) { if (psinfo->pr_flag & SSYS) /* RSS */ (void) printf(" 0"); else if (psinfo->pr_rssize) (void) printf(" %5lu", (ulong_t)psinfo->pr_rssize); else (void) printf(" ?"); if (psinfo->pr_flag & SSYS) /* SZ */ (void) printf(" 0"); else if (psinfo->pr_size) (void) printf(" %6lu", (ulong_t)psinfo->pr_size); else (void) printf(" ?"); } else { #ifndef _LP64 if (psinfo->pr_addr) /* ADDR */ (void) printf(" %8lx", (ulong_t)psinfo->pr_addr); else #endif (void) printf(" ?"); if (psinfo->pr_flag & SSYS) /* SZ */ (void) printf(" 0"); else if (psinfo->pr_size) (void) printf(" %6lu", (ulong_t)psinfo->pr_size / kbytes_per_page); else (void) printf(" ?"); } if (psinfo->pr_lwp.pr_sname != 'S') /* WCHAN */ (void) printf(" "); #ifndef _LP64 else if (psinfo->pr_lwp.pr_wchan) (void) printf(" %8lx", (ulong_t)psinfo->pr_lwp.pr_wchan); #endif else (void) printf(" ?"); } if (fflg) { /* STIME */ int width = fname[F_STIME].width; if (Lflg) prtime(psinfo->pr_lwp.pr_start, width + 1, 1); else prtime(psinfo->pr_start, width + 1, 1); } if (Hflg) { /* Display home lgroup */ (void) printf(" %4d", (int)psinfo->pr_lwp.pr_lgrp); } (void) printf(" %-8.14s", ttyp); /* TTY */ if (Lflg) { tm = psinfo->pr_lwp.pr_time.tv_sec; if (psinfo->pr_lwp.pr_time.tv_nsec > 500000000) tm++; } else { tm = psinfo->pr_time.tv_sec; if (psinfo->pr_time.tv_nsec > 500000000) tm++; } (void) printf(" %4ld:%.2ld", tm / 60, tm % 60); /* [L]TIME */ if (zombie_lwp) { (void) printf(" <defunct>\n"); return; } if (!fflg) { /* CMD */ wcnt = namencnt(psinfo->pr_fname, 16, 8); (void) printf(" %.*s\n", wcnt, psinfo->pr_fname); return; } /* * PRARGSZ == length of cmd arg string. */ psinfo->pr_psargs[PRARGSZ-1] = '\0'; bytesleft = PRARGSZ; for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) { length = mbtowc(&wchar, cp, MB_LEN_MAX); if (length == 0) break; if (length < 0 || !iswprint(wchar)) { if (length < 0) length = 1; if (bytesleft <= length) { *cp = '\0'; break; } /* omit the unprintable character */ (void) memmove(cp, cp+length, bytesleft-length); length = 0; } bytesleft -= length; } wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, lflg ? 35 : PRARGSZ); (void) printf(" %.*s\n", wcnt, psinfo->pr_psargs); } /* * Print percent from 16-bit binary fraction [0 .. 1] * Round up .01 to .1 to indicate some small percentage (the 0x7000 below). */ static void prtpct(ushort_t pct, int width) { uint_t value = pct; /* need 32 bits to compute with */ value = ((value * 1000) + 0x7000) >> 15; /* [0 .. 1000] */ if (value >= 1000) value = 999; if ((width -= 2) < 2) width = 2; (void) printf("%*u.%u", width, value / 10, value % 10); } static void print_time(time_t tim, int width) { char buf[30]; time_t seconds; time_t minutes; time_t hours; time_t days; if (tim < 0) { (void) printf("%*s", width, "-"); return; } seconds = tim % 60; tim /= 60; minutes = tim % 60; tim /= 60; hours = tim % 24; days = tim / 24; if (days > 0) { (void) snprintf(buf, sizeof (buf), "%ld-%2.2ld:%2.2ld:%2.2ld", days, hours, minutes, seconds); } else if (hours > 0) { (void) snprintf(buf, sizeof (buf), "%2.2ld:%2.2ld:%2.2ld", hours, minutes, seconds); } else { (void) snprintf(buf, sizeof (buf), "%2.2ld:%2.2ld", minutes, seconds); } (void) printf("%*s", width, buf); } static void print_field(psinfo_t *psinfo, struct field *f, const char *ttyp) { int width = f->width; struct passwd *pwd; struct group *grp; time_t cputime; int bytesleft; int wcnt; wchar_t wchar; char *cp; int length; ulong_t mask; char c = '\0', *csave = NULL; int zombie_lwp; zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z'); switch (f->fname) { case F_RUSER: if ((pwd = getpwuid(psinfo->pr_uid)) != NULL) { size_t nw; nw = mbstowcs(NULL, pwd->pw_name, 0); if (nw == (size_t)-1) (void) printf("%*s ", width, "ERROR"); else if (Wflg && nw > width) (void) wprintf(L"%.*s%c", width - 1, pwd->pw_name, '*'); else (void) wprintf(L"%*s", width, pwd->pw_name); } else { if (Wflg && snprintf(NULL, 0, "%u", (psinfo->pr_uid)) > width) (void) printf("%*u%c", width - 1, psinfo->pr_uid, '*'); else (void) printf("%*u", width, psinfo->pr_uid); } break; case F_USER: if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) { size_t nw; nw = mbstowcs(NULL, pwd->pw_name, 0); if (nw == (size_t)-1) (void) printf("%*s ", width, "ERROR"); else if (Wflg && nw > width) (void) wprintf(L"%.*s%c", width - 1, pwd->pw_name, '*'); else (void) wprintf(L"%*s", width, pwd->pw_name); } else { if (Wflg && snprintf(NULL, 0, "%u", (psinfo->pr_euid)) > width) (void) printf("%*u%c", width - 1, psinfo->pr_euid, '*'); else (void) printf("%*u", width, psinfo->pr_euid); } break; case F_RGROUP: if ((grp = getgrgid(psinfo->pr_gid)) != NULL) (void) printf("%*s", width, grp->gr_name); else (void) printf("%*u", width, psinfo->pr_gid); break; case F_GROUP: if ((grp = getgrgid(psinfo->pr_egid)) != NULL) (void) printf("%*s", width, grp->gr_name); else (void) printf("%*u", width, psinfo->pr_egid); break; case F_RUID: (void) printf("%*u", width, psinfo->pr_uid); break; case F_UID: (void) printf("%*u", width, psinfo->pr_euid); break; case F_RGID: (void) printf("%*u", width, psinfo->pr_gid); break; case F_GID: (void) printf("%*u", width, psinfo->pr_egid); break; case F_PID: (void) printf("%*d", width, (int)psinfo->pr_pid); break; case F_PPID: (void) printf("%*d", width, (int)psinfo->pr_ppid); break; case F_PGID: (void) printf("%*d", width, (int)psinfo->pr_pgid); break; case F_SID: (void) printf("%*d", width, (int)psinfo->pr_sid); break; case F_PSR: if (zombie_lwp || psinfo->pr_lwp.pr_bindpro == PBIND_NONE) (void) printf("%*s", width, "-"); else (void) printf("%*d", width, psinfo->pr_lwp.pr_bindpro); break; case F_LWP: (void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lwpid); break; case F_NLWP: (void) printf("%*d", width, psinfo->pr_nlwp + psinfo->pr_nzomb); break; case F_OPRI: if (zombie_lwp) (void) printf("%*s", width, "-"); else (void) printf("%*d", width, psinfo->pr_lwp.pr_oldpri); break; case F_PRI: if (zombie_lwp) (void) printf("%*s", width, "-"); else (void) printf("%*d", width, psinfo->pr_lwp.pr_pri); break; case F_F: mask = 0xffffffffUL; if (width < 8) mask >>= (8 - width) * 4; (void) printf("%*lx", width, psinfo->pr_flag & mask); break; case F_S: (void) printf("%*c", width, psinfo->pr_lwp.pr_sname); break; case F_C: if (zombie_lwp) (void) printf("%*s", width, "-"); else (void) printf("%*d", width, psinfo->pr_lwp.pr_cpu); break; case F_PCPU: if (zombie_lwp) (void) printf("%*s", width, "-"); else if (Lflg) prtpct(psinfo->pr_lwp.pr_pctcpu, width); else prtpct(psinfo->pr_pctcpu, width); break; case F_PMEM: prtpct(psinfo->pr_pctmem, width); break; case F_OSZ: (void) printf("%*lu", width, (ulong_t)psinfo->pr_size / kbytes_per_page); break; case F_VSZ: (void) printf("%*lu", width, (ulong_t)psinfo->pr_size); break; case F_RSS: (void) printf("%*lu", width, (ulong_t)psinfo->pr_rssize); break; case F_NICE: /* if pr_oldpri is zero, then this class has no nice */ if (zombie_lwp) (void) printf("%*s", width, "-"); else if (psinfo->pr_lwp.pr_oldpri != 0) (void) printf("%*d", width, psinfo->pr_lwp.pr_nice); else (void) printf("%*.*s", width, width, psinfo->pr_lwp.pr_clname); break; case F_CLASS: if (zombie_lwp) (void) printf("%*s", width, "-"); else (void) printf("%*.*s", width, width, psinfo->pr_lwp.pr_clname); break; case F_STIME: if (Lflg) prtime(psinfo->pr_lwp.pr_start, width, 0); else prtime(psinfo->pr_start, width, 0); break; case F_ETIME: if (Lflg) print_time(delta_secs(&psinfo->pr_lwp.pr_start), width); else print_time(delta_secs(&psinfo->pr_start), width); break; case F_TIME: if (Lflg) { cputime = psinfo->pr_lwp.pr_time.tv_sec; if (psinfo->pr_lwp.pr_time.tv_nsec > 500000000) cputime++; } else { cputime = psinfo->pr_time.tv_sec; if (psinfo->pr_time.tv_nsec > 500000000) cputime++; } print_time(cputime, width); break; case F_TTY: (void) printf("%-*s", width, ttyp); break; case F_ADDR: if (zombie_lwp) (void) printf("%*s", width, "-"); else if (Lflg) (void) printf("%*lx", width, (long)psinfo->pr_lwp.pr_addr); else (void) printf("%*lx", width, (long)psinfo->pr_addr); break; case F_WCHAN: if (!zombie_lwp && psinfo->pr_lwp.pr_wchan) (void) printf("%*lx", width, (long)psinfo->pr_lwp.pr_wchan); else (void) printf("%*.*s", width, width, "-"); break; case F_FNAME: /* * Print full width unless this is the last output format. */ if (zombie_lwp) { if (f->next != NULL) (void) printf("%-*s", width, "<defunct>"); else (void) printf("%s", "<defunct>"); break; } wcnt = namencnt(psinfo->pr_fname, 16, width); if (f->next != NULL) (void) printf("%-*.*s", width, wcnt, psinfo->pr_fname); else (void) printf("%-.*s", wcnt, psinfo->pr_fname); break; case F_COMM: if (zombie_lwp) { if (f->next != NULL) (void) printf("%-*s", width, "<defunct>"); else (void) printf("%s", "<defunct>"); break; } csave = strpbrk(psinfo->pr_psargs, " \t\r\v\f\n"); if (csave) { c = *csave; *csave = '\0'; } /* FALLTHROUGH */ case F_ARGS: /* * PRARGSZ == length of cmd arg string. */ if (zombie_lwp) { (void) printf("%-*s", width, "<defunct>"); break; } psinfo->pr_psargs[PRARGSZ-1] = '\0'; bytesleft = PRARGSZ; for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) { length = mbtowc(&wchar, cp, MB_LEN_MAX); if (length == 0) break; if (length < 0 || !iswprint(wchar)) { if (length < 0) length = 1; if (bytesleft <= length) { *cp = '\0'; break; } /* omit the unprintable character */ (void) memmove(cp, cp+length, bytesleft-length); length = 0; } bytesleft -= length; } wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, width); /* * Print full width unless this is the last format. */ if (f->next != NULL) (void) printf("%-*.*s", width, wcnt, psinfo->pr_psargs); else (void) printf("%-.*s", wcnt, psinfo->pr_psargs); if (f->fname == F_COMM && csave) *csave = c; break; case F_TASKID: (void) printf("%*d", width, (int)psinfo->pr_taskid); break; case F_PROJID: (void) printf("%*d", width, (int)psinfo->pr_projid); break; case F_PROJECT: { struct project cproj; char proj_buf[PROJECT_BUFSZ]; if ((getprojbyid(psinfo->pr_projid, &cproj, (void *)&proj_buf, PROJECT_BUFSZ)) == NULL) { if (Wflg && snprintf(NULL, 0, "%d", ((int)psinfo->pr_projid)) > width) (void) printf("%.*d%c", width - 1, ((int)psinfo->pr_projid), '*'); else (void) printf("%*d", width, (int)psinfo->pr_projid); } else { size_t nw; if (cproj.pj_name != NULL) nw = mbstowcs(NULL, cproj.pj_name, 0); if (cproj.pj_name == NULL) (void) printf("%*s ", width, "---"); else if (nw == (size_t)-1) (void) printf("%*s ", width, "ERROR"); else if (Wflg && nw > width) (void) wprintf(L"%.*s%c", width - 1, cproj.pj_name, '*'); else (void) wprintf(L"%*s", width, cproj.pj_name); } } break; case F_PSET: if (zombie_lwp || psinfo->pr_lwp.pr_bindpset == PS_NONE) (void) printf("%*s", width, "-"); else (void) printf("%*d", width, psinfo->pr_lwp.pr_bindpset); break; case F_ZONEID: (void) printf("%*d", width, (int)psinfo->pr_zoneid); break; case F_ZONE: { char zonename[ZONENAME_MAX]; if (getzonenamebyid(psinfo->pr_zoneid, zonename, sizeof (zonename)) < 0) { if (Wflg && snprintf(NULL, 0, "%d", ((int)psinfo->pr_zoneid)) > width) (void) printf("%.*d%c", width - 1, ((int)psinfo->pr_zoneid), '*'); else (void) printf("%*d", width, (int)psinfo->pr_zoneid); } else { size_t nw; nw = mbstowcs(NULL, zonename, 0); if (nw == (size_t)-1) (void) printf("%*s ", width, "ERROR"); else if (Wflg && nw > width) (void) wprintf(L"%.*s%c", width - 1, zonename, '*'); else (void) wprintf(L"%*s", width, zonename); } } break; case F_CTID: if (psinfo->pr_contract == -1) (void) printf("%*s", width, "-"); else (void) printf("%*ld", width, (long)psinfo->pr_contract); break; case F_LGRP: /* Display home lgroup */ (void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lgrp); break; case F_DMODEL: (void) printf("%*s", width, psinfo->pr_dmodel == PR_MODEL_LP64 ? "_LP64" : "_ILP32"); break; } } static void print_zombie_field(psinfo_t *psinfo, struct field *f, const char *ttyp) { int wcnt; int width = f->width; switch (f->fname) { case F_FNAME: case F_COMM: case F_ARGS: /* * Print full width unless this is the last output format. */ wcnt = min(width, sizeof ("<defunct>")); if (f->next != NULL) (void) printf("%-*.*s", width, wcnt, "<defunct>"); else (void) printf("%-.*s", wcnt, "<defunct>"); break; case F_PSR: case F_PCPU: case F_PMEM: case F_NICE: case F_CLASS: case F_STIME: case F_ETIME: case F_WCHAN: case F_PSET: (void) printf("%*s", width, "-"); break; case F_OPRI: case F_PRI: case F_OSZ: case F_VSZ: case F_RSS: (void) printf("%*d", width, 0); break; default: print_field(psinfo, f, ttyp); break; } } static void pr_fields(psinfo_t *psinfo, const char *ttyp, void (*print_fld)(psinfo_t *, struct field *, const char *)) { struct field *f; for (f = fields; f != NULL; f = f->next) { print_fld(psinfo, f, ttyp); if (f->next != NULL) (void) printf(" "); } (void) printf("\n"); } /* * Returns 1 if arg is found in array arr, of length num; 0 otherwise. */ static int search(pid_t *arr, int number, pid_t arg) { int i; for (i = 0; i < number; i++) if (arg == arr[i]) return (1); return (0); } /* * Add an entry (user, group) to the specified table. */ static void add_ugentry(struct ughead *tbl, char *name) { struct ugdata *entp; if (tbl->size == tbl->nent) { /* reallocate the table entries */ if ((tbl->size *= 2) == 0) tbl->size = 32; /* first time */ tbl->ent = Realloc(tbl->ent, tbl->size*sizeof (struct ugdata)); } entp = &tbl->ent[tbl->nent++]; entp->id = 0; (void) strncpy(entp->name, name, MAXUGNAME); entp->name[MAXUGNAME] = '\0'; } static int uconv(struct ughead *uhead) { struct ugdata *utbl = uhead->ent; int n = uhead->nent; struct passwd *pwd; int i; int fnd = 0; uid_t uid; /* * Ask the name service for names. */ for (i = 0; i < n; i++) { /* * If name is numeric, ask for numeric id */ if (str2uid(utbl[i].name, &uid, 0, MAXEPHUID) == 0) pwd = getpwuid(uid); else pwd = getpwnam(utbl[i].name); /* * If found, enter found index into tbl array. */ if (pwd == NULL) { (void) fprintf(stderr, gettext("ps: unknown user %s\n"), utbl[i].name); continue; } utbl[fnd].id = pwd->pw_uid; (void) strncpy(utbl[fnd].name, pwd->pw_name, MAXUGNAME); fnd++; } uhead->nent = fnd; /* in case it changed */ return (n - fnd); } static int gconv(struct ughead *ghead) { struct ugdata *gtbl = ghead->ent; int n = ghead->nent; struct group *grp; gid_t gid; int i; int fnd = 0; /* * Ask the name service for names. */ for (i = 0; i < n; i++) { /* * If name is numeric, ask for numeric id */ if (str2uid(gtbl[i].name, (uid_t *)&gid, 0, MAXEPHUID) == 0) grp = getgrgid(gid); else grp = getgrnam(gtbl[i].name); /* * If found, enter found index into tbl array. */ if (grp == NULL) { (void) fprintf(stderr, gettext("ps: unknown group %s\n"), gtbl[i].name); continue; } gtbl[fnd].id = grp->gr_gid; (void) strncpy(gtbl[fnd].name, grp->gr_name, MAXUGNAME); fnd++; } ghead->nent = fnd; /* in case it changed */ return (n - fnd); } /* * Return 1 if puid is in table, otherwise 0. */ static int ugfind(id_t id, struct ughead *ughead) { struct ugdata *utbl = ughead->ent; int n = ughead->nent; int i; for (i = 0; i < n; i++) if (utbl[i].id == id) return (1); return (0); } /* * Print starting time of process unless process started more than 24 hours * ago, in which case the date is printed. The date is printed in the form * "MMM dd" if old format, else the blank is replaced with an '_' so * it appears as a single word (for parseability). */ static void prtime(timestruc_t st, int width, int old) { char sttim[26]; time_t starttime; starttime = st.tv_sec; if (st.tv_nsec > 500000000) starttime++; if ((now.tv_sec - starttime) >= 24*60*60) { (void) strftime(sttim, sizeof (sttim), old? /* * TRANSLATION_NOTE * This time format is used by STIME field when -f option * is specified. Used for processes that begun more than * 24 hours. */ dcgettext(NULL, "%b %d", LC_TIME) : /* * TRANSLATION_NOTE * This time format is used by STIME field when -o option * is specified. Used for processes that begun more than * 24 hours. */ dcgettext(NULL, "%b_%d", LC_TIME), localtime(&starttime)); } else { /* * TRANSLATION_NOTE * This time format is used by STIME field when -f or -o option * is specified. Used for processes that begun less than * 24 hours. */ (void) strftime(sttim, sizeof (sttim), dcgettext(NULL, "%H:%M:%S", LC_TIME), localtime(&starttime)); } (void) printf("%*.*s", width, width, sttim); } static void przom(psinfo_t *psinfo) { long tm; struct passwd *pwd; char zonename[ZONENAME_MAX]; /* * All fields before 'PID' are printed with a trailing space as a * spearator, rather than keeping track of which column is first. All * other fields are printed with a leading space. */ if (lflg) { /* F S */ if (!yflg) (void) printf("%2x ", psinfo->pr_flag & 0377); /* F */ (void) printf("%c ", psinfo->pr_lwp.pr_sname); /* S */ } if (Zflg) { if (getzonenamebyid(psinfo->pr_zoneid, zonename, sizeof (zonename)) < 0) { if (snprintf(NULL, 0, "%d", ((int)psinfo->pr_zoneid)) > 7) (void) printf(" %6.6d%c ", ((int)psinfo->pr_zoneid), '*'); else (void) printf(" %7.7d ", ((int)psinfo->pr_zoneid)); } else { size_t nw; nw = mbstowcs(NULL, zonename, 0); if (nw == (size_t)-1) (void) printf("%8.8s ", "ERROR"); else if (nw > 8) (void) wprintf(L"%7.7s%c ", zonename, '*'); else (void) wprintf(L"%8.8s ", zonename); } } if (Hflg) { /* Display home lgroup */ (void) printf(" %6d", (int)psinfo->pr_lwp.pr_lgrp); /* LGRP */ } if (fflg) { if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) { size_t nw; nw = mbstowcs(NULL, pwd->pw_name, 0); if (nw == (size_t)-1) (void) printf("%8.8s ", "ERROR"); else if (nw > 8) (void) wprintf(L"%7.7s%c ", pwd->pw_name, '*'); else (void) wprintf(L"%8.8s ", pwd->pw_name); } else { if (snprintf(NULL, 0, "%u", (psinfo->pr_euid)) > 7) (void) printf(" %6.6u%c ", psinfo->pr_euid, '*'); else (void) printf(" %7.7u ", psinfo->pr_euid); } } else if (lflg) { if (snprintf(NULL, 0, "%u", (psinfo->pr_euid)) > 6) (void) printf("%5.5u%c ", psinfo->pr_euid, '*'); else (void) printf("%6u ", psinfo->pr_euid); } (void) printf("%*d", pidwidth, (int)psinfo->pr_pid); /* PID */ if (lflg || fflg) (void) printf(" %*d", pidwidth, (int)psinfo->pr_ppid); /* PPID */ if (jflg) { (void) printf(" %*d", pidwidth, (int)psinfo->pr_pgid); /* PGID */ (void) printf(" %*d", pidwidth, (int)psinfo->pr_sid); /* SID */ } if (Lflg) (void) printf(" %5d", 0); /* LWP */ if (Pflg) (void) printf(" -"); /* PSR */ if (Lflg && fflg) (void) printf(" %5d", 0); /* NLWP */ if (cflg) { (void) printf(" %4s", "-"); /* zombies have no class */ (void) printf(" %3d", psinfo->pr_lwp.pr_pri); /* PRI */ } else if (lflg || fflg) { (void) printf(" %3d", psinfo->pr_lwp.pr_cpu & 0377); /* C */ if (lflg) (void) printf(" %3d %2s", psinfo->pr_lwp.pr_oldpri, "-"); /* PRI NI */ } if (lflg) { if (yflg) /* RSS SZ WCHAN */ (void) printf(" %5d %6d %8s", 0, 0, "-"); else /* ADDR SZ WCHAN */ (void) printf(" %8s %6d %8s", "-", 0, "-"); } if (fflg) { int width = fname[F_STIME].width; (void) printf(" %*.*s", width, width, "-"); /* STIME */ } (void) printf(" %-8.14s", "?"); /* TTY */ tm = psinfo->pr_time.tv_sec; if (psinfo->pr_time.tv_nsec > 500000000) tm++; (void) printf(" %4ld:%.2ld", tm / 60, tm % 60); /* TIME */ (void) printf(" <defunct>\n"); } /* * Function to compute the number of printable bytes in a multibyte * command string ("internationalization"). */ static int namencnt(char *cmd, int csisize, int scrsize) { int csiwcnt = 0, scrwcnt = 0; int ncsisz, nscrsz; wchar_t wchar; int len; while (*cmd != '\0') { if ((len = csisize - csiwcnt) > (int)MB_CUR_MAX) len = MB_CUR_MAX; if ((ncsisz = mbtowc(&wchar, cmd, len)) < 0) return (8); /* default to use for illegal chars */ if ((nscrsz = wcwidth(wchar)) <= 0) return (8); if (csiwcnt + ncsisz > csisize || scrwcnt + nscrsz > scrsize) break; csiwcnt += ncsisz; scrwcnt += nscrsz; cmd += ncsisz; } return (csiwcnt); } static char * err_string(int err) { static char buf[32]; char *str = strerror(err); if (str == NULL) (void) snprintf(str = buf, sizeof (buf), "Errno #%d", err); return (str); } /* If allocation fails, die */ static void * Realloc(void *ptr, size_t size) { ptr = realloc(ptr, size); if (ptr == NULL) { (void) fprintf(stderr, gettext("ps: no memory\n")); exit(1); } return (ptr); } static time_t delta_secs(const timestruc_t *start) { time_t seconds = now.tv_sec - start->tv_sec; long nanosecs = now.tv_usec * 1000 - start->tv_nsec; if (nanosecs >= (NANOSEC / 2)) seconds++; else if (nanosecs < -(NANOSEC / 2)) seconds--; return (seconds); } /* * Returns the following: * * 0 No error * EINVAL Invalid number * ERANGE Value exceeds (min, max) range */ static int str2id(const char *p, pid_t *val, long min, long max) { char *q; long number; int error; errno = 0; number = strtol(p, &q, 10); if (errno != 0 || q == p || *q != '\0') { if ((error = errno) == 0) { /* * strtol() can fail without setting errno, or it can * set it to EINVAL or ERANGE. In the case errno is * still zero, return EINVAL. */ error = EINVAL; } } else if (number < min || number > max) { error = ERANGE; } else { error = 0; } *val = number; return (error); } /* * Returns the following: * * 0 No error * EINVAL Invalid number * ERANGE Value exceeds (min, max) range */ static int str2uid(const char *p, uid_t *val, unsigned long min, unsigned long max) { char *q; unsigned long number; int error; errno = 0; number = strtoul(p, &q, 10); if (errno != 0 || q == p || *q != '\0') { if ((error = errno) == 0) { /* * strtoul() can fail without setting errno, or it can * set it to EINVAL or ERANGE. In the case errno is * still zero, return EINVAL. */ error = EINVAL; } } else if (number < min || number > max) { error = ERANGE; } else { error = 0; } *val = number; return (error); } static int pidcmp(const void *p1, const void *p2) { pid_t i = *((pid_t *)p1); pid_t j = *((pid_t *)p2); return (i - j); }