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