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