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