1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2012, Joyent, Inc. All rights reserved.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30 /*
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
34 *
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
38 */
39
40 /*
41 * ps -- print things about processes.
42 */
43
44 #define _SYSCALL32
45
46 #include <stdio.h>
47 #include <ctype.h>
48 #include <string.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <pwd.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <sys/mkdev.h>
55 #include <unistd.h>
56 #include <stdlib.h>
57 #include <limits.h>
58 #include <dirent.h>
59 #include <procfs.h>
60 #include <sys/param.h>
61 #include <sys/ttold.h>
62 #include <libelf.h>
63 #include <gelf.h>
64 #include <locale.h>
65 #include <wctype.h>
66 #include <stdarg.h>
67 #include <sys/proc.h>
68 #include <priv_utils.h>
69 #include <zone.h>
70
71 #define NTTYS 2 /* max ttys that can be specified with the -t option */
72 /* only one tty can be specified with SunOS ps */
73 #define SIZ 30 /* max processes that can be specified with -p and -g */
74 #define ARGSIZ 30 /* size of buffer holding args for -t, -p, -u options */
75
76 #define FSTYPE_MAX 8
77
78 struct psent {
79 psinfo_t *psinfo;
80 char *psargs;
81 int found;
82 };
83
84 static int tplen, maxlen, twidth;
85 static char hdr[81];
86 static struct winsize win;
87
88 static int retcode = 1;
89 static int lflg; /* long format */
90 static int uflg; /* user-oriented output */
91 static int aflg; /* Display all processes */
92 static int eflg; /* Display environment as well as arguments */
93 static int gflg; /* Display process group leaders */
94 static int tflg; /* Processes running on specific terminals */
95 static int rflg; /* Running processes only flag */
96 static int Sflg; /* Accumulated time plus all reaped children */
97 static int xflg; /* Include processes with no controlling tty */
98 static int cflg; /* Display command name */
99 static int vflg; /* Virtual memory-oriented output */
100 static int nflg; /* Numerical output */
101 static int pflg; /* Specific process id passed as argument */
102 static int Uflg; /* Update private database, ups_data */
103 static int errflg;
104
105 static char *gettty();
106 static char argbuf[ARGSIZ];
107 static char *parg;
108 static char *p1; /* points to successive option arguments */
109 static uid_t my_uid;
110 static char stdbuf[BUFSIZ];
111
112 static int ndev; /* number of devices */
113 static int maxdev; /* number of devl structures allocated */
114
115 #define DNINCR 100
116 #define DNSIZE 14
117 static struct devl { /* device list */
118 char dname[DNSIZE]; /* device name */
119 dev_t ddev; /* device number */
120 } *devl;
121
122 static struct tty {
123 char *tname;
124 dev_t tdev;
125 } tty[NTTYS]; /* for t option */
126 static int ntty = 0;
127 static pid_t pidsave;
128 static int pidwidth;
129
130 static char *procdir = "/proc"; /* standard /proc directory */
131 static void usage(); /* print usage message and quit */
132 static void getarg(void);
133 static void prtime(timestruc_t st);
134 static void przom(psinfo_t *psinfo);
135 static int num(char *);
136 static int preadargs(int, psinfo_t *, char *);
137 static int preadenvs(int, psinfo_t *, char *);
138 static int prcom(int, psinfo_t *, char *);
139 static int namencnt(char *, int, int);
140 static int pscompare(const void *, const void *);
141 static char *err_string(int);
142
143 extern int scrwidth(wchar_t); /* header file? */
144
145 int
ucbmain(int argc,char ** argv)146 ucbmain(int argc, char **argv)
147 {
148 psinfo_t info; /* process information structure from /proc */
149 char *psargs = NULL; /* pointer to buffer for -w and -ww options */
150 char *svpsargs = NULL;
151 struct psent *psent;
152 int entsize;
153 int nent;
154 pid_t maxpid;
155
156 struct tty *ttyp = tty;
157 char *tmp;
158 char *p;
159 int c;
160 pid_t pid; /* pid: process id */
161 pid_t ppid; /* ppid: parent process id */
162 int i, found;
163
164 size_t size;
165
166 DIR *dirp;
167 struct dirent *dentp;
168 char psname[100];
169 char asname[100];
170 int pdlen;
171 size_t len;
172
173 (void) setlocale(LC_ALL, "");
174
175 my_uid = getuid();
176
177 /*
178 * This program needs the proc_owner privilege
179 */
180 (void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_PROC_OWNER,
181 (char *)NULL);
182
183 /*
184 * calculate width of pid fields based on configured MAXPID
185 * (must be at least 5 to retain output format compatibility)
186 */
187 maxpid = (pid_t)sysconf(_SC_MAXPID);
188 pidwidth = 1;
189 while ((maxpid /= 10) > 0)
190 ++pidwidth;
191 pidwidth = pidwidth < 5 ? 5 : pidwidth;
192
193 if (ioctl(1, TIOCGWINSZ, &win) == -1)
194 twidth = 80;
195 else
196 twidth = (win.ws_col == 0 ? 80 : win.ws_col);
197
198 /* add the '-' for BSD compatibility */
199 if (argc > 1) {
200 if (argv[1][0] != '-' && !isdigit(argv[1][0])) {
201 len = strlen(argv[1]) + 2;
202 tmp = malloc(len);
203 if (tmp != NULL) {
204 (void) snprintf(tmp, len, "%s%s", "-", argv[1]);
205 argv[1] = tmp;
206 }
207 }
208 }
209
210 setbuf(stdout, stdbuf);
211 while ((c = getopt(argc, argv, "lcaengrSt:xuvwU")) != EOF)
212 switch (c) {
213 case 'g':
214 gflg++; /* include process group leaders */
215 break;
216 case 'c': /* display internal command name */
217 cflg++;
218 break;
219 case 'r': /* restrict output to running processes */
220 rflg++;
221 break;
222 case 'S': /* display time by process and all reaped children */
223 Sflg++;
224 break;
225 case 'x': /* process w/o controlling tty */
226 xflg++;
227 break;
228 case 'l': /* long listing */
229 lflg++;
230 uflg = vflg = 0;
231 break;
232 case 'u': /* user-oriented output */
233 uflg++;
234 lflg = vflg = 0;
235 break;
236 case 'U': /* update private database ups_data */
237 Uflg++;
238 break;
239 case 'w': /* increase display width */
240 if (twidth < 132)
241 twidth = 132;
242 else /* second w option */
243 twidth = NCARGS;
244 break;
245 case 'v': /* display virtual memory format */
246 vflg++;
247 lflg = uflg = 0;
248 break;
249 case 'a':
250 /*
251 * display all processes except process group
252 * leaders and processes w/o controlling tty
253 */
254 aflg++;
255 gflg++;
256 break;
257 case 'e':
258 /* Display environment along with aguments. */
259 eflg++;
260 break;
261 case 'n': /* Display numerical output */
262 nflg++;
263 break;
264 case 't': /* restrict output to named terminal */
265 #define TSZ 30
266 tflg++;
267 gflg++;
268 xflg = 0;
269
270 p1 = optarg;
271 do { /* only loop through once (NTTYS = 2) */
272 parg = argbuf;
273 if (ntty >= NTTYS-1)
274 break;
275 getarg();
276 if ((p = malloc(TSZ+1)) == NULL) {
277 (void) fprintf(stderr,
278 "ps: no memory\n");
279 exit(1);
280 }
281 p[0] = '\0';
282 size = TSZ;
283 if (isdigit(*parg)) {
284 (void) strcpy(p, "tty");
285 size -= 3;
286 }
287
288 (void) strncat(p, parg, size);
289 ttyp->tdev = PRNODEV;
290 if (parg && *parg == '?')
291 xflg++;
292 else {
293 char nambuf[TSZ+6]; /* for /dev/+\0 */
294 struct stat64 s;
295 (void) strcpy(nambuf, "/dev/");
296 (void) strcat(nambuf, p);
297 if (stat64(nambuf, &s) == 0)
298 ttyp->tdev = s.st_rdev;
299 }
300 ttyp++->tname = p;
301 ntty++;
302 } while (*p1);
303 break;
304 default: /* error on ? */
305 errflg++;
306 break;
307 }
308
309 if (errflg)
310 usage();
311
312 if (optind + 1 < argc) { /* more than one additional argument */
313 (void) fprintf(stderr, "ps: too many arguments\n");
314 usage();
315 }
316
317 /*
318 * The -U option is obsolete. Attempts to use it cause ps to exit
319 * without printing anything.
320 */
321 if (Uflg)
322 exit(0);
323
324 if (optind < argc) { /* user specified a specific proc id */
325 pflg++;
326 p1 = argv[optind];
327 parg = argbuf;
328 getarg();
329 if (!num(parg)) {
330 (void) fprintf(stderr,
331 "ps: %s is an invalid non-numeric argument for a process id\n", parg);
332 usage();
333 }
334 pidsave = (pid_t)atol(parg);
335 aflg = rflg = xflg = 0;
336 gflg++;
337 }
338
339 if (tflg)
340 ttyp->tname = NULL;
341
342 /* allocate an initial guess for the number of processes */
343 entsize = 1024;
344 psent = malloc(entsize * sizeof (struct psent));
345 if (psent == NULL) {
346 (void) fprintf(stderr, "ps: no memory\n");
347 exit(1);
348 }
349 nent = 0; /* no active entries yet */
350
351 if (lflg) {
352 (void) sprintf(hdr,
353 " F UID%*s%*s %%C PRI NI SZ RSS "
354 "WCHAN S TT TIME COMMAND", pidwidth + 1, "PID",
355 pidwidth + 1, "PPID");
356 } else if (uflg) {
357 if (nflg)
358 (void) sprintf(hdr,
359 " UID%*s %%CPU %%MEM SZ RSS "
360 "TT S START TIME COMMAND",
361 pidwidth + 1, "PID");
362 else
363 (void) sprintf(hdr,
364 "USER %*s %%CPU %%MEM SZ RSS "
365 "TT S START TIME COMMAND",
366 pidwidth + 1, "PID");
367 } else if (vflg) {
368 (void) sprintf(hdr,
369 "%*s TT S TIME SIZE RSS %%CPU %%MEM "
370 "COMMAND", pidwidth + 1, "PID");
371 } else
372 (void) sprintf(hdr, "%*s TT S TIME COMMAND",
373 pidwidth + 1, "PID");
374
375 twidth = twidth - strlen(hdr) + 6;
376 (void) printf("%s\n", hdr);
377
378 if (twidth > PRARGSZ && (psargs = malloc(twidth)) == NULL) {
379 (void) fprintf(stderr, "ps: no memory\n");
380 exit(1);
381 }
382 svpsargs = psargs;
383
384 /*
385 * Determine which processes to print info about by searching
386 * the /proc directory and looking at each process.
387 */
388 if ((dirp = opendir(procdir)) == NULL) {
389 (void) fprintf(stderr, "ps: cannot open PROC directory %s\n",
390 procdir);
391 exit(1);
392 }
393
394 (void) strcpy(psname, procdir);
395 pdlen = strlen(psname);
396 psname[pdlen++] = '/';
397
398 /* for each active process --- */
399 while ((dentp = readdir(dirp)) != NULL) {
400 int psfd; /* file descriptor for /proc/nnnnn/psinfo */
401 int asfd; /* file descriptor for /proc/nnnnn/as */
402
403 if (dentp->d_name[0] == '.') /* skip . and .. */
404 continue;
405 (void) strcpy(psname + pdlen, dentp->d_name);
406 (void) strcpy(asname, psname);
407 (void) strcat(psname, "/psinfo");
408 (void) strcat(asname, "/as");
409 retry:
410 if ((psfd = open(psname, O_RDONLY)) == -1)
411 continue;
412 asfd = -1;
413 if (psargs != NULL || eflg) {
414
415 /* now we need the proc_owner privilege */
416 (void) __priv_bracket(PRIV_ON);
417
418 asfd = open(asname, O_RDONLY);
419
420 /* drop proc_owner privilege after open */
421 (void) __priv_bracket(PRIV_OFF);
422 }
423
424 /*
425 * Get the info structure for the process
426 */
427 if (read(psfd, &info, sizeof (info)) != sizeof (info)) {
428 int saverr = errno;
429
430 (void) close(psfd);
431 if (asfd > 0)
432 (void) close(asfd);
433 if (saverr == EAGAIN)
434 goto retry;
435 if (saverr != ENOENT)
436 (void) fprintf(stderr, "ps: read() on %s: %s\n",
437 psname, err_string(saverr));
438 continue;
439 }
440 (void) close(psfd);
441
442 found = 0;
443 if (info.pr_lwp.pr_state == 0) /* can't happen? */
444 goto closeit;
445 pid = info.pr_pid;
446 ppid = info.pr_ppid;
447
448 /* Display only process from command line */
449 if (pflg) { /* pid in arg list */
450 if (pidsave == pid)
451 found++;
452 else
453 goto closeit;
454 }
455
456 /*
457 * Omit "uninteresting" processes unless 'g' option.
458 */
459 if ((ppid == 1) && !(gflg))
460 goto closeit;
461
462 /*
463 * Omit non-running processes for 'r' option
464 */
465 if (rflg &&
466 !(info.pr_lwp.pr_sname == 'O' ||
467 info.pr_lwp.pr_sname == 'R'))
468 goto closeit;
469
470 if (!found && !tflg && !aflg && info.pr_euid != my_uid)
471 goto closeit;
472
473 /*
474 * Read the args for the -w and -ww cases
475 */
476 if (asfd > 0) {
477 if ((psargs != NULL &&
478 preadargs(asfd, &info, psargs) == -1) ||
479 (eflg && preadenvs(asfd, &info, psargs) == -1)) {
480 int saverr = errno;
481
482 (void) close(asfd);
483 if (saverr == EAGAIN)
484 goto retry;
485 if (saverr != ENOENT)
486 (void) fprintf(stderr,
487 "ps: read() on %s: %s\n",
488 asname, err_string(saverr));
489 continue;
490 }
491 } else {
492 psargs = info.pr_psargs;
493 }
494
495 if (nent >= entsize) {
496 entsize *= 2;
497 psent = (struct psent *)realloc((char *)psent,
498 entsize * sizeof (struct psent));
499 if (psent == NULL) {
500 (void) fprintf(stderr, "ps: no memory\n");
501 exit(1);
502 }
503 }
504 if ((psent[nent].psinfo = malloc(sizeof (psinfo_t)))
505 == NULL) {
506 (void) fprintf(stderr, "ps: no memory\n");
507 exit(1);
508 }
509 *psent[nent].psinfo = info;
510 if (psargs == NULL)
511 psent[nent].psargs = NULL;
512 else {
513 if ((psent[nent].psargs = malloc(strlen(psargs)+1))
514 == NULL) {
515 (void) fprintf(stderr, "ps: no memory\n");
516 exit(1);
517 }
518 (void) strcpy(psent[nent].psargs, psargs);
519 }
520 psent[nent].found = found;
521 nent++;
522 closeit:
523 if (asfd > 0)
524 (void) close(asfd);
525 psargs = svpsargs;
526 }
527
528 /* revert to non-privileged user */
529 (void) __priv_relinquish();
530
531 (void) closedir(dirp);
532
533 qsort((char *)psent, nent, sizeof (psent[0]), pscompare);
534
535 for (i = 0; i < nent; i++) {
536 struct psent *pp = &psent[i];
537 if (prcom(pp->found, pp->psinfo, pp->psargs)) {
538 (void) printf("\n");
539 retcode = 0;
540 }
541 }
542
543 return (retcode);
544 }
545
546 static void
usage()547 usage() /* print usage message and quit */
548 {
549 static char usage1[] = "ps [ -aceglnrSuUvwx ] [ -t term ] [ num ]";
550
551 (void) fprintf(stderr, "usage: %s\n", usage1);
552 exit(1);
553 }
554
555 /*
556 * Read the process arguments from the process.
557 * This allows >PRARGSZ characters of arguments to be displayed but,
558 * unlike pr_psargs[], the process may have changed them.
559 */
560 #define NARG 100
561 static int
preadargs(int pfd,psinfo_t * psinfo,char * psargs)562 preadargs(int pfd, psinfo_t *psinfo, char *psargs)
563 {
564 off_t argvoff = (off_t)psinfo->pr_argv;
565 size_t len;
566 char *psa = psargs;
567 int bsize = twidth;
568 int narg = NARG;
569 off_t argv[NARG];
570 off_t argoff;
571 off_t nextargoff;
572 int i;
573 #ifdef _LP64
574 caddr32_t argv32[NARG];
575 int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64);
576 #endif
577
578 if (psinfo->pr_nlwp == 0 ||
579 strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0)
580 goto out;
581
582 (void) memset(psa, 0, bsize--);
583 nextargoff = 0;
584 errno = EIO;
585 while (bsize > 0) {
586 if (narg == NARG) {
587 (void) memset(argv, 0, sizeof (argv));
588 #ifdef _LP64
589 if (is32) {
590 if ((i = pread(pfd, argv32, sizeof (argv32),
591 argvoff)) <= 0) {
592 if (i == 0 || errno == EIO)
593 break;
594 return (-1);
595 }
596 for (i = 0; i < NARG; i++)
597 argv[i] = argv32[i];
598 } else
599 #endif
600 if ((i = pread(pfd, argv, sizeof (argv),
601 argvoff)) <= 0) {
602 if (i == 0 || errno == EIO)
603 break;
604 return (-1);
605 }
606 narg = 0;
607 }
608 if ((argoff = argv[narg++]) == 0)
609 break;
610 if (argoff != nextargoff &&
611 (i = pread(pfd, psa, bsize, argoff)) <= 0) {
612 if (i == 0 || errno == EIO)
613 break;
614 return (-1);
615 }
616 len = strlen(psa);
617 psa += len;
618 *psa++ = ' ';
619 bsize -= len + 1;
620 nextargoff = argoff + len + 1;
621 #ifdef _LP64
622 argvoff += is32? sizeof (caddr32_t) : sizeof (caddr_t);
623 #else
624 argvoff += sizeof (caddr_t);
625 #endif
626 }
627 while (psa > psargs && isspace(*(psa-1)))
628 psa--;
629
630 out:
631 *psa = '\0';
632 if (strlen(psinfo->pr_psargs) > strlen(psargs))
633 (void) strcpy(psargs, psinfo->pr_psargs);
634
635 return (0);
636 }
637
638 /*
639 * Read environment variables from the process.
640 * Append them to psargs if there is room.
641 */
642 static int
preadenvs(int pfd,psinfo_t * psinfo,char * psargs)643 preadenvs(int pfd, psinfo_t *psinfo, char *psargs)
644 {
645 off_t envpoff = (off_t)psinfo->pr_envp;
646 int len;
647 char *psa;
648 char *psainit;
649 int bsize;
650 int nenv = NARG;
651 off_t envp[NARG];
652 off_t envoff;
653 off_t nextenvoff;
654 int i;
655 #ifdef _LP64
656 caddr32_t envp32[NARG];
657 int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64);
658 #endif
659
660 psainit = psa = (psargs != NULL)? psargs : psinfo->pr_psargs;
661 len = strlen(psa);
662 psa += len;
663 bsize = twidth - len - 1;
664
665 if (bsize <= 0 || psinfo->pr_nlwp == 0 ||
666 strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0)
667 return (0);
668
669 nextenvoff = 0;
670 errno = EIO;
671 while (bsize > 0) {
672 if (nenv == NARG) {
673 (void) memset(envp, 0, sizeof (envp));
674 #ifdef _LP64
675 if (is32) {
676 if ((i = pread(pfd, envp32, sizeof (envp32),
677 envpoff)) <= 0) {
678 if (i == 0 || errno == EIO)
679 break;
680 return (-1);
681 }
682 for (i = 0; i < NARG; i++)
683 envp[i] = envp32[i];
684 } else
685 #endif
686 if ((i = pread(pfd, envp, sizeof (envp),
687 envpoff)) <= 0) {
688 if (i == 0 || errno == EIO)
689 break;
690 return (-1);
691 }
692 nenv = 0;
693 }
694 if ((envoff = envp[nenv++]) == 0)
695 break;
696 if (envoff != nextenvoff &&
697 (i = pread(pfd, psa+1, bsize, envoff)) <= 0) {
698 if (i == 0 || errno == EIO)
699 break;
700 return (-1);
701 }
702 *psa++ = ' ';
703 len = strlen(psa);
704 psa += len;
705 bsize -= len + 1;
706 nextenvoff = envoff + len + 1;
707 #ifdef _LP64
708 envpoff += is32? sizeof (caddr32_t) : sizeof (caddr_t);
709 #else
710 envpoff += sizeof (caddr_t);
711 #endif
712 }
713 while (psa > psainit && isspace(*(psa-1)))
714 psa--;
715 *psa = '\0';
716
717 return (0);
718 }
719
720 /*
721 * getarg() finds the next argument in list and copies arg into argbuf.
722 * p1 first pts to arg passed back from getopt routine. p1 is then
723 * bumped to next character that is not a comma or blank -- p1 NULL
724 * indicates end of list.
725 */
726
727 static void
getarg()728 getarg()
729 {
730 char *parga;
731 int c;
732
733 while ((c = *p1) != '\0' && (c == ',' || isspace(c)))
734 p1++;
735
736 parga = argbuf;
737 while ((c = *p1) != '\0' && c != ',' && !isspace(c)) {
738 if (parga < argbuf + ARGSIZ - 1)
739 *parga++ = c;
740 p1++;
741 }
742 *parga = '\0';
743
744 while ((c = *p1) != '\0' && (c == ',' || isspace(c)))
745 p1++;
746 }
747
748 static char *
devlookup(dev_t ddev)749 devlookup(dev_t ddev)
750 {
751 struct devl *dp;
752 int i;
753
754 for (dp = devl, i = 0; i < ndev; dp++, i++) {
755 if (dp->ddev == ddev)
756 return (dp->dname);
757 }
758 return (NULL);
759 }
760
761 static char *
devadd(char * name,dev_t ddev)762 devadd(char *name, dev_t ddev)
763 {
764 struct devl *dp;
765 int leng, start, i;
766
767 if (ndev == maxdev) {
768 maxdev += DNINCR;
769 devl = realloc(devl, maxdev * sizeof (struct devl));
770 if (devl == NULL) {
771 (void) fprintf(stderr,
772 "ps: not enough memory for %d devices\n", maxdev);
773 exit(1);
774 }
775 }
776 dp = &devl[ndev++];
777
778 dp->ddev = ddev;
779 if (name == NULL) {
780 (void) strcpy(dp->dname, "??");
781 return (dp->dname);
782 }
783
784 leng = strlen(name);
785 /* Strip off /dev/ */
786 if (leng < DNSIZE + 4)
787 (void) strcpy(dp->dname, &name[5]);
788 else {
789 start = leng - (DNSIZE - 1);
790
791 for (i = start; i < leng && name[i] != '/'; i++)
792 ;
793 if (i == leng)
794 (void) strlcpy(dp->dname, &name[start], DNSIZE);
795 else
796 (void) strlcpy(dp->dname, &name[i+1], DNSIZE);
797 }
798 return (dp->dname);
799 }
800
801 /*
802 * gettty returns the user's tty number or ? if none.
803 */
804 static char *
gettty(psinfo_t * psinfo)805 gettty(psinfo_t *psinfo)
806 {
807 extern char *_ttyname_dev(dev_t, char *, size_t);
808 static zoneid_t zid = -1;
809 char devname[TTYNAME_MAX];
810 char *retval;
811
812 if (zid == -1)
813 zid = getzoneid();
814
815 if (psinfo->pr_ttydev == PRNODEV || psinfo->pr_zoneid != zid)
816 return ("?");
817
818 if ((retval = devlookup(psinfo->pr_ttydev)) != NULL)
819 return (retval);
820
821 retval = _ttyname_dev(psinfo->pr_ttydev, devname, sizeof (devname));
822
823 return (devadd(retval, psinfo->pr_ttydev));
824 }
825
826 /*
827 * Print percent from 16-bit binary fraction [0 .. 1]
828 * Round up .01 to .1 to indicate some small percentage (the 0x7000 below).
829 */
830 static void
prtpct(ushort_t pct)831 prtpct(ushort_t pct)
832 {
833 uint_t value = pct; /* need 32 bits to compute with */
834
835 value = ((value * 1000) + 0x7000) >> 15; /* [0 .. 1000] */
836 (void) printf("%3u.%u", value / 10, value % 10);
837 }
838
839 /*
840 * Print info about the process.
841 */
842 static int
prcom(int found,psinfo_t * psinfo,char * psargs)843 prcom(int found, psinfo_t *psinfo, char *psargs)
844 {
845 char *cp;
846 char *tp;
847 char *psa;
848 long tm;
849 int i, wcnt, length;
850 wchar_t wchar;
851 struct tty *ttyp;
852
853 /*
854 * If process is zombie, call print routine and return.
855 */
856 if (psinfo->pr_nlwp == 0) {
857 if (tflg && !found)
858 return (0);
859 else {
860 przom(psinfo);
861 return (1);
862 }
863 }
864
865 /*
866 * Get current terminal. If none ("?") and 'a' is set, don't print
867 * info. If 't' is set, check if term is in list of desired terminals
868 * and print it if it is.
869 */
870 i = 0;
871 tp = gettty(psinfo);
872
873 if (*tp == '?' && !found && !xflg)
874 return (0);
875
876 if (!(*tp == '?' && aflg) && tflg && !found) {
877 int match = 0;
878 char *other = NULL;
879 for (ttyp = tty; ttyp->tname != NULL; ttyp++) {
880 /*
881 * Look for a name match
882 */
883 if (strcmp(tp, ttyp->tname) == 0) {
884 match = 1;
885 break;
886 }
887 /*
888 * Look for same device under different names.
889 */
890 if ((other == NULL) &&
891 (psinfo->pr_ttydev == ttyp->tdev))
892 other = ttyp->tname;
893 }
894 if (!match) {
895 if (other == NULL)
896 return (0);
897 tp = other;
898 }
899 }
900
901 if (lflg)
902 (void) printf("%2x", psinfo->pr_flag & 0377);
903 if (uflg) {
904 if (!nflg) {
905 struct passwd *pwd;
906
907 if ((pwd = getpwuid(psinfo->pr_euid)) != NULL)
908 /* USER */
909 (void) printf("%-8.8s", pwd->pw_name);
910 else
911 /* UID */
912 (void) printf(" %7.7d", (int)psinfo->pr_euid);
913 } else {
914 (void) printf(" %5d", (int)psinfo->pr_euid); /* UID */
915 }
916 } else if (lflg)
917 (void) printf(" %5d", (int)psinfo->pr_euid); /* UID */
918
919 (void) printf("%*d", pidwidth + 1, (int)psinfo->pr_pid); /* PID */
920 if (lflg)
921 (void) printf("%*d", pidwidth + 1,
922 (int)psinfo->pr_ppid); /* PPID */
923 if (lflg)
924 (void) printf("%3d", psinfo->pr_lwp.pr_cpu & 0377); /* CP */
925 if (uflg) {
926 prtpct(psinfo->pr_pctcpu); /* %CPU */
927 prtpct(psinfo->pr_pctmem); /* %MEM */
928 }
929 if (lflg) {
930 (void) printf("%4d", psinfo->pr_lwp.pr_pri); /* PRI */
931 (void) printf("%3d", psinfo->pr_lwp.pr_nice); /* NICE */
932 }
933 if (lflg || uflg) {
934 if (psinfo->pr_flag & SSYS) /* SZ */
935 (void) printf(" 0");
936 else if (psinfo->pr_size)
937 (void) printf(" %4lu", (ulong_t)psinfo->pr_size);
938 else
939 (void) printf(" ?");
940 if (psinfo->pr_flag & SSYS) /* RSS */
941 (void) printf(" 0");
942 else if (psinfo->pr_rssize)
943 (void) printf(" %4lu", (ulong_t)psinfo->pr_rssize);
944 else
945 (void) printf(" ?");
946 }
947 if (lflg) { /* WCHAN */
948 if (psinfo->pr_lwp.pr_sname != 'S') {
949 (void) printf(" ");
950 } else if (psinfo->pr_lwp.pr_wchan) {
951 (void) printf(" %+8.8lx",
952 (ulong_t)psinfo->pr_lwp.pr_wchan);
953 } else {
954 (void) printf(" ?");
955 }
956 }
957 if ((tplen = strlen(tp)) > 9)
958 maxlen = twidth - tplen + 9;
959 else
960 maxlen = twidth;
961
962 if (!lflg)
963 (void) printf(" %-8.14s", tp); /* TTY */
964 (void) printf(" %c", psinfo->pr_lwp.pr_sname); /* STATE */
965 if (lflg)
966 (void) printf(" %-8.14s", tp); /* TTY */
967 if (uflg)
968 prtime(psinfo->pr_start); /* START */
969
970 /* time just for process */
971 tm = psinfo->pr_time.tv_sec;
972 if (Sflg) { /* calculate time for process and all reaped children */
973 tm += psinfo->pr_ctime.tv_sec;
974 if (psinfo->pr_time.tv_nsec + psinfo->pr_ctime.tv_nsec
975 >= 1000000000)
976 tm += 1;
977 }
978
979 (void) printf(" %2ld:%.2ld", tm / 60, tm % 60); /* TIME */
980
981 if (vflg) {
982 if (psinfo->pr_flag & SSYS) /* SZ */
983 (void) printf(" 0");
984 else if (psinfo->pr_size)
985 (void) printf("%5lu", (ulong_t)psinfo->pr_size);
986 else
987 (void) printf(" ?");
988 if (psinfo->pr_flag & SSYS) /* SZ */
989 (void) printf(" 0");
990 else if (psinfo->pr_rssize)
991 (void) printf("%5lu", (ulong_t)psinfo->pr_rssize);
992 else
993 (void) printf(" ?");
994 prtpct(psinfo->pr_pctcpu); /* %CPU */
995 prtpct(psinfo->pr_pctmem); /* %MEM */
996 }
997 if (cflg) { /* CMD */
998 wcnt = namencnt(psinfo->pr_fname, 16, maxlen);
999 (void) printf(" %.*s", wcnt, psinfo->pr_fname);
1000 return (1);
1001 }
1002 /*
1003 * PRARGSZ == length of cmd arg string.
1004 */
1005 if (psargs == NULL) {
1006 psa = &psinfo->pr_psargs[0];
1007 i = PRARGSZ;
1008 tp = &psinfo->pr_psargs[PRARGSZ];
1009 } else {
1010 psa = psargs;
1011 i = strlen(psargs);
1012 tp = psa + i;
1013 }
1014
1015 for (cp = psa; cp < tp; /* empty */) {
1016 if (*cp == 0)
1017 break;
1018 length = mbtowc(&wchar, cp, MB_LEN_MAX);
1019 if (length < 0 || !iswprint(wchar)) {
1020 (void) printf(" [ %.16s ]", psinfo->pr_fname);
1021 return (1);
1022 }
1023 cp += length;
1024 }
1025 wcnt = namencnt(psa, i, maxlen);
1026 #if 0
1027 /* dumps core on really long strings */
1028 (void) printf(" %.*s", wcnt, psa);
1029 #else
1030 (void) putchar(' ');
1031 (void) fwrite(psa, 1, wcnt, stdout);
1032 #endif
1033 return (1);
1034 }
1035
1036 /*
1037 * Print starting time of process unless process started more than 24 hours
1038 * ago, in which case the date is printed.
1039 */
1040 static void
prtime(timestruc_t st)1041 prtime(timestruc_t st)
1042 {
1043 char sttim[26];
1044 static time_t tim = 0L;
1045 time_t starttime;
1046
1047 if (tim == 0L)
1048 tim = time((time_t *)0);
1049 starttime = st.tv_sec;
1050 if (tim - starttime > 24*60*60) {
1051 (void) strftime(sttim, sizeof (sttim), "%b %d",
1052 localtime(&starttime));
1053 } else {
1054 (void) strftime(sttim, sizeof (sttim), "%H:%M:%S",
1055 localtime(&starttime));
1056 }
1057 (void) printf("%9.9s", sttim);
1058 }
1059
1060 static void
przom(psinfo_t * psinfo)1061 przom(psinfo_t *psinfo)
1062 {
1063 long tm;
1064
1065 if (lflg)
1066 (void) printf("%2x", psinfo->pr_flag & 0377);
1067 if (uflg) {
1068 struct passwd *pwd;
1069
1070 if ((pwd = getpwuid(psinfo->pr_euid)) != NULL)
1071 (void) printf("%-8.8s", pwd->pw_name); /* USER */
1072 else
1073 (void) printf(" %7.7d", (int)psinfo->pr_euid); /* UID */
1074 } else if (lflg)
1075 (void) printf(" %5d", (int)psinfo->pr_euid); /* UID */
1076
1077 (void) printf("%*d", pidwidth + 1, (int)psinfo->pr_pid); /* PID */
1078 if (lflg)
1079 (void) printf("%*d", pidwidth + 1,
1080 (int)psinfo->pr_ppid); /* PPID */
1081 if (lflg)
1082 (void) printf(" 0"); /* CP */
1083 if (uflg) {
1084 prtpct(0); /* %CPU */
1085 prtpct(0); /* %MEM */
1086 }
1087 if (lflg) {
1088 (void) printf("%4d", psinfo->pr_lwp.pr_pri); /* PRI */
1089 (void) printf(" "); /* NICE */
1090 }
1091 if (lflg || uflg) {
1092 (void) printf(" 0"); /* SZ */
1093 (void) printf(" 0"); /* RSS */
1094 }
1095 if (lflg)
1096 (void) printf(" "); /* WCHAN */
1097 (void) printf(" "); /* TTY */
1098 (void) printf("%c", psinfo->pr_lwp.pr_sname); /* STATE */
1099 if (uflg)
1100 (void) printf(" "); /* START */
1101
1102 /* time just for process */
1103 tm = psinfo->pr_time.tv_sec;
1104 if (Sflg) { /* calculate time for process and all reaped children */
1105 tm += psinfo->pr_ctime.tv_sec;
1106 if (psinfo->pr_time.tv_nsec + psinfo->pr_ctime.tv_nsec
1107 >= 1000000000)
1108 tm += 1;
1109 }
1110 (void) printf(" %2ld:%.2ld", tm / 60, tm % 60); /* TIME */
1111
1112 if (vflg) {
1113 (void) printf(" 0"); /* SZ */
1114 (void) printf(" 0"); /* RSS */
1115 prtpct(0); /* %CPU */
1116 prtpct(0); /* %MEM */
1117 }
1118 (void) printf(" %.*s", maxlen, " <defunct>");
1119 }
1120
1121 /*
1122 * Returns true iff string is all numeric.
1123 */
1124 static int
num(char * s)1125 num(char *s)
1126 {
1127 int c;
1128
1129 if (s == NULL)
1130 return (0);
1131 c = *s;
1132 do {
1133 if (!isdigit(c))
1134 return (0);
1135 } while ((c = *++s) != '\0');
1136 return (1);
1137 }
1138
1139 /*
1140 * Function to compute the number of printable bytes in a multibyte
1141 * command string ("internationalization").
1142 */
1143 static int
namencnt(char * cmd,int eucsize,int scrsize)1144 namencnt(char *cmd, int eucsize, int scrsize)
1145 {
1146 int eucwcnt = 0, scrwcnt = 0;
1147 int neucsz, nscrsz;
1148 wchar_t wchar;
1149
1150 while (*cmd != '\0') {
1151 if ((neucsz = mbtowc(&wchar, cmd, MB_LEN_MAX)) < 0)
1152 return (8); /* default to use for illegal chars */
1153 if ((nscrsz = scrwidth(wchar)) == 0)
1154 return (8);
1155 if (eucwcnt + neucsz > eucsize || scrwcnt + nscrsz > scrsize)
1156 break;
1157 eucwcnt += neucsz;
1158 scrwcnt += nscrsz;
1159 cmd += neucsz;
1160 }
1161 return (eucwcnt);
1162 }
1163
1164 static int
pscompare(const void * v1,const void * v2)1165 pscompare(const void *v1, const void *v2)
1166 {
1167 const struct psent *p1 = v1;
1168 const struct psent *p2 = v2;
1169 int i;
1170
1171 if (uflg)
1172 i = p2->psinfo->pr_pctcpu - p1->psinfo->pr_pctcpu;
1173 else if (vflg)
1174 i = p2->psinfo->pr_rssize - p1->psinfo->pr_rssize;
1175 else
1176 i = p1->psinfo->pr_ttydev - p2->psinfo->pr_ttydev;
1177 if (i == 0)
1178 i = p1->psinfo->pr_pid - p2->psinfo->pr_pid;
1179 return (i);
1180 }
1181
1182 static char *
err_string(int err)1183 err_string(int err)
1184 {
1185 static char buf[32];
1186 char *str = strerror(err);
1187
1188 if (str == NULL)
1189 (void) sprintf(str = buf, "Errno #%d", err);
1190
1191 return (str);
1192 }
1193