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 (c) 2013 Gary Mills
23 *
24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 *
27 * Copyright 2020 Joyent, Inc.
28 */
29
30 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
31 /* All Rights Reserved */
32
33 /*
34 * University Copyright- Copyright (c) 1982, 1986, 1988
35 * The Regents of the University of California
36 * All Rights Reserved
37 *
38 * University Acknowledgment- Portions of this document are derived from
39 * software developed by the University of California, Berkeley, and its
40 * contributors.
41 */
42
43 /*
44 * This is the new whodo command which takes advantage of
45 * the /proc interface to gain access to the information
46 * of all the processes currently on the system.
47 *
48 * Maintenance note:
49 *
50 * Much of this code is replicated in w.c. If you're
51 * fixing bugs here, then you should probably fix 'em there too.
52 */
53
54 #include <stdio.h>
55 #include <string.h>
56 #include <stdlib.h>
57 #include <ctype.h>
58 #include <fcntl.h>
59 #include <time.h>
60 #include <err.h>
61 #include <errno.h>
62 #include <sys/types.h>
63 #include <utmpx.h>
64 #include <sys/sysmacros.h>
65 #include <sys/utsname.h>
66 #include <sys/stat.h>
67 #include <sys/mkdev.h>
68 #include <dirent.h>
69 #include <procfs.h> /* /proc header file */
70 #include <sys/wait.h>
71 #include <locale.h>
72 #include <unistd.h>
73 #include <limits.h>
74 #include <priv_utils.h>
75
76 /*
77 * Use the full lengths from utmpx for user and line.
78 */
79 #define NMAX (sizeof (((struct utmpx *)0)->ut_user))
80 #define LMAX (sizeof (((struct utmpx *)0)->ut_line))
81
82 /* Print minimum field widths. */
83 #define LOGIN_WIDTH 8
84 #define LINE_WIDTH 8
85
86 #define DIV60(t) ((t+30)/60) /* x/60 rounded */
87
88 #ifdef ERR
89 #undef ERR
90 #endif
91 #define ERR (-1)
92
93 #define DEVNAMELEN 14
94 #define HSIZE 256 /* size of process hash table */
95 #define PROCDIR "/proc"
96 #define INITPROCESS (pid_t)1 /* init process pid */
97 #define NONE 'n' /* no state */
98 #define RUNNING 'r' /* runnable process */
99 #define ZOMBIE 'z' /* zombie process */
100 #define VISITED 'v' /* marked node as visited */
101
102 static uint_t ndevs; /* number of configured devices */
103 static uint_t maxdev; /* slots for configured devices */
104 #define DNINCR 100
105 static struct devl { /* device list */
106 char dname[DEVNAMELEN]; /* device name */
107 dev_t ddev; /* device number */
108 } *devl;
109
110 struct uproc {
111 pid_t p_upid; /* user process id */
112 char p_state; /* numeric value of process state */
113 dev_t p_ttyd; /* controlling tty of process */
114 time_t p_time; /* ticks of user & system time */
115 time_t p_ctime; /* ticks of child user & system time */
116 int p_igintr; /* 1=ignores SIGQUIT and SIGINT */
117 char p_comm[PRARGSZ+1]; /* command */
118 char p_args[PRARGSZ+1]; /* command line arguments */
119 struct uproc *p_child, /* first child pointer */
120 *p_sibling, /* sibling pointer */
121 *p_pgrplink, /* pgrp link */
122 *p_link; /* hash table chain pointer */
123 };
124
125 /*
126 * define hash table for struct uproc
127 * Hash function uses process id
128 * and the size of the hash table(HSIZE)
129 * to determine process index into the table.
130 */
131 static struct uproc pr_htbl[HSIZE];
132
133 static struct uproc *findhash(pid_t);
134 static time_t findidle(char *);
135 static void clnarglist(char *);
136 static void showproc(struct uproc *);
137 static void showtotals(struct uproc *);
138 static void calctotals(struct uproc *);
139 static char *getty(dev_t);
140 static void prttime(time_t, int);
141 static void prtat(time_t *);
142
143 static int priv_proc_open(const char *, int);
144 static int priv_proc_openat(int, const char *, int);
145 static boolean_t do_proc_read(int, void *, size_t);
146
147 static char *prog;
148 static int header = 1; /* true if -h flag: don't print heading */
149 static int lflag = 0; /* true if -l flag: w command format */
150 static char *sel_user; /* login of particular user selected */
151 static time_t now; /* current time of day */
152 static time_t uptime; /* time of last reboot & elapsed time since */
153 static int nusers; /* number of users logged in now */
154 static time_t idle; /* number of minutes user is idle */
155 static time_t jobtime; /* total cpu time visible */
156 static char doing[520]; /* process attached to terminal */
157 static time_t proctime; /* cpu time of process in doing */
158 static int empty;
159 static pid_t curpid;
160
161 /*
162 * Basic privs we never need and can drop. This is likely not exhaustive,
163 * but should significantly reduce any potential attack surfaces.
164 */
165 static const char *drop_privs[] = {
166 PRIV_FILE_WRITE,
167 PRIV_NET_ACCESS,
168 PRIV_PROC_EXEC,
169 PRIV_PROC_FORK,
170 PRIV_FILE_LINK_ANY
171 };
172
173 #if SIGQUIT > SIGINT
174 #define ACTSIZE SIGQUIT
175 #else
176 #define ACTSIZE SIGINT
177 #endif
178
179 int
main(int argc,char * argv[])180 main(int argc, char *argv[])
181 {
182 struct utmpx *ut;
183 struct utmpx *utmpbegin;
184 struct utmpx *utmpend;
185 struct utmpx *utp;
186 struct tm *tm;
187 struct uproc *up, *parent, *pgrp;
188 struct psinfo info;
189 struct sigaction actinfo[ACTSIZE];
190 struct pstatus statinfo;
191 struct stat sbuf;
192 struct utsname uts;
193 DIR *dirp;
194 struct dirent *dp;
195 char pname[PATH_MAX];
196 int procfd, dirfd;
197 int i;
198 int days, hrs, mins;
199 int entries;
200 priv_set_t *pset;
201
202 if (__init_suid_priv(PU_CLEARLIMITSET, PRIV_PROC_OWNER, NULL) != 0) {
203 err(EXIT_FAILURE, "failed to enable privilege bracketing");
204 }
205
206 /*
207 * After setting up privilege bracketing, we can further reduce the
208 * privileges in use. The effective set is set to the basic set minus
209 * the privs in drop_privs. The permitted set is the effective set
210 * plus PRIV_PROC_OWNER (i.e. the privilege being bracketed).
211 */
212 pset = priv_allocset();
213 if (pset == NULL)
214 err(EXIT_FAILURE, "priv_allocset failed");
215
216 priv_basicset(pset);
217 for (i = 0; i < ARRAY_SIZE(drop_privs); i++) {
218 if (priv_delset(pset, drop_privs[i]) != 0) {
219 err(EXIT_FAILURE,
220 "failed to remove %s privilege from privilege set",
221 drop_privs[i]);
222 }
223 }
224
225 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pset) < 0)
226 err(EXIT_FAILURE, "failed setting effective privilege set");
227
228 if (priv_addset(pset, PRIV_PROC_OWNER) != 0) {
229 err(EXIT_FAILURE,
230 "failed to add PRIV_PROC_OWNER privilege to privilege set");
231 }
232
233 if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) < 0)
234 err(EXIT_FAILURE, "failed to set permitted privilege set");
235
236 /*
237 * Unfortunately, when run as root, privilege bracketing is a no-op,
238 * so we have to add PRIV_PROC_OWNER into our effective set for things
239 * to work.
240 */
241 if (getuid() == 0 && setppriv(PRIV_SET, PRIV_EFFECTIVE, pset) < 0) {
242 err(EXIT_FAILURE, "failed to set effective privilege set");
243 }
244
245 priv_freeset(pset);
246 pset = NULL;
247
248 (void) setlocale(LC_ALL, "");
249 #if !defined(TEXT_DOMAIN)
250 #define TEXT_DOMAIN "SYS_TEST"
251 #endif
252 (void) textdomain(TEXT_DOMAIN);
253
254 prog = argv[0];
255
256 while (argc > 1) {
257 if (argv[1][0] == '-') {
258 for (i = 1; argv[1][i]; i++) {
259 switch (argv[1][i]) {
260
261 case 'h':
262 header = 0;
263 break;
264
265 case 'l':
266 lflag++;
267 break;
268
269 default:
270 (void) printf(gettext(
271 "usage: %s [ -hl ] [ user ]\n"),
272 prog);
273 exit(1);
274 }
275 }
276 } else {
277 if (!isalnum(argv[1][0]) || argc > 2) {
278 (void) printf(gettext(
279 "usage: %s [ -hl ] [ user ]\n"), prog);
280 exit(1);
281 } else
282 sel_user = argv[1];
283 }
284 argc--; argv++;
285 }
286
287 /*
288 * read the UTMPX_FILE (contains information about
289 * each logged in user)
290 */
291 if (stat(UTMPX_FILE, &sbuf) < 0)
292 err(EXIT_FAILURE, gettext("stat error of %s"), UTMPX_FILE);
293
294 entries = sbuf.st_size / sizeof (struct futmpx);
295
296 if ((ut = calloc(entries, sizeof (struct utmpx))) == NULL)
297 err(EXIT_FAILURE, gettext("calloc error of %s"), UTMPX_FILE);
298
299 (void) utmpxname(UTMPX_FILE);
300
301 utmpbegin = ut;
302 utmpend = utmpbegin + entries;
303
304 setutxent();
305 while ((ut < utmpend) && ((utp = getutxent()) != NULL))
306 (void) memcpy(ut++, utp, sizeof (*ut));
307 endutxent();
308
309 (void) time(&now); /* get current time */
310
311 if (header) { /* print a header */
312 if (lflag) { /* w command format header */
313 prtat(&now);
314 for (ut = utmpbegin; ut < utmpend; ut++) {
315 if (ut->ut_type == USER_PROCESS) {
316 nusers++;
317 } else if (ut->ut_type == BOOT_TIME) {
318 uptime = now - ut->ut_xtime;
319 uptime += 30;
320 days = uptime / (60*60*24);
321 uptime %= (60*60*24);
322 hrs = uptime / (60*60);
323 uptime %= (60*60);
324 mins = uptime / 60;
325
326 (void) printf(dcgettext(NULL,
327 "up %d day(s), %d hr(s), "
328 "%d min(s)", LC_TIME),
329 days, hrs, mins);
330 }
331 }
332
333 ut = utmpbegin; /* rewind utmp data */
334 (void) printf(dcgettext(NULL,
335 " %d user(s)\n", LC_TIME), nusers);
336 (void) printf(dcgettext(NULL, "User tty "
337 "login@ idle JCPU PCPU what\n",
338 LC_TIME));
339 } else { /* standard whodo header */
340 char date_buf[100];
341
342 /*
343 * print current time and date
344 */
345 (void) strftime(date_buf, sizeof (date_buf),
346 "%c", localtime(&now));
347 (void) printf("%s\n", date_buf);
348
349 /*
350 * print system name
351 */
352 (void) uname(&uts);
353 (void) printf("%s\n", uts.nodename);
354 }
355 }
356
357 /*
358 * loop through /proc, reading info about each process
359 * and build the parent/child tree
360 */
361 if ((dirp = opendir(PROCDIR)) == NULL)
362 err(EXIT_FAILURE, gettext("could not open %s"), PROCDIR);
363
364 while ((dp = readdir(dirp)) != NULL) {
365 if (dp->d_name[0] == '.')
366 continue;
367
368 if (snprintf(pname, sizeof (pname), "%s/%s", PROCDIR,
369 dp->d_name) > sizeof (pname))
370 continue;
371
372 dirfd = priv_proc_open(pname, O_RDONLY | O_DIRECTORY);
373
374 if (dirfd < 0)
375 continue;
376
377 procfd = priv_proc_openat(dirfd, "psinfo", O_RDONLY);
378 if (procfd < 0) {
379 (void) close(dirfd);
380 continue;
381 }
382
383 if (!do_proc_read(procfd, &info, sizeof (info))) {
384 warn(gettext("read() failed on %s"), pname);
385 (void) close(dirfd);
386 continue;
387 }
388 (void) close(procfd);
389
390 up = findhash(info.pr_pid);
391 up->p_ttyd = info.pr_ttydev;
392 up->p_state = (info.pr_nlwp == 0? ZOMBIE : RUNNING);
393 up->p_time = 0;
394 up->p_ctime = 0;
395 up->p_igintr = 0;
396 (void) strlcpy(up->p_comm, info.pr_fname,
397 sizeof (up->p_comm));
398 up->p_args[0] = 0;
399
400 if (up->p_state != NONE && up->p_state != ZOMBIE) {
401 procfd = priv_proc_openat(dirfd, "status", O_RDONLY);
402 if (procfd < 0) {
403 (void) close(dirfd);
404 continue;
405 }
406
407 if (!do_proc_read(procfd, &statinfo,
408 sizeof (statinfo))) {
409 warn(gettext("read() failed on %s/status"),
410 pname);
411
412 (void) close(procfd);
413 (void) close(dirfd);
414 continue;
415 }
416 (void) close(procfd);
417
418 up->p_time = statinfo.pr_utime.tv_sec +
419 statinfo.pr_stime.tv_sec;
420 up->p_ctime = statinfo.pr_cutime.tv_sec +
421 statinfo.pr_cstime.tv_sec;
422
423 procfd = priv_proc_openat(dirfd, "sigact", O_RDONLY);
424 if (procfd < 0) {
425 (void) close(dirfd);
426 continue;
427 }
428
429 if (!do_proc_read(procfd, actinfo, sizeof (actinfo))) {
430 warn(gettext("read() failed on %s/sigact"),
431 pname);
432
433 (void) close(procfd);
434 (void) close(dirfd);
435 continue;
436 }
437 (void) close(procfd);
438 (void) close(dirfd);
439
440 up->p_igintr =
441 actinfo[SIGINT-1].sa_handler == SIG_IGN &&
442 actinfo[SIGQUIT-1].sa_handler == SIG_IGN;
443
444 up->p_args[0] = 0;
445
446 /*
447 * Process args if there's a chance we'll print it.
448 */
449 if (lflag) { /* w command needs args */
450 clnarglist(info.pr_psargs);
451 (void) strlcpy(up->p_args, info.pr_psargs,
452 sizeof (up->p_args));
453 if (up->p_args[0] == 0 ||
454 up->p_args[0] == '-' &&
455 up->p_args[1] <= ' ' ||
456 up->p_args[0] == '?') {
457 (void) strlcat(up->p_args, " (",
458 sizeof (up->p_args));
459 (void) strlcat(up->p_args, up->p_comm,
460 sizeof (up->p_args));
461 (void) strlcat(up->p_args, ")",
462 sizeof (up->p_args));
463 }
464 }
465
466 }
467
468 /*
469 * link pgrp together in case parents go away
470 * Pgrp chain is a single linked list originating
471 * from the pgrp leader to its group member.
472 */
473 if (info.pr_pgid != info.pr_pid) { /* not pgrp leader */
474 pgrp = findhash(info.pr_pgid);
475 up->p_pgrplink = pgrp->p_pgrplink;
476 pgrp->p_pgrplink = up;
477 }
478 parent = findhash(info.pr_ppid);
479
480 /* if this is the new member, link it in */
481 if (parent->p_upid != INITPROCESS) {
482 if (parent->p_child) {
483 up->p_sibling = parent->p_child;
484 up->p_child = 0;
485 }
486 parent->p_child = up;
487 }
488
489 }
490
491 /* revert to non-privileged user */
492 __priv_relinquish();
493 if (getuid() == 0) {
494 /*
495 * Since the privilege bracketing functions are effectively
496 * no-ops when running as root, we must explicitly
497 * relinquish PRIV_PROC_OWNER ourselves.
498 */
499 pset = priv_allocset();
500 if (pset == NULL) {
501 err(EXIT_FAILURE,
502 gettext("failed to allocate privilege set"));
503 }
504
505 priv_emptyset(pset);
506
507 if (priv_addset(pset, PRIV_PROC_OWNER) != 0) {
508 err(EXIT_FAILURE, gettext("failed to add "
509 "PRIV_PROC_OWNER to privilege set"));
510 }
511
512 if (setppriv(PRIV_OFF, PRIV_PERMITTED, pset) != 0) {
513 err(EXIT_FAILURE,
514 gettext("failed to set permitted privilege set"));
515 }
516
517 priv_freeset(pset);
518 pset = NULL;
519 }
520
521 (void) closedir(dirp);
522 (void) time(&now); /* get current time */
523
524 /*
525 * loop through utmpx file, printing process info
526 * about each logged in user
527 */
528 for (ut = utmpbegin; ut < utmpend; ut++) {
529 time_t tim;
530
531 if (ut->ut_type != USER_PROCESS)
532 continue;
533 if (sel_user && strncmp(ut->ut_name, sel_user, NMAX) != 0)
534 continue; /* we're looking for somebody else */
535 if (lflag) { /* -l flag format (w command) */
536 /* print login name of the user */
537 (void) printf("%-*.*s ", LOGIN_WIDTH, (int)NMAX,
538 ut->ut_name);
539
540 /* print tty user is on */
541 (void) printf("%-*.*s ", LINE_WIDTH, (int)LMAX,
542 ut->ut_line);
543
544 /* print when the user logged in */
545 tim = ut->ut_xtime;
546 (void) prtat(&tim);
547
548 /* print idle time */
549 idle = findidle(ut->ut_line);
550 prttime(idle, 8);
551 showtotals(findhash((pid_t)ut->ut_pid));
552 } else { /* standard whodo format */
553 tim = ut->ut_xtime;
554 tm = localtime(&tim);
555 (void) printf("\n%-*.*s %-*.*s %2.1d:%2.2d\n",
556 LINE_WIDTH, (int)LMAX, ut->ut_line,
557 LOGIN_WIDTH, (int)NMAX, ut->ut_name, tm->tm_hour,
558 tm->tm_min);
559 showproc(findhash((pid_t)ut->ut_pid));
560 }
561 }
562
563 return (0);
564 }
565
566 /*
567 * Used for standard whodo format.
568 * This is the recursive routine descending the process
569 * tree starting from the given process pointer(up).
570 * It used depth-first search strategy and also marked
571 * each node as printed as it traversed down the tree.
572 */
573 static void
showproc(struct uproc * up)574 showproc(struct uproc *up)
575 {
576 struct uproc *zp;
577
578 if (up->p_state == VISITED) /* we already been here */
579 return;
580 /* print the data for this process */
581 if (up->p_state == ZOMBIE)
582 (void) printf(" %-*.*s %5d %4.1ld:%2.2ld %s\n",
583 LINE_WIDTH, (int)LMAX, " ?", (int)up->p_upid, 0L, 0L,
584 "<defunct>");
585 else if (up->p_state != NONE) {
586 (void) printf(" %-*.*s %5d %4.1ld:%2.2ld %s\n",
587 LINE_WIDTH, (int)LMAX, getty(up->p_ttyd), (int)up->p_upid,
588 up->p_time / 60L, up->p_time % 60L,
589 up->p_comm);
590 }
591 up->p_state = VISITED;
592
593 /* descend for its children */
594 if (up->p_child) {
595 showproc(up->p_child);
596 for (zp = up->p_child->p_sibling; zp; zp = zp->p_sibling) {
597 showproc(zp);
598 }
599 }
600
601 /* print the pgrp relation */
602 if (up->p_pgrplink)
603 showproc(up->p_pgrplink);
604 }
605
606
607 /*
608 * Used for -l flag (w command) format.
609 * Prints the CPU time for all processes & children,
610 * and the cpu time for interesting process,
611 * and what the user is doing.
612 */
613 static void
showtotals(struct uproc * up)614 showtotals(struct uproc *up)
615 {
616 jobtime = 0;
617 proctime = 0;
618 empty = 1;
619 curpid = -1;
620
621 /* default act: normally never prints */
622 (void) strlcpy(doing, "-", sizeof (doing));
623 calctotals(up);
624
625 /* print CPU time for all processes & children */
626 /* and need to convert clock ticks to seconds first */
627 prttime((time_t)jobtime, 8);
628
629 /* print cpu time for interesting process */
630 /* and need to convert clock ticks to seconds first */
631 prttime((time_t)proctime, 8);
632
633 /* what user is doing, current process */
634 (void) printf("%-.32s\n", doing);
635 }
636
637 /*
638 * Used for -l flag (w command) format.
639 * This recursive routine descends the process
640 * tree starting from the given process pointer(up).
641 * It used depth-first search strategy and also marked
642 * each node as visited as it traversed down the tree.
643 * It calculates the process time for all processes &
644 * children. It also finds the "interesting" process
645 * and determines its cpu time and command.
646 */
647 static void
calctotals(struct uproc * up)648 calctotals(struct uproc *up)
649 {
650 struct uproc *zp;
651
652 if (up->p_state == VISITED)
653 return;
654 up->p_state = VISITED;
655 if (up->p_state == NONE || up->p_state == ZOMBIE)
656 return;
657 jobtime += up->p_time + up->p_ctime;
658 proctime += up->p_time;
659
660 if (empty && !up->p_igintr) {
661 empty = 0;
662 curpid = -1;
663 }
664
665 if (up->p_upid > curpid && (!up->p_igintr || empty)) {
666 curpid = up->p_upid;
667 (void) strlcpy(doing, up->p_args, sizeof (doing));
668 }
669
670 /* descend for its children */
671 if (up->p_child) {
672 calctotals(up->p_child);
673 for (zp = up->p_child->p_sibling; zp; zp = zp->p_sibling)
674 calctotals(zp);
675 }
676 }
677
678 static char *
devadd(const char * name,dev_t ddev)679 devadd(const char *name, dev_t ddev)
680 {
681 struct devl *dp;
682 int leng, start, i;
683
684 if (ndevs == maxdev) {
685 uint_t newdev;
686
687 newdev = maxdev + DNINCR;
688 if (newdev < DNINCR)
689 errx(EXIT_FAILURE, gettext("devadd overflow"));
690
691 dp = recallocarray(devl, maxdev, newdev, sizeof (struct devl));
692 if (dp == NULL)
693 err(EXIT_FAILURE, gettext("out of memory!"));
694 maxdev = newdev;
695 devl = dp;
696 }
697 dp = &devl[ndevs++];
698
699 dp->ddev = ddev;
700 if (name == NULL) {
701 (void) strlcpy(dp->dname, " ? ", sizeof (dp->dname));
702 return (dp->dname);
703 }
704
705 leng = strlen(name);
706 if (leng < DEVNAMELEN + 4) {
707 /* strip off "/dev/" */
708 (void) strlcpy(dp->dname, &name[5], sizeof (dp->dname));
709 } else {
710 /* strip enough off the front to fit */
711 start = leng - DEVNAMELEN - 1;
712
713 for (i = start; i < leng && name[i] != '/'; i++)
714 ;
715 if (i == leng)
716 (void) strlcpy(dp->dname, &name[start], DEVNAMELEN);
717 else
718 (void) strlcpy(dp->dname, &name[i+1], DEVNAMELEN);
719 }
720 return (dp->dname);
721 }
722
723 static char *
devlookup(dev_t ddev)724 devlookup(dev_t ddev)
725 {
726 struct devl *dp;
727 int i;
728
729 for (dp = devl, i = 0; i < ndevs; dp++, i++) {
730 if (dp->ddev == ddev)
731 return (dp->dname);
732 }
733 return (NULL);
734 }
735
736 /*
737 * This routine gives back a corresponding device name
738 * from the device number given.
739 */
740 static char *
getty(dev_t dev)741 getty(dev_t dev)
742 {
743 extern char *_ttyname_dev(dev_t, char *, size_t);
744 char devname[TTYNAME_MAX];
745 char *retval;
746
747 if (dev == PRNODEV)
748 return (" ? ");
749
750 if ((retval = devlookup(dev)) != NULL)
751 return (retval);
752
753 retval = _ttyname_dev(dev, devname, sizeof (devname));
754 return (devadd(retval, dev));
755 }
756
757 /*
758 * Findhash finds the appropriate entry in the process
759 * hash table (pr_htbl) for the given pid in case that
760 * pid exists on the hash chain. It returns back a pointer
761 * to that uproc structure. If this is a new pid, it allocates
762 * a new node, initializes it, links it into the chain (after
763 * head) and returns a structure pointer.
764 */
765 static struct uproc *
findhash(pid_t pid)766 findhash(pid_t pid)
767 {
768 struct uproc *up, *tp;
769
770 tp = up = &pr_htbl[(int)pid % HSIZE];
771 if (up->p_upid == 0) { /* empty slot */
772 up->p_upid = pid;
773 up->p_state = NONE;
774 up->p_child = up->p_sibling = up->p_pgrplink = up->p_link = 0;
775 return (up);
776 }
777 if (up->p_upid == pid) { /* found in hash table */
778 return (up);
779 }
780 for (tp = up->p_link; tp; tp = tp->p_link) { /* follow chain */
781 if (tp->p_upid == pid) {
782 return (tp);
783 }
784 }
785 tp = malloc(sizeof (*tp)); /* add new node */
786 if (tp == NULL)
787 err(EXIT_FAILURE, gettext("out of memory!"));
788
789 (void) memset((char *)tp, 0, sizeof (*tp));
790 tp->p_upid = pid;
791 tp->p_state = NONE;
792 tp->p_child = tp->p_sibling = tp->p_pgrplink = (pid_t)0;
793 tp->p_link = up->p_link; /* insert after head */
794 up->p_link = tp;
795 return (tp);
796 }
797
798 #define HR (60 * 60)
799 #define DAY (24 * HR)
800 #define MON (30 * DAY)
801 #define PRINTF(a) (void) printf a
802
803 /*
804 * Prttime prints an elapsed time in hours, minutes, or seconds,
805 * right-justified with the rightmost column always blank.
806 * The second argument is the minimum field width.
807 */
808 static void
prttime(time_t tim,int width)809 prttime(time_t tim, int width)
810 {
811 char value[36];
812
813 if (tim >= 36 * 60) {
814 (void) snprintf(value, sizeof (value), "%d:%02d:%02d",
815 (int)tim / HR, (int)(tim % HR) / 60, (int)tim % 60);
816 } else if (tim >= 60) {
817 (void) snprintf(value, sizeof (value), "%d:%02d",
818 (int)tim / 60, (int)tim % 60);
819 } else if (tim > 0) {
820 (void) snprintf(value, sizeof (value), "%d", (int)tim);
821 } else {
822 (void) strcpy(value, "0");
823 }
824 width = (width > 2) ? width - 1 : 1;
825 PRINTF(("%*s ", width, value));
826 }
827
828 /*
829 * Prints the ISO date or time given a pointer to a time of day,
830 * left-justfied in a 12-character expanding field with the
831 * rightmost column always blank.
832 * Includes a dcgettext() override in case a message catalog is needed.
833 */
834 static void
prtat(time_t * time)835 prtat(time_t *time)
836 {
837 struct tm *p;
838
839 p = localtime(time);
840 if (now - *time <= 18 * HR) {
841 char timestr[50];
842
843 (void) strftime(timestr, sizeof (timestr),
844 dcgettext(NULL, "%T", LC_TIME), p);
845 PRINTF(("%-11s ", timestr));
846 } else if (now - *time <= 7 * DAY) {
847 char weekdaytime[20];
848
849 (void) strftime(weekdaytime, sizeof (weekdaytime),
850 dcgettext(NULL, "%a %H:%M", LC_TIME), p);
851 PRINTF(("%-11s ", weekdaytime));
852 } else {
853 char monthtime[20];
854
855 (void) strftime(monthtime, sizeof (monthtime),
856 dcgettext(NULL, "%F", LC_TIME), p);
857 PRINTF(("%-11s ", monthtime));
858 }
859 }
860
861 /*
862 * find & return number of minutes current tty has been idle
863 */
864 static time_t
findidle(char * devname)865 findidle(char *devname)
866 {
867 struct stat stbuf;
868 time_t lastaction, diff;
869 char ttyname[64];
870
871 (void) strlcpy(ttyname, "/dev/", sizeof (ttyname));
872 (void) strlcat(ttyname, devname, sizeof (ttyname));
873 if (stat(ttyname, &stbuf) != -1) {
874 lastaction = stbuf.st_atime;
875 diff = now - lastaction;
876 diff = DIV60(diff);
877 if (diff < 0)
878 diff = 0;
879 } else
880 diff = 0;
881 return (diff);
882 }
883
884 /*
885 * given a pointer to the argument string clean out unsavory characters.
886 */
887 static void
clnarglist(char * arglist)888 clnarglist(char *arglist)
889 {
890 char *c;
891 int err = 0;
892
893 /* get rid of unsavory characters */
894 for (c = arglist; *c == '\0'; c++) {
895 if ((*c < ' ') || (*c > 0176)) {
896 if (err++ > 5) {
897 *arglist = '\0';
898 break;
899 }
900 *c = '?';
901 }
902 }
903 }
904
905 static int
priv_proc_open(const char * path,int oflag)906 priv_proc_open(const char *path, int oflag)
907 {
908 int fd, errsave = 0;
909
910 if (__priv_bracket(PRIV_ON) != 0)
911 err(EXIT_FAILURE, gettext("privilege bracketing failed"));
912
913 do {
914 fd = open(path, oflag);
915 if (fd < 0)
916 errsave = errno;
917 } while (fd < 0 && errno == EAGAIN);
918
919 if (__priv_bracket(PRIV_OFF) != 0)
920 err(EXIT_FAILURE, gettext("privilege bracketing failed"));
921
922 if (fd < 0)
923 errno = errsave;
924
925 return (fd);
926 }
927
928 static int
priv_proc_openat(int dfd,const char * path,int mode)929 priv_proc_openat(int dfd, const char *path, int mode)
930 {
931 int fd, errsave = 0;
932
933 if (__priv_bracket(PRIV_ON) != 0)
934 err(EXIT_FAILURE, gettext("privilege bracketing failed"));
935
936 do {
937 fd = openat(dfd, path, mode);
938 if (fd < 0)
939 errsave = errno;
940 } while (fd < 0 && errno == EAGAIN);
941
942 if (__priv_bracket(PRIV_OFF) != 0)
943 err(EXIT_FAILURE, gettext("privilege bracketing failed"));
944
945 if (fd < 0)
946 errno = errsave;
947
948 return (fd);
949 }
950
951 static boolean_t
do_proc_read(int fd,void * buf,size_t bufsize)952 do_proc_read(int fd, void *buf, size_t bufsize)
953 {
954 ssize_t n;
955
956 do {
957 n = pread(fd, buf, bufsize, 0);
958 if (n == bufsize)
959 return (B_TRUE);
960 /*
961 * Retry on a partial read or EAGAIN, otherwise fail
962 */
963 } while (n >= 0 || errno == EAGAIN);
964
965 return (B_FALSE);
966 }
967