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