Reimplement the who(1) utility to add some features required by SUSv3:

-H option (show column headings),
-T (show mesg(1) state),
-m (same as "am I"),
-u (show idle time),
-q (quick mode; list names in columns).

PR:		36128
Reviewed by:	mike
This commit is contained in:
Tim J. Robbins 2002-05-09 23:04:40 +00:00
parent eed3fdf51c
commit 38a9994201
2 changed files with 269 additions and 121 deletions

View File

@ -32,31 +32,60 @@
.\" @(#)who.1 8.2 (Berkeley) 12/30/93
.\" $FreeBSD$
.\"
.Dd December 30, 1993
.Dd May 8, 2002
.Dt WHO 1
.Os
.Sh NAME
.Nm who
.Nd display who is logged in
.Nd display who is on the system
.Sh SYNOPSIS
.Nm
.Ar am I
.Nm
.Op Fl HmqsTu
.Op Cm am I
.Op Ar file
.Sh DESCRIPTION
The
.Nm
utility displays
a list of all users currently logged on, showing for each user
the login name,
tty name, the date and time of login, and hostname if not local.
utility displays information about currently logged in users.
By default, this includes the login name, tty name, date and time of login and
remote hostname if not local.
.Pp
Available options:
The options are as follows:
.Bl -tag -width indent
.It Fl H
Write column headings above the output.
.It Fl m
Show information about the terminal attached to standard input only.
.It Fl q
.Dq Quick mode Ns :
List the names and number of logged in users in columns.
All other command line options are ignored.
.It Fl s
Show the name, line and time fields only.
This is the default.
.It Fl T
Indicate whether each user is accepting messages.
One of the following characters is written:
.Bl -tag -width x -compact
.It +
User is accepting messages.
.It \&-
User is not accepting messages.
.It ?
An error occurred.
.El
.It Fl u
Show idle time for each user in hours and minutes as
.Ql hh:mm ,
.Ql \&.
if the user has been idle less that a minute and
.Ql old
if the user has been idle more than 24 hours.
.It Cm am I
Equivalent to
.Fl m .
.El
.Pp
.Bl -tag -width file
.It Ar \&am I
Returns the invoker's real user name.
.It Ar file
By default,
.Nm
gathers information from the file
@ -80,7 +109,6 @@ since
.Pa wtmp
was last truncated or
created.
.El
.Pp
If
.Pa /var/log/wtmp
@ -95,11 +123,18 @@ special characters, see
.It Pa /var/log/wtmp
.It Pa /var/log/wtmp.[0-6]
.El
.Sh DIAGNOSTICS
.Ex -std
.Sh SEE ALSO
.Xr last 1 ,
.Xr users 1 ,
.Xr getuid 2 ,
.Xr w 1 ,
.Xr utmp 5
.Sh STANDARDS
The
.Nm
utility conforms to
.St -p1003.1-2001 .
.Sh HISTORY
A
.Nm

View File

@ -1,9 +1,6 @@
/*
* 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
@ -13,18 +10,11 @@
* 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)
@ -35,25 +25,16 @@
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#ifndef lint
static const char copyright[] =
"@(#) Copyright (c) 1989, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#endif
#ifndef lint
static const char sccsid[] = "@(#)who.c 8.1 (Berkeley) 6/6/93";
#endif
#include <sys/types.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <err.h>
#include <langinfo.h>
#include <locale.h>
#include <paths.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
@ -62,107 +43,239 @@ static const char sccsid[] = "@(#)who.c 8.1 (Berkeley) 6/6/93";
#include <unistd.h>
#include <utmp.h>
static void usage(void);
static void output(struct utmp *);
static FILE *file(const char *);
static void heading(void);
static void process_utmp(FILE *);
static void quick(FILE *);
static void row(struct utmp *);
static int ttywidth(void);
static void usage(void);
static void whoami(FILE *);
static int Hflag; /* Write column headings */
static int mflag; /* Show info about current terminal */
static int qflag; /* "Quick" mode */
static int sflag; /* Show name, line, time */
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[])
{
char *p;
struct utmp usr;
struct passwd *pw;
FILE *ufp;
char *t;
int ch;
const char *file;
FILE *fp;
(void) setlocale(LC_TIME, "");
setlocale(LC_TIME, "");
switch (argc) {
case 1: /* who */
ufp = file(_PATH_UTMP);
/* 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 2: /* who utmp_file */
ufp = file(argv[1]);
/* all entries */
while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
output(&usr);
break;
case 3: /* who am i */
if (strcmp(argv[1], "am")
|| (strcmp(argv[2], "I") && strcmp(argv[2], "i")))
usage();
ufp = file(_PATH_UTMP);
/* search through the utmp and find an entry for this tty */
if ((p = ttyname(0))) {
/* strip any directory component */
if ((t = rindex(p, '/')))
p = t + 1;
while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
if (*usr.ut_name && !strcmp(usr.ut_line, p)) {
output(&usr);
exit(0);
}
/* 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);
break;
default:
usage();
while ((ch = getopt(argc, argv, "HTabdlmpqrstu")) != -1) {
switch (ch) {
case 'H': /* Write column headings */
Hflag = 1;
break;
case 'T': /* Show terminal state */
Tflag = 1;
break;
case 'm': /* Show info about current terminal */
mflag = 1;
break;
case 'q': /* "Quick" mode */
qflag = 1;
break;
case 's': /* Show name, line, time */
sflag = 1;
break;
case 'u': /* Show idle time */
uflag = 1;
break;
default:
usage();
/*NOTREACHED*/
}
}
argc -= optind;
argv += optind;
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 (*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
process_utmp(fp);
}
fclose(fp);
exit(0);
}
static void
usage()
void
usage(void)
{
(void)fprintf(stderr, "%s\n%s\n",
"usage: who [file]",
" who am i");
fprintf(stderr, "usage: who [-HmqsTu] [am I] [file]\n");
exit(1);
}
void
output(up)
struct utmp *up;
heading(void)
{
char buf[80];
printf("%-*s ", UT_NAMESIZE, "NAME");
if (Tflag)
printf("S ");
printf("%-*s ", UT_LINESIZE, "LINE");
printf("%-*s ", 12, "TIME");
if (uflag)
printf("IDLE ");
putchar('\n');
}
void
row(struct utmp *ut)
{
char buf[80], tty[sizeof(_PATH_DEV) + UT_LINESIZE];
struct stat sb;
time_t idle;
static int d_first = -1;
struct tm *tm;
char state;
if (d_first < 0)
d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
(void)printf("%-*.*s %-*.*s", UT_NAMESIZE, UT_NAMESIZE, up->ut_name,
UT_LINESIZE, UT_LINESIZE, up->ut_line);
(void)strftime(buf, sizeof(buf),
d_first ? "%e %b %R" : "%b %e %R",
localtime(&up->ut_time));
(void)printf("%s", buf);
if (*up->ut_host)
printf("\t(%.*s)", UT_HOSTSIZE, up->ut_host);
(void)putchar('\n');
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;
}
}
printf("%-*.*s ", UT_NAMESIZE, UT_NAMESIZE, ut->ut_name);
if (Tflag)
printf("%c ", state);
printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, ut->ut_line);
tm = localtime(&ut->ut_time);
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
printf(" old ");
}
if (*ut->ut_host != '\0')
printf("(%.*s)", UT_HOSTSIZE, ut->ut_host);
putchar('\n');
}
static FILE *
file(name)
const char *name;
void
process_utmp(FILE *fp)
{
FILE *ufp;
struct utmp ut;
if (!(ufp = fopen(name, "r")))
err(1, "%s", name);
return(ufp);
while (fread(&ut, sizeof(ut), 1, fp) == 1)
if (*ut.ut_name != '\0')
row(&ut);
}
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');
printf("# users = %d\n", num);
}
void
whoami(FILE *fp)
{
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;
}
/* 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);
time(&ut.ut_time);
row(&ut);
}
int
ttywidth(void)
{
struct winsize ws;
int width;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
width = ws.ws_col;
else
width = 80;
return (width);
}