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 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 if (errno == ENOENT) 376 continue; 377 warn(gettext("failed to open %s"), pname); 378 continue; 379 } 380 381 382 procfd = priv_proc_openat(dirfd, "psinfo", O_RDONLY); 383 if (procfd < 0) { 384 (void) close(dirfd); 385 continue; 386 } 387 388 if (!do_proc_read(procfd, &info, sizeof (info))) { 389 warn(gettext("read() failed on %s"), pname); 390 (void) close(dirfd); 391 continue; 392 } 393 (void) close(procfd); 394 395 up = findhash(info.pr_pid); 396 up->p_ttyd = info.pr_ttydev; 397 up->p_state = (info.pr_nlwp == 0? ZOMBIE : RUNNING); 398 up->p_time = 0; 399 up->p_ctime = 0; 400 up->p_igintr = 0; 401 (void) strlcpy(up->p_comm, info.pr_fname, 402 sizeof (up->p_comm)); 403 up->p_args[0] = 0; 404 405 if (up->p_state != NONE && up->p_state != ZOMBIE) { 406 procfd = priv_proc_openat(dirfd, "status", O_RDONLY); 407 if (procfd < 0) { 408 (void) close(dirfd); 409 continue; 410 } 411 412 if (!do_proc_read(procfd, &statinfo, 413 sizeof (statinfo))) { 414 warn(gettext("read() failed on %s/status"), 415 pname); 416 417 (void) close(procfd); 418 (void) close(dirfd); 419 continue; 420 } 421 (void) close(procfd); 422 423 up->p_time = statinfo.pr_utime.tv_sec + 424 statinfo.pr_stime.tv_sec; 425 up->p_ctime = statinfo.pr_cutime.tv_sec + 426 statinfo.pr_cstime.tv_sec; 427 428 procfd = priv_proc_openat(dirfd, "sigact", O_RDONLY); 429 if (procfd < 0) { 430 (void) close(dirfd); 431 continue; 432 } 433 434 if (!do_proc_read(procfd, actinfo, sizeof (actinfo))) { 435 warn(gettext("read() failed on %s/sigact"), 436 pname); 437 438 (void) close(procfd); 439 (void) close(dirfd); 440 continue; 441 } 442 (void) close(procfd); 443 (void) close(dirfd); 444 445 up->p_igintr = 446 actinfo[SIGINT-1].sa_handler == SIG_IGN && 447 actinfo[SIGQUIT-1].sa_handler == SIG_IGN; 448 449 up->p_args[0] = 0; 450 451 /* 452 * Process args if there's a chance we'll print it. 453 */ 454 if (lflag) { /* w command needs args */ 455 clnarglist(info.pr_psargs); 456 (void) strlcpy(up->p_args, info.pr_psargs, 457 sizeof (up->p_args)); 458 if (up->p_args[0] == 0 || 459 up->p_args[0] == '-' && 460 up->p_args[1] <= ' ' || 461 up->p_args[0] == '?') { 462 (void) strlcat(up->p_args, " (", 463 sizeof (up->p_args)); 464 (void) strlcat(up->p_args, up->p_comm, 465 sizeof (up->p_args)); 466 (void) strlcat(up->p_args, ")", 467 sizeof (up->p_args)); 468 } 469 } 470 471 } 472 473 /* 474 * link pgrp together in case parents go away 475 * Pgrp chain is a single linked list originating 476 * from the pgrp leader to its group member. 477 */ 478 if (info.pr_pgid != info.pr_pid) { /* not pgrp leader */ 479 pgrp = findhash(info.pr_pgid); 480 up->p_pgrplink = pgrp->p_pgrplink; 481 pgrp->p_pgrplink = up; 482 } 483 parent = findhash(info.pr_ppid); 484 485 /* if this is the new member, link it in */ 486 if (parent->p_upid != INITPROCESS) { 487 if (parent->p_child) { 488 up->p_sibling = parent->p_child; 489 up->p_child = 0; 490 } 491 parent->p_child = up; 492 } 493 494 } 495 496 /* revert to non-privileged user */ 497 __priv_relinquish(); 498 if (getuid() == 0) { 499 /* 500 * Since the privilege bracketing functions are effectively 501 * no-ops when running as root, we must explicitly 502 * relinquish PRIV_PROC_OWNER ourselves. 503 */ 504 pset = priv_allocset(); 505 if (pset == NULL) { 506 err(EXIT_FAILURE, 507 gettext("failed to allocate privilege set")); 508 } 509 510 priv_emptyset(pset); 511 512 if (priv_addset(pset, PRIV_PROC_OWNER) != 0) { 513 err(EXIT_FAILURE, gettext("failed to add " 514 "PRIV_PROC_OWNER to privilege set")); 515 } 516 517 if (setppriv(PRIV_OFF, PRIV_PERMITTED, pset) != 0) { 518 err(EXIT_FAILURE, 519 gettext("failed to set permitted privilege set")); 520 } 521 522 priv_freeset(pset); 523 pset = NULL; 524 } 525 526 (void) closedir(dirp); 527 (void) time(&now); /* get current time */ 528 529 /* 530 * loop through utmpx file, printing process info 531 * about each logged in user 532 */ 533 for (ut = utmpbegin; ut < utmpend; ut++) { 534 time_t tim; 535 536 if (ut->ut_type != USER_PROCESS) 537 continue; 538 if (sel_user && strncmp(ut->ut_name, sel_user, NMAX) != 0) 539 continue; /* we're looking for somebody else */ 540 if (lflag) { /* -l flag format (w command) */ 541 /* print login name of the user */ 542 (void) printf("%-*.*s ", LOGIN_WIDTH, (int)NMAX, 543 ut->ut_name); 544 545 /* print tty user is on */ 546 (void) printf("%-*.*s ", LINE_WIDTH, (int)LMAX, 547 ut->ut_line); 548 549 /* print when the user logged in */ 550 tim = ut->ut_xtime; 551 (void) prtat(&tim); 552 553 /* print idle time */ 554 idle = findidle(ut->ut_line); 555 prttime(idle, 8); 556 showtotals(findhash((pid_t)ut->ut_pid)); 557 } else { /* standard whodo format */ 558 tim = ut->ut_xtime; 559 tm = localtime(&tim); 560 (void) printf("\n%-*.*s %-*.*s %2.1d:%2.2d\n", 561 LINE_WIDTH, (int)LMAX, ut->ut_line, 562 LOGIN_WIDTH, (int)NMAX, ut->ut_name, tm->tm_hour, 563 tm->tm_min); 564 showproc(findhash((pid_t)ut->ut_pid)); 565 } 566 } 567 568 return (0); 569 } 570 571 /* 572 * Used for standard whodo format. 573 * This is the recursive routine descending the process 574 * tree starting from the given process pointer(up). 575 * It used depth-first search strategy and also marked 576 * each node as printed as it traversed down the tree. 577 */ 578 static void 579 showproc(struct uproc *up) 580 { 581 struct uproc *zp; 582 583 if (up->p_state == VISITED) /* we already been here */ 584 return; 585 /* print the data for this process */ 586 if (up->p_state == ZOMBIE) 587 (void) printf(" %-*.*s %5d %4.1ld:%2.2ld %s\n", 588 LINE_WIDTH, (int)LMAX, " ?", (int)up->p_upid, 0L, 0L, 589 "<defunct>"); 590 else if (up->p_state != NONE) { 591 (void) printf(" %-*.*s %5d %4.1ld:%2.2ld %s\n", 592 LINE_WIDTH, (int)LMAX, getty(up->p_ttyd), (int)up->p_upid, 593 up->p_time / 60L, up->p_time % 60L, 594 up->p_comm); 595 } 596 up->p_state = VISITED; 597 598 /* descend for its children */ 599 if (up->p_child) { 600 showproc(up->p_child); 601 for (zp = up->p_child->p_sibling; zp; zp = zp->p_sibling) { 602 showproc(zp); 603 } 604 } 605 606 /* print the pgrp relation */ 607 if (up->p_pgrplink) 608 showproc(up->p_pgrplink); 609 } 610 611 612 /* 613 * Used for -l flag (w command) format. 614 * Prints the CPU time for all processes & children, 615 * and the cpu time for interesting process, 616 * and what the user is doing. 617 */ 618 static void 619 showtotals(struct uproc *up) 620 { 621 jobtime = 0; 622 proctime = 0; 623 empty = 1; 624 curpid = -1; 625 626 /* default act: normally never prints */ 627 (void) strlcpy(doing, "-", sizeof (doing)); 628 calctotals(up); 629 630 /* print CPU time for all processes & children */ 631 /* and need to convert clock ticks to seconds first */ 632 prttime((time_t)jobtime, 8); 633 634 /* print cpu time for interesting process */ 635 /* and need to convert clock ticks to seconds first */ 636 prttime((time_t)proctime, 8); 637 638 /* what user is doing, current process */ 639 (void) printf("%-.32s\n", doing); 640 } 641 642 /* 643 * Used for -l flag (w command) format. 644 * This recursive routine descends the process 645 * tree starting from the given process pointer(up). 646 * It used depth-first search strategy and also marked 647 * each node as visited as it traversed down the tree. 648 * It calculates the process time for all processes & 649 * children. It also finds the "interesting" process 650 * and determines its cpu time and command. 651 */ 652 static void 653 calctotals(struct uproc *up) 654 { 655 struct uproc *zp; 656 657 if (up->p_state == VISITED) 658 return; 659 up->p_state = VISITED; 660 if (up->p_state == NONE || up->p_state == ZOMBIE) 661 return; 662 jobtime += up->p_time + up->p_ctime; 663 proctime += up->p_time; 664 665 if (empty && !up->p_igintr) { 666 empty = 0; 667 curpid = -1; 668 } 669 670 if (up->p_upid > curpid && (!up->p_igintr || empty)) { 671 curpid = up->p_upid; 672 (void) strlcpy(doing, up->p_args, sizeof (doing)); 673 } 674 675 /* descend for its children */ 676 if (up->p_child) { 677 calctotals(up->p_child); 678 for (zp = up->p_child->p_sibling; zp; zp = zp->p_sibling) 679 calctotals(zp); 680 } 681 } 682 683 static char * 684 devadd(const char *name, dev_t ddev) 685 { 686 struct devl *dp; 687 int leng, start, i; 688 689 if (ndevs == maxdev) { 690 uint_t newdev; 691 692 newdev = maxdev + DNINCR; 693 if (newdev < DNINCR) 694 errx(EXIT_FAILURE, gettext("devadd overflow")); 695 696 dp = recallocarray(devl, maxdev, newdev, sizeof (struct devl)); 697 if (dp == NULL) 698 err(EXIT_FAILURE, gettext("out of memory!")); 699 maxdev = newdev; 700 devl = dp; 701 } 702 dp = &devl[ndevs++]; 703 704 dp->ddev = ddev; 705 if (name == NULL) { 706 (void) strlcpy(dp->dname, " ? ", sizeof (dp->dname)); 707 return (dp->dname); 708 } 709 710 leng = strlen(name); 711 if (leng < DEVNAMELEN + 4) { 712 /* strip off "/dev/" */ 713 (void) strlcpy(dp->dname, &name[5], sizeof (dp->dname)); 714 } else { 715 /* strip enough off the front to fit */ 716 start = leng - DEVNAMELEN - 1; 717 718 for (i = start; i < leng && name[i] != '/'; i++) 719 ; 720 if (i == leng) 721 (void) strlcpy(dp->dname, &name[start], DEVNAMELEN); 722 else 723 (void) strlcpy(dp->dname, &name[i+1], DEVNAMELEN); 724 } 725 return (dp->dname); 726 } 727 728 static char * 729 devlookup(dev_t ddev) 730 { 731 struct devl *dp; 732 int i; 733 734 for (dp = devl, i = 0; i < ndevs; dp++, i++) { 735 if (dp->ddev == ddev) 736 return (dp->dname); 737 } 738 return (NULL); 739 } 740 741 /* 742 * This routine gives back a corresponding device name 743 * from the device number given. 744 */ 745 static char * 746 getty(dev_t dev) 747 { 748 extern char *_ttyname_dev(dev_t, char *, size_t); 749 char devname[TTYNAME_MAX]; 750 char *retval; 751 752 if (dev == PRNODEV) 753 return (" ? "); 754 755 if ((retval = devlookup(dev)) != NULL) 756 return (retval); 757 758 retval = _ttyname_dev(dev, devname, sizeof (devname)); 759 return (devadd(retval, dev)); 760 } 761 762 /* 763 * Findhash finds the appropriate entry in the process 764 * hash table (pr_htbl) for the given pid in case that 765 * pid exists on the hash chain. It returns back a pointer 766 * to that uproc structure. If this is a new pid, it allocates 767 * a new node, initializes it, links it into the chain (after 768 * head) and returns a structure pointer. 769 */ 770 static struct uproc * 771 findhash(pid_t pid) 772 { 773 struct uproc *up, *tp; 774 775 tp = up = &pr_htbl[(int)pid % HSIZE]; 776 if (up->p_upid == 0) { /* empty slot */ 777 up->p_upid = pid; 778 up->p_state = NONE; 779 up->p_child = up->p_sibling = up->p_pgrplink = up->p_link = 0; 780 return (up); 781 } 782 if (up->p_upid == pid) { /* found in hash table */ 783 return (up); 784 } 785 for (tp = up->p_link; tp; tp = tp->p_link) { /* follow chain */ 786 if (tp->p_upid == pid) { 787 return (tp); 788 } 789 } 790 tp = malloc(sizeof (*tp)); /* add new node */ 791 if (tp == NULL) 792 err(EXIT_FAILURE, gettext("out of memory!")); 793 794 (void) memset((char *)tp, 0, sizeof (*tp)); 795 tp->p_upid = pid; 796 tp->p_state = NONE; 797 tp->p_child = tp->p_sibling = tp->p_pgrplink = (pid_t)0; 798 tp->p_link = up->p_link; /* insert after head */ 799 up->p_link = tp; 800 return (tp); 801 } 802 803 #define HR (60 * 60) 804 #define DAY (24 * HR) 805 #define MON (30 * DAY) 806 #define PRINTF(a) (void) printf a 807 808 /* 809 * Prttime prints an elapsed time in hours, minutes, or seconds, 810 * right-justified with the rightmost column always blank. 811 * The second argument is the minimum field width. 812 */ 813 static void 814 prttime(time_t tim, int width) 815 { 816 char value[36]; 817 818 if (tim >= 36 * 60) { 819 (void) snprintf(value, sizeof (value), "%d:%02d:%02d", 820 (int)tim / HR, (int)(tim % HR) / 60, (int)tim % 60); 821 } else if (tim >= 60) { 822 (void) snprintf(value, sizeof (value), "%d:%02d", 823 (int)tim / 60, (int)tim % 60); 824 } else if (tim > 0) { 825 (void) snprintf(value, sizeof (value), "%d", (int)tim); 826 } else { 827 (void) strcpy(value, "0"); 828 } 829 width = (width > 2) ? width - 1 : 1; 830 PRINTF(("%*s ", width, value)); 831 } 832 833 /* 834 * Prints the ISO date or time given a pointer to a time of day, 835 * left-justfied in a 12-character expanding field with the 836 * rightmost column always blank. 837 * Includes a dcgettext() override in case a message catalog is needed. 838 */ 839 static void 840 prtat(time_t *time) 841 { 842 struct tm *p; 843 844 p = localtime(time); 845 if (now - *time <= 18 * HR) { 846 char timestr[50]; 847 848 (void) strftime(timestr, sizeof (timestr), 849 dcgettext(NULL, "%T", LC_TIME), p); 850 PRINTF(("%-11s ", timestr)); 851 } else if (now - *time <= 7 * DAY) { 852 char weekdaytime[20]; 853 854 (void) strftime(weekdaytime, sizeof (weekdaytime), 855 dcgettext(NULL, "%a %H:%M", LC_TIME), p); 856 PRINTF(("%-11s ", weekdaytime)); 857 } else { 858 char monthtime[20]; 859 860 (void) strftime(monthtime, sizeof (monthtime), 861 dcgettext(NULL, "%F", LC_TIME), p); 862 PRINTF(("%-11s ", monthtime)); 863 } 864 } 865 866 /* 867 * find & return number of minutes current tty has been idle 868 */ 869 static time_t 870 findidle(char *devname) 871 { 872 struct stat stbuf; 873 time_t lastaction, diff; 874 char ttyname[64]; 875 876 (void) strlcpy(ttyname, "/dev/", sizeof (ttyname)); 877 (void) strlcat(ttyname, devname, sizeof (ttyname)); 878 if (stat(ttyname, &stbuf) != -1) { 879 lastaction = stbuf.st_atime; 880 diff = now - lastaction; 881 diff = DIV60(diff); 882 if (diff < 0) 883 diff = 0; 884 } else 885 diff = 0; 886 return (diff); 887 } 888 889 /* 890 * given a pointer to the argument string clean out unsavory characters. 891 */ 892 static void 893 clnarglist(char *arglist) 894 { 895 char *c; 896 int err = 0; 897 898 /* get rid of unsavory characters */ 899 for (c = arglist; *c == '\0'; c++) { 900 if ((*c < ' ') || (*c > 0176)) { 901 if (err++ > 5) { 902 *arglist = '\0'; 903 break; 904 } 905 *c = '?'; 906 } 907 } 908 } 909 910 static int 911 priv_proc_open(const char *path, int oflag) 912 { 913 int fd, errsave = 0; 914 915 if (__priv_bracket(PRIV_ON) != 0) 916 err(EXIT_FAILURE, gettext("privilege bracketing failed")); 917 918 do { 919 fd = open(path, oflag); 920 if (fd < 0) 921 errsave = errno; 922 } while (fd < 0 && errno == EAGAIN); 923 924 if (__priv_bracket(PRIV_OFF) != 0) 925 err(EXIT_FAILURE, gettext("privilege bracketing failed")); 926 927 if (fd < 0) 928 errno = errsave; 929 930 return (fd); 931 } 932 933 static int 934 priv_proc_openat(int dfd, const char *path, int mode) 935 { 936 int fd, errsave = 0; 937 938 if (__priv_bracket(PRIV_ON) != 0) 939 err(EXIT_FAILURE, gettext("privilege bracketing failed")); 940 941 do { 942 fd = openat(dfd, path, mode); 943 if (fd < 0) 944 errsave = errno; 945 } while (fd < 0 && errno == EAGAIN); 946 947 if (__priv_bracket(PRIV_OFF) != 0) 948 err(EXIT_FAILURE, gettext("privilege bracketing failed")); 949 950 if (fd < 0) 951 errno = errsave; 952 953 return (fd); 954 } 955 956 static boolean_t 957 do_proc_read(int fd, void *buf, size_t bufsize) 958 { 959 ssize_t n; 960 961 do { 962 n = pread(fd, buf, bufsize, 0); 963 if (n == bufsize) 964 return (B_TRUE); 965 /* 966 * Retry on a partial read or EAGAIN, otherwise fail 967 */ 968 } while (n >= 0 || errno == EAGAIN); 969 970 return (B_FALSE); 971 } 972