-/* $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);
}