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 }