]> git.saurik.com Git - apple/shell_cmds.git/blobdiff - who/who.c
shell_cmds-74.1.tar.gz
[apple/shell_cmds.git] / who / who.c
index f045888f13a6ba27a786536e21b74f3678fafee0..8b2633379abef973d41b44bb7e4c6e09c95c279d 100644 (file)
--- a/who/who.c
+++ b/who/who.c
@@ -1,11 +1,6 @@
-/*     $NetBSD: who.c,v 1.6 1997/10/20 03:20:29 lukem Exp $    */
-
-/*
- * Copyright (c) 1989, 1993
- *     The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Michael Fischbein.
+/*-
+ * Copyright (c) 2002 Tim J. Robbins.
+ * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  */
 
 #include <sys/cdefs.h>
-#ifndef lint
-__COPYRIGHT(
-"@(#) Copyright (c) 1989, 1993\n\
-       The Regents of the University of California.  All rights reserved.\n");
-#endif /* not lint */
-
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)who.c      8.1 (Berkeley) 6/6/93";
-#endif
-__RCSID("$NetBSD: who.c,v 1.6 1997/10/20 03:20:29 lukem Exp $");
-#endif /* not lint */
+/* commented as FBSDID not needed for Tiger ......
+__FBSDID("$FreeBSD: src/usr.bin/who/who.c,v 1.20 2003/10/26 05:05:48 peter Exp $");
+*/
 
 #include <sys/types.h>
+#include <sys/ioctl.h>
 #include <sys/stat.h>
+
 #include <err.h>
+#include <errno.h>
+#include <langinfo.h>
+#include <limits.h>
 #include <locale.h>
+#include <paths.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
+#include <timeconv.h>
 #include <unistd.h>
 #include <utmp.h>
 
-void output __P((struct utmp *));
-void output_labels __P((void));
-void who_am_i __P((FILE *));
-FILE *file __P((char *));
-void usage __P((void));
-
-int show_term;                 /* show term state */
-int show_idle;                 /* show idle time */
-
-int main __P((int, char **));
+static void    heading(void);
+static void    process_utmp(FILE *);
+static void    process_wtmp(FILE *);
+static void    quick(FILE *);
+static void    row(struct utmp *);
+static int     ttywidth(void);
+static void    usage(void);
+static void    whoami(FILE *);
+
+static int     bflag;                  /* date & time of last reboot */
+static int     dflag;                  /* dead processes */
+static int     Hflag;                  /* Write column headings */
+static int     lflag;                  /* waiting to login */
+static int     mflag;                  /* Show info about current terminal */
+static int     pflag;                  /* Processes active & spawned by init */
+static int     qflag;                  /* "Quick" mode */
+static int     rflag;                  /* run-level of the init process */
+static int     sflag;                  /* Show name, line, time */
+static int     tflag;                  /* time of change to system clock */
+static int     Tflag;                  /* Show terminal state */
+static int     uflag;                  /* Show idle time */
 
 int
-main(argc, argv)
-       int argc;
-       char **argv;
+main(int argc, char *argv[])
 {
-       struct utmp usr;
-       FILE *ufp;
-       int c, only_current_term, show_labels;
+       int ch;
+       const char *file;
+       FILE *fp;
+       FILE *wtmp_fp;
+
+       setlocale(LC_TIME, "");
 
-       setlocale(LC_ALL, "");
+       while ((ch = getopt(argc, argv, "abdHlmpqrstTu")) != -1) {
+               switch (ch) {
 
-       only_current_term = show_term = show_idle = show_labels = 0;
-       while ((c = getopt(argc, argv, "mTuH")) != -1) {
-               switch (c) {
-               case 'm':
-                       only_current_term = 1;
+               case 'a':               /* -b, -d, -l, -p, -r, -t, -T and -u */
+                       bflag = dflag = lflag = pflag = 1;
+                       rflag = tflag = Tflag = uflag = 1;
+                       break;
+               case 'b':               /* date & time of last reboot */
+                       bflag = 1;
+                       break;
+               case 'd':               /* dead processes */
+                       dflag = 1;
+                       break;
+               case 'H':               /* Write column headings */
+                       Hflag = 1;
+                       break;
+               case 'l':               /* waiting to login */
+                       lflag = 1;
+                       break;
+               case 'm':               /* Show info about current terminal */
+                       mflag = 1;
+                       break;
+               case 'p':               /* Processes active & spawned by init */
+                       pflag = 1;
+                       break;
+               case 'q':               /* "Quick" mode */
+                       qflag = 1;
                        break;
-               case 'T':
-                       show_term = 1;
+               case 'r':               /* run-level of the init process */
+                       rflag = 1;
                        break;
-               case 'u':
-                       show_idle = 1;
+               case 's':               /* Show name, line, time */
+                       sflag = 1;
                        break;
-               case 'H':
-                       show_labels = 1;
+               case 't':               /* time of change to system clock */
+                       tflag = 1;
+                       break;
+               case 'T':               /* Show terminal state */
+                       Tflag = 1;
+                       break;
+               case 'u':               /* Show idle time */
+                       uflag = 1;
                        break;
                default:
                        usage();
-                       /* NOTREACHED */
+                       /*NOTREACHED*/
                }
        }
        argc -= optind;
        argv += optind;
 
-       if (chdir("/dev")) {
-               err(1, "cannot change directory to /dev");
-               /* NOTREACHED */
+       if (argc >= 2 && strcmp(argv[0], "am") == 0 &&
+           (strcmp(argv[1], "i") == 0 || strcmp(argv[1], "I") == 0)) {
+               /* "who am i" or "who am I", equivalent to -m */
+               mflag = 1;
+               argc -= 2;
+               argv += 2;
        }
+       if (argc > 1)
+               usage();
 
-       if (show_labels)
-               output_labels();
+       if (*argv != NULL)
+               file = *argv;
+       else
+               file = _PATH_UTMP;
+       if ((fp = fopen(file, "r")) == NULL)
+               err(1, "%s", file);
+
+       if (qflag)
+               quick(fp);
+       else {
+               if (sflag)
+                       Tflag = uflag = 0;
+               if (Hflag)
+                       heading();
+               if (mflag)
+                       whoami(fp);
+               else
+                       /* read and process utmp file for relevant options */
+                       if( Tflag || uflag || !(bflag || dflag || lflag || pflag || rflag) )
+                               process_utmp(fp);
+       }
 
-       switch (argc) {
-       case 0:                                 /* who */
-               ufp = file(_PATH_UTMP);
+       fclose(fp);
 
-               if (only_current_term) {
-                       who_am_i(ufp);
-               } else {
-                       /* only entries with both name and line fields */
-                       while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
-                               if (*usr.ut_name && *usr.ut_line)
-                                       output(&usr);
-               }
-               break;
-       case 1:                                 /* who utmp_file */
-               ufp = file(*argv);
-
-               if (only_current_term) {
-                       who_am_i(ufp);
-               } else {
-                       /* all entries */
-                       while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
-                               output(&usr);
+       /* read and process wtmp file for relevant options */
+       if (bflag || dflag || lflag || pflag || rflag ) {
+
+               /* Open the wtmp file */
+               if ((wtmp_fp = fopen(_PATH_WTMP, "r")) == NULL)
+                       err(1, "%s", _PATH_WTMP);
+               else {
+                       process_wtmp(wtmp_fp);
+                       fclose(wtmp_fp);
                }
-               break;
-       case 2:                                 /* who am i */
-               ufp = file(_PATH_UTMP);
-               who_am_i(ufp);
-               break;
-       default:
-               usage();
-               /* NOTREACHED */
        }
+
        exit(0);
 }
 
-void
-who_am_i(ufp)
-       FILE *ufp;
+static void
+usage(void)
 {
-       struct utmp usr;
-       struct passwd *pw;
-       char *p;
-       char *t;
-
-       /* search through the utmp and find an entry for this tty */
-       if ((p = ttyname(0)) != NULL) {
-               /* strip any directory component */
-               if ((t = strrchr(p, '/')) != NULL)
-                       p = t + 1;
-               while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
-                       if (usr.ut_name && !strcmp(usr.ut_line, p)) {
-                               output(&usr);
-                               return;
-                       }
-               /* well, at least we know what the tty is */
-               (void)strncpy(usr.ut_line, p, UT_LINESIZE);
-       } else
-               (void)strcpy(usr.ut_line, "tty??");
-
-       pw = getpwuid(getuid());
-       (void)strncpy(usr.ut_name, pw ? pw->pw_name : "?", UT_NAMESIZE);
-       (void)time(&usr.ut_time);
-       *usr.ut_host = '\0';
-       output(&usr);
+
+       fprintf(stderr, "usage: who [-abdHlmpqrstTu] [am I] [file]\n");
+       exit(1);
 }
 
-void
-output(up)
-       struct utmp *up;
+static void
+heading(void)
 {
+
+       printf("%-*s ", UT_NAMESIZE, "NAME");
+       if (Tflag)
+               printf("S ");
+       printf("%-*s ", UT_LINESIZE, "LINE");
+       printf("%-*s ", 12, "TIME");
+       if (uflag)
+               printf("IDLE  ");
+       printf("%-*s", UT_HOSTSIZE, "FROM");
+       putchar('\n');
+}
+
+static void
+row(struct utmp *ut)
+{
+       char buf[80], tty[sizeof(_PATH_DEV) + UT_LINESIZE];
        struct stat sb;
-       char line[sizeof (up->ut_line) + 1];
+       time_t idle, t;
+       static int d_first = -1;
+       struct tm *tm;
        char state;
-       static time_t now = 0;
-       time_t idle;
-
-       state = '?';
-       idle = 0;
-
-       if (show_term || show_idle) {
-               if (now == 0)
-                       time(&now);
-               
-               strncpy(line, up->ut_line, sizeof (up->ut_line));
-               line[sizeof (up->ut_line)] = '\0';
-
-               if (stat(line, &sb) == 0) {
-                       state = (sb.st_mode & 020) ? '+' : '-';
-                       idle = now - sb.st_atime;
-               }
-               
-       }
-
-       (void)printf("%-*.*s ", UT_NAMESIZE, UT_NAMESIZE, up->ut_name);
 
-       if (show_term) {
-               (void)printf("%c ", state);
+       if (d_first < 0)
+               d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
+
+       if (Tflag || uflag) {
+               snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV,
+                       UT_LINESIZE, ut->ut_line);
+               state = '?';
+               idle = 0;
+               if (stat(tty, &sb) == 0) {
+                       state = sb.st_mode & (S_IWOTH|S_IWGRP) ?
+                           '+' : '-';
+                       idle = time(NULL) - sb.st_mtime;
+               }
        }
 
-       (void)printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, up->ut_line);
-       (void)printf("%.12s ", ctime(&up->ut_time) + 4);
-
-       if (show_idle) {
-               if (idle < 60) 
-                       (void)printf("  .   ");
-               else if (idle < (24 * 60 * 60))
-                       (void)printf("%02ld:%02ld ", 
-                                    (long)(idle / (60 * 60)),
-                                    (long)(idle % (60 * 60)) / 60);
+       printf("%-*.*s ", UT_NAMESIZE, UT_NAMESIZE, ut->ut_name);
+       if (Tflag)
+               printf("%c ", state);
+       printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, ut->ut_line);
+       t = _time32_to_time(ut->ut_time);
+       tm = localtime(&t);
+       strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm);
+       printf("%-*s ", 12, buf);
+       if (uflag) {
+               if (idle < 60)
+                       printf("  .   ");
+               else if (idle < 24 * 60 * 60)
+                       printf("%02d:%02d ", (int)(idle / 60 / 60),
+                           (int)(idle / 60 % 60));
                else
-                       (void)printf(" old  ");
+                       printf(" old  ");
        }
-       
-       if (*up->ut_host)
-               printf("\t(%.*s)", UT_HOSTSIZE, up->ut_host);
-       (void)putchar('\n');
+       if (*ut->ut_host != '\0')
+               printf("(%.*s)", UT_HOSTSIZE, ut->ut_host);
+       putchar('\n');
+
 }
 
-void
-output_labels()
+static void
+process_utmp(FILE *fp)
 {
-       (void)printf("%-*.*s ", UT_NAMESIZE, UT_NAMESIZE, "USER");
+       struct utmp ut;
 
-       if (show_term)
-               (void)printf("S ");
-       
-       (void)printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, "LINE");
-       (void)printf("WHEN         ");
+       while (fread(&ut, sizeof(ut), 1, fp) == 1)
+               if (*ut.ut_name != '\0') {
+                       row(&ut);
+               }
+}
 
-       if (show_idle)
-               (void)printf("IDLE  ");
-       
-       (void)printf("\t%.*s", UT_HOSTSIZE, "FROM");
+/* For some options, process the wtmp file to generate output */
+static void
+process_wtmp(FILE *fp)
+{
+       struct utmp ut;
+       struct utmp lboot_ut = { "", "", "", 0 };
+       int num = 0;    /* count of user entries */
+
+       while (fread(&ut, sizeof(ut), 1, fp) == 1)
+               if (*ut.ut_name != '\0') {
+                       if (bflag && (!strcmp(ut.ut_name, "reboot"))) {
+                               memcpy(&lboot_ut, &ut, sizeof(ut));
+                       }
+                       else
+                               num++;
+               };
+
+       if (bflag && (!strcmp(lboot_ut.ut_name, "reboot")))
+               row(&lboot_ut);
+
+       /* run level of the init process is unknown in BSD system. If multi
+          user, then display the highest run level. Else, no-op.
+       */
+       if (rflag && (num > 1))
+               printf("   .       run-level 3\n");
+}
+
+static void
+quick(FILE *fp)
+{
+       struct utmp ut;
+       int col, ncols, num;
+
+       ncols = ttywidth();
+       col = num = 0;
+       while (fread(&ut, sizeof(ut), 1, fp) == 1) {
+               if (*ut.ut_name == '\0')
+                       continue;
+               printf("%-*.*s", UT_NAMESIZE, UT_NAMESIZE, ut.ut_name);
+               if (++col < ncols / (UT_NAMESIZE + 1))
+                       putchar(' ');
+               else {
+                       col = 0;
+                       putchar('\n');
+               }
+               num++;
+       }
+       if (col != 0)
+               putchar('\n');
 
-       (void)putchar('\n');
+       printf("# users = %d\n", num);
 }
 
-FILE *
-file(name)
-       char *name;
+static void
+whoami(FILE *fp)
 {
-       FILE *ufp;
+       struct utmp ut;
+       struct passwd *pwd;
+       const char *name, *p, *tty;
+
+       if ((tty = ttyname(STDIN_FILENO)) == NULL)
+               tty = "tty??";
+       else if ((p = strrchr(tty, '/')) != NULL)
+               tty = p + 1;
+
+       /* Search utmp for our tty, dump first matching record. */
+       while (fread(&ut, sizeof(ut), 1, fp) == 1)
+               if (*ut.ut_name != '\0' && strncmp(ut.ut_line, tty,
+                   UT_LINESIZE) == 0) {
+                       row(&ut);
+                       return;
+               }
 
-       if (!(ufp = fopen(name, "r"))) {
-               err(1, "%s", name);
-               /* NOTREACHED */
-       }
-       return (ufp);
+       /* Not found; fill the utmp structure with the information we have. */
+       memset(&ut, 0, sizeof(ut));
+       if ((pwd = getpwuid(getuid())) != NULL)
+               name = pwd->pw_name;
+       else
+               name = "?";
+       strncpy(ut.ut_name, name, UT_NAMESIZE);
+       strncpy(ut.ut_line, tty, UT_LINESIZE);
+       ut.ut_time = _time_to_time32(time(NULL));
+       row(&ut);
 }
 
-void
-usage()
+static int
+ttywidth(void)
 {
-       (void)fprintf(stderr, "usage: who [-mTuH] [ file ]\n       who am i\n");
-       exit(1);
+       struct winsize ws;
+       long width;
+       char *cols, *ep;
+
+       if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') {
+               errno = 0;
+               width = strtol(cols, &ep, 10);
+               if (errno || width <= 0 || width > INT_MAX || ep == cols ||
+                   *ep != '\0')
+                       warnx("invalid COLUMNS environment variable ignored");
+               else
+                       return (width);
+       }
+       if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
+               return (ws.ws_col);
+
+       return (80);
 }