| /* |
| * 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) 2018, Joyent, Inc. |
| */ |
| |
| /* 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_LWPNAME, /* lwp name */ |
| 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 }, |
| { "lwpname", "LWPNAME", 32, 8 }, |
| { "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_LWPNAME: |
| 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 || |
| strcmp(name, "lwpname") == 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_LWPNAME: { |
| char lwpname[THREAD_NAME_MAX] = ""; |
| char *path = NULL; |
| int fd; |
| |
| if (asprintf(&path, "%s/%d/lwp/%d/lwpname", procdir, |
| (int)psinfo->pr_pid, (int)psinfo->pr_lwp.pr_lwpid) != -1 && |
| (fd = open(path, O_RDONLY)) != -1) { |
| (void) read(fd, lwpname, sizeof (lwpname)); |
| lwpname[THREAD_NAME_MAX - 1] = '\0'; |
| (void) close(fd); |
| } |
| |
| free(path); |
| |
| if (f->next != NULL) |
| (void) printf("%-*s", width, lwpname); |
| else |
| (void) printf("%s", lwpname); |
| 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 = |