w: new usage & fix coding style

Coding style fixed and more readable help output.

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
This commit is contained in:
Sami Kerola 2011-06-05 14:34:42 +02:00
parent 922d218e1e
commit 7af14af685

310
w.c
View File

@ -1,20 +1,30 @@
/* w - show what logged in users are doing. Almost entirely rewritten from /*
* scratch by Charles Blake circa June 1996. Some vestigal traces of the * w - show what logged in users are doing.
* original may exist. That was done in 1993 by Larry Greenfield with some *
* fixes by Michael K. Johnson. * Almost entirely rewritten from scratch by Charles Blake circa
* June 1996. Some vestigal traces of the original may exist.
* That was done in 1993 by Larry Greenfield with some fixes by
* Michael K. Johnson.
* *
* Changes by Albert Cahalan, 2002. * Changes by Albert Cahalan, 2002.
*/ */
#include "proc/devname.h"
#include "proc/escape.h"
#include "proc/procps.h"
#include "proc/readproc.h"
#include "proc/sysinfo.h"
#include "proc/version.h" #include "proc/version.h"
#include "proc/whattime.h" #include "proc/whattime.h"
#include "proc/readproc.h"
#include "proc/devname.h"
#include "proc/procps.h"
#include "proc/sysinfo.h"
#include "proc/escape.h"
#include <ctype.h> #include <ctype.h>
#include <err.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <locale.h>
#include <locale.h>
#include <pwd.h>
#include <pwd.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -23,12 +33,10 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <pwd.h> #include <termios.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <utmp.h> #include <utmp.h>
#include <locale.h>
#include <termios.h>
static int ignoreuser = 0; /* for '-u' */ static int ignoreuser = 0; /* for '-u' */
static int oldstyle = 0; /* for '-o' */ static int oldstyle = 0; /* for '-o' */
@ -49,15 +57,18 @@ typedef struct utmp utmp_t;
#define HOSTSZ 40 #define HOSTSZ 40
/* This routine is careful since some programs leave utmp strings /*
* unprintable. Always outputs at least fromlen chars padded with spaces * This routine is careful since some programs leave utmp strings
* on the right if necessary. * unprintable. Always outputs at least 16 chars padded with
* spaces on the right if necessary.
*/ */
static void print_host(const char *restrict host, int len, const int fromlen) { static void print_host(const char *restrict host, int len, const int fromlen)
{
const char *last; const char *last;
int width = 0; int width = 0;
if (len > fromlen) len = fromlen; if (len > fromlen)
len = fromlen;
last = host + len; last = host + len;
for (; host < last; host++) { for (; host < last; host++) {
if (isprint(*host) && *host != ' ') { if (isprint(*host) && *host != ' ') {
@ -67,51 +78,72 @@ static void print_host(const char *restrict host, int len, const int fromlen) {
break; break;
} }
} }
// space-fill, and a '-' too if needed to ensure the column exists
/*
* space-fill, and a '-' too if needed to ensure the
* column exists
*/
while (width++ < fromlen) while (width++ < fromlen)
fputc(' ', stdout); fputc(' ', stdout);
} }
/***** compact 7 char format for time intervals (belongs in libproc?) */ /* compact 7 char format for time intervals (belongs in libproc?) */
static void print_time_ival7(time_t t, int centi_sec, FILE* fout) { static void print_time_ival7(time_t t, int centi_sec, FILE * fout)
if((long)t < (long)0){ /* system clock changed? */ {
if ((long)t < (long)0) {
/* system clock changed? */
printf(" ? "); printf(" ? ");
return; return;
} }
if (oldstyle) { if (oldstyle) {
if (t >= 48*60*60) /* > 2 days */ if (t >= 48 * 60 * 60)
/* > 2 days */
fprintf(fout, " %2ludays", t / (24 * 60 * 60)); fprintf(fout, " %2ludays", t / (24 * 60 * 60));
else if (t >= 60*60) /* > 1 hour */ else if (t >= 60 * 60)
fprintf(fout, " %2lu:%02u ", t/(60*60), (unsigned) ((t/60)%60)); /* > 1 hour */
else if (t > 60) /* > 1 minute */ fprintf(fout, " %2lu:%02u ", t / (60 * 60),
(unsigned)((t / 60) % 60));
else if (t > 60)
/* > 1 minute */
fprintf(fout, " %2lu:%02um", t / 60, (unsigned)t % 60); fprintf(fout, " %2lu:%02um", t / 60, (unsigned)t % 60);
else else
fprintf(fout, " "); fprintf(fout, " ");
} else { } else {
if (t >= 48*60*60) /* > 2 days */ if (t >= 48 * 60 * 60)
/* 2 days or more */
fprintf(fout, " %2ludays", t / (24 * 60 * 60)); fprintf(fout, " %2ludays", t / (24 * 60 * 60));
else if (t >= 60*60) /* > 1 hour */ else if (t >= 60 * 60)
fprintf(fout, " %2lu:%02um", t/(60*60), (unsigned) ((t/60)%60)); /* 1 hour or more */
else if (t > 60) /* > 1 minute */ fprintf(fout, " %2lu:%02um", t / (60 * 60),
(unsigned)((t / 60) % 60));
else if (t > 60)
/* 1 minute or more */
fprintf(fout, " %2lu:%02u ", t / 60, (unsigned)t % 60); fprintf(fout, " %2lu:%02u ", t / 60, (unsigned)t % 60);
else else
fprintf(fout, " %2lu.%02us", t, centi_sec); fprintf(fout, " %2lu.%02us", t, centi_sec);
} }
} }
/**** stat the device file to get an idle time */ /* stat the device file to get an idle time */
static time_t idletime(const char *restrict const tty) { static time_t idletime(const char *restrict const tty)
{
struct stat sbuf; struct stat sbuf;
if (stat(tty, &sbuf) != 0) if (stat(tty, &sbuf) != 0)
return 0; return 0;
return time(NULL) - sbuf.st_atime; return time(NULL) - sbuf.st_atime;
} }
/***** 7 character formatted login time */ /* 7 character formatted login time */
static void print_logintime(time_t logt, FILE* fout) {
char weekday[][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }, static void print_logintime(time_t logt, FILE * fout)
month [][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", {
"Aug", "Sep", "Oct", "Nov", "Dec" }; char weekday[][4] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
char month[][4] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
"Sep", "Oct", "Nov", "Dec"
};
time_t curt; time_t curt;
struct tm *logtm, *curtm; struct tm *logtm, *curtm;
int today; int today;
@ -123,22 +155,28 @@ static void print_logintime(time_t logt, FILE* fout) {
logtm = localtime(&logt); logtm = localtime(&logt);
if (curt - logt > 12 * 60 * 60 && logtm->tm_yday != today) { if (curt - logt > 12 * 60 * 60 && logtm->tm_yday != today) {
if (curt - logt > 6 * 24 * 60 * 60) if (curt - logt > 6 * 24 * 60 * 60)
fprintf(fout, " %02d%3s%02d", logtm->tm_mday, month[logtm->tm_mon], fprintf(fout, " %02d%3s%02d", logtm->tm_mday,
logtm->tm_year % 100); month[logtm->tm_mon], logtm->tm_year % 100);
else else
fprintf(fout, " %3s%02d ", weekday[logtm->tm_wday], logtm->tm_hour); fprintf(fout, " %3s%02d ", weekday[logtm->tm_wday],
logtm->tm_hour);
} else { } else {
fprintf(fout, " %02d:%02d ", logtm->tm_hour, logtm->tm_min); fprintf(fout, " %02d:%02d ", logtm->tm_hour, logtm->tm_min);
} }
} }
/*
/* This function scans the process table accumulating total cpu times for * This function scans the process table accumulating total cpu
* any processes "associated" with this login session. It also searches * times for any processes "associated" with this login session.
* for the "best" process to report as "(w)hat" the user for that login * It also searches for the "best" process to report as "(w)hat"
* session is doing currently. This the essential core of 'w'. * the user for that login session is doing currently. This the
* essential core of 'w'.
*/ */
static const proc_t *getproc(const utmp_t *restrict const u, const char *restrict const tty, unsigned long long *restrict const jcpu, int *restrict const found_utpid) { static const proc_t *getproc(const utmp_t * restrict const u,
const char *restrict const tty,
unsigned long long *restrict const jcpu,
int *restrict const found_utpid)
{
int line; int line;
proc_t **pptr = procs; proc_t **pptr = procs;
const proc_t *best = NULL; const proc_t *best = NULL;
@ -148,11 +186,13 @@ static const proc_t *getproc(const utmp_t *restrict const u, const char *restric
*found_utpid = 0; *found_utpid = 0;
if (!ignoreuser) { if (!ignoreuser) {
char buf[UT_NAMESIZE + 1]; char buf[UT_NAMESIZE + 1];
struct passwd *passwd_data; /* pointer to static data */ /* pointer to static data */
struct passwd *passwd_data;
strncpy(buf, u->ut_user, UT_NAMESIZE); strncpy(buf, u->ut_user, UT_NAMESIZE);
buf[UT_NAMESIZE] = '\0'; buf[UT_NAMESIZE] = '\0';
passwd_data = getpwnam(buf); passwd_data = getpwnam(buf);
if(!passwd_data) return NULL; if (!passwd_data)
return NULL;
uid = passwd_data->pw_uid; uid = passwd_data->pw_uid;
/* OK to have passwd_data go out of scope here */ /* OK to have passwd_data go out of scope here */
} }
@ -164,32 +204,36 @@ static const proc_t *getproc(const utmp_t *restrict const u, const char *restric
*found_utpid = 1; *found_utpid = 1;
best = tmp; best = tmp;
} }
if(tmp->tty != line) continue; if (tmp->tty != line)
continue;
(*jcpu) += tmp->utime + tmp->stime; (*jcpu) += tmp->utime + tmp->stime;
secondbest = tmp; secondbest = tmp;
/* same time-logic here as for "best" below */ /* same time-logic here as for "best" below */
if (!(secondbest && tmp->start_time <= secondbest->start_time)) { if (!(secondbest && tmp->start_time <= secondbest->start_time)) {
secondbest = tmp; secondbest = tmp;
} }
if(!ignoreuser && uid != tmp->euid && uid != tmp->ruid) continue; if (!ignoreuser && uid != tmp->euid && uid != tmp->ruid)
if(tmp->pgrp != tmp->tpgid) continue; continue;
if(best && tmp->start_time <= best->start_time) continue; if (tmp->pgrp != tmp->tpgid)
continue;
if (best && tmp->start_time <= best->start_time)
continue;
best = tmp; best = tmp;
} }
return best ? best : secondbest; return best ? best : secondbest;
} }
static void showinfo(utmp_t * u, int formtype, int maxcmd, int from,
/***** showinfo */ const int userlen, const int fromlen)
static void showinfo(utmp_t *u, int formtype, int maxcmd, int from, const int userlen, const int fromlen) { {
unsigned long long jcpu; unsigned long long jcpu;
int ut_pid_found; int ut_pid_found;
unsigned i; unsigned i;
char uname[USERSZ + 1] = "", char uname[USERSZ + 1] = "", tty[5 + sizeof u->ut_line + 1] = "/dev/";
tty[5 + sizeof u->ut_line + 1] = "/dev/";
const proc_t *best; const proc_t *best;
for (i=0; i < sizeof(u->ut_line); i++) /* clean up tty if garbled */ for (i = 0; i < sizeof(u->ut_line); i++)
/* clean up tty if garbled */
if (isalnum(u->ut_line[i]) || (u->ut_line[i] == '/')) if (isalnum(u->ut_line[i]) || (u->ut_line[i] == '/'))
tty[i + 5] = u->ut_line[i]; tty[i + 5] = u->ut_line[i];
else else
@ -197,34 +241,44 @@ static void showinfo(utmp_t *u, int formtype, int maxcmd, int from, const int us
best = getproc(u, tty + 5, &jcpu, &ut_pid_found); best = getproc(u, tty + 5, &jcpu, &ut_pid_found);
/* just skip if stale utmp entry (i.e. login proc doesn't exist). If there /*
* is a desire a cmdline flag could be added to optionally show it with a * just skip if stale utmp entry (i.e. login proc doesn't
* prefix of (stale) in front of cmd or something like that. * exist). If there is a desire a cmdline flag could be
* added to optionally show it with a prefix of (stale)
* in front of cmd or something like that.
*/ */
if (!ut_pid_found) if (!ut_pid_found)
return; return;
strncpy(uname, u->ut_user, USERSZ); /* force NUL term for printf */ /* force NUL term for printf */
strncpy(uname, u->ut_user, USERSZ);
if (formtype) { if (formtype) {
printf("%-*.*s%-9.8s", userlen + 1, userlen, uname, u->ut_line); printf("%-*.*s%-9.8s", userlen + 1, userlen, uname, u->ut_line);
if (from) if (from)
print_host(u->ut_host, sizeof u->ut_host, fromlen); print_host(u->ut_host, sizeof u->ut_host, fromlen);
print_logintime(u->ut_time, stdout); print_logintime(u->ut_time, stdout);
if (*u->ut_line == ':') /* idle unknown for xdm logins */ if (*u->ut_line == ':')
/* idle unknown for xdm logins */
printf(" ?xdm? "); printf(" ?xdm? ");
else else
print_time_ival7(idletime(tty), 0, stdout); print_time_ival7(idletime(tty), 0, stdout);
print_time_ival7(jcpu/Hertz, (jcpu%Hertz)*(100./Hertz), stdout); print_time_ival7(jcpu / Hertz, (jcpu % Hertz) * (100. / Hertz),
stdout);
if (best) { if (best) {
unsigned long long pcpu = best->utime + best->stime; unsigned long long pcpu = best->utime + best->stime;
print_time_ival7(pcpu/Hertz, (pcpu%Hertz)*(100./Hertz), stdout); print_time_ival7(pcpu / Hertz,
(pcpu % Hertz) * (100. / Hertz),
stdout);
} else } else
printf(" ? "); printf(" ? ");
} else { } else {
printf("%-*.*s%-9.8s", userlen+1, userlen, u->ut_user, u->ut_line); printf("%-*.*s%-9.8s", userlen + 1, userlen, u->ut_user,
u->ut_line);
if (from) if (from)
print_host(u->ut_host, sizeof u->ut_host, fromlen); print_host(u->ut_host, sizeof u->ut_host, fromlen);
if (*u->ut_line == ':') /* idle unknown for xdm logins */ if (*u->ut_line == ':')
/* idle unknown for xdm logins */
printf(" ?xdm? "); printf(" ?xdm? ");
else else
print_time_ival7(idletime(tty), 0, stdout); print_time_ival7(idletime(tty), 0, stdout);
@ -240,8 +294,27 @@ static void showinfo(utmp_t *u, int formtype, int maxcmd, int from, const int us
fputc('\n', stdout); fputc('\n', stdout);
} }
/***** main */ static void __attribute__ ((__noreturn__))
int main(int argc, char **argv) { usage(FILE * out)
{
fprintf(out,
"\nUsage: %s [options]\n"
"\nOptions:\n", program_invocation_short_name);
fprintf(out,
" -h, --no-header do not print header\n"
" -u, --no-current ignore current process username\n"
" -s, --short short format\n"
" -f, --from show remote hostname field\n"
" -o, --old-style old style output\n"
" --help display this help text\n"
" -V, --version display version information and exit\n");
fprintf(out, "\nFor more information see w(1).\n");
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
int main(int argc, char **argv)
{
char *user = NULL, *p; char *user = NULL, *p;
utmp_t *u; utmp_t *u;
struct winsize win; struct winsize win;
@ -250,30 +323,53 @@ int main(int argc, char **argv) {
int fromlen = 16; int fromlen = 16;
char *env_var; char *env_var;
enum {
HELP_OPTION = CHAR_MAX + 1
};
static const struct option longopts[] = {
{"no-header", no_argument, NULL, 'h'},
{"no-current", no_argument, NULL, 'u'},
{"sort", no_argument, NULL, 's'},
{"from", no_argument, NULL, 'f'},
{"old-style", no_argument, NULL, 'o'},
{"help", no_argument, NULL, HELP_OPTION},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
#ifndef W_SHOWFROM #ifndef W_SHOWFROM
from = 0; from = 0;
#endif #endif
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
for (args=0; (ch = getopt(argc, argv, "hlusfVo")) != EOF; args++) while ((ch = getopt_long(argc, argv, "hlusfVo", longopts, NULL)) != -1)
switch (ch) { switch (ch) {
case 'h': header = 0; break; case 'h':
case 'l': longform = 1; break; header = 0;
case 's': longform = 0; break; break;
case 'f': from = !from; break; case 'l':
case 'V': display_version(); exit(0); longform = 1;
case 'u': ignoreuser = 1; break; break;
case 'o': oldstyle = 1; break; case 's':
longform = 0;
break;
case 'f':
from = !from;
break;
case 'V':
display_version();
exit(0);
case 'u':
ignoreuser = 1;
break;
case 'o':
oldstyle = 1;
break;
case HELP_OPTION:
usage(stdout);
default: default:
printf("usage: w -hlsufV [user]\n" usage(stderr);
" -h skip header\n"
" -l long listing (default)\n"
" -s short listing\n"
" -u ignore uid of processes\n"
" -f toggle FROM field (default %s)\n"
" -o old-style output\n"
" -V display version\n", FROM_STRING);
exit(1);
} }
if ((argv[optind])) if ((argv[optind]))
@ -283,7 +379,9 @@ int main(int argc, char **argv) {
if ((env_var = getenv("PROCPS_USERLEN")) != NULL) { if ((env_var = getenv("PROCPS_USERLEN")) != NULL) {
userlen = atoi(env_var); userlen = atoi(env_var);
if (userlen < 8 || userlen > USERSZ) { if (userlen < 8 || userlen > USERSZ) {
fprintf(stderr, "User length environment PROCPS_USERLEN must be between 8 and %d, ignoring.\n", USERSZ); warnx
("User length environment PROCPS_USERLEN must be between 8 and %d, ignoring.\n",
USERSZ);
userlen = 8; userlen = 8;
} }
} }
@ -291,27 +389,29 @@ int main(int argc, char **argv) {
if ((env_var = getenv("PROCPS_FROMLEN")) != NULL) { if ((env_var = getenv("PROCPS_FROMLEN")) != NULL) {
fromlen = atoi(env_var); fromlen = atoi(env_var);
if (fromlen < 8 || fromlen > HOSTSZ) { if (fromlen < 8 || fromlen > HOSTSZ) {
fprintf(stderr, "From length environment PROCPS_FROMLEN must be between 8 and %d, ignoring.\n", HOSTSZ); warnx
("From length environment PROCPS_FROMLEN must be between 8 and %d, ignoring.\n",
HOSTSZ);
fromlen = 16; fromlen = 16;
} }
} }
if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0) if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
maxcmd = win.ws_col; maxcmd = win.ws_col;
else if (p = getenv("COLUMNS")) else if (p = getenv("COLUMNS"))
maxcmd = atoi(p); maxcmd = atoi(p);
else else
maxcmd = 80; maxcmd = 80;
if (maxcmd < 71) { if (maxcmd < 71)
fprintf(stderr, "%d column window is too narrow\n", maxcmd); errx(EXIT_FAILURE, "%d column window is too narrow", maxcmd);
exit(1);
}
maxcmd -= 21 + userlen + (from ? fromlen : 0) + (longform ? 20 : 0); maxcmd -= 21 + userlen + (from ? fromlen : 0) + (longform ? 20 : 0);
if (maxcmd < 3) if (maxcmd < 3)
fprintf(stderr, "warning: screen width %d suboptimal.\n", win.ws_col); warnx("warning: screen width %d suboptimal", win.ws_col);
procs = readproctab(PROC_FILLCOM | PROC_FILLUSR | PROC_FILLSTAT); procs = readproctab(PROC_FILLCOM | PROC_FILLUSR | PROC_FILLSTAT);
if (header) { /* print uptime and headers */ if (header) {
/* print uptime and headers */
print_uptime(); print_uptime();
printf("%-*s TTY ", userlen, "USER"); printf("%-*s TTY ", userlen, "USER");
if (from) if (from)
@ -327,19 +427,27 @@ int main(int argc, char **argv) {
if (user) { if (user) {
for (;;) { for (;;) {
u = getutent(); u = getutent();
if (unlikely(!u)) break; if (unlikely(!u))
if (u->ut_type != USER_PROCESS) continue; break;
if (!strncmp(u->ut_user, user, USERSZ)) showinfo(u, longform, maxcmd, from, userlen, fromlen); if (u->ut_type != USER_PROCESS)
continue;
if (!strncmp(u->ut_user, user, USERSZ))
showinfo(u, longform, maxcmd, from, userlen,
fromlen);
} }
} else { } else {
for (;;) { for (;;) {
u = getutent(); u = getutent();
if (unlikely(!u)) break; if (unlikely(!u))
if (u->ut_type != USER_PROCESS) continue; break;
if (*u->ut_user) showinfo(u, longform, maxcmd, from, userlen, fromlen); if (u->ut_type != USER_PROCESS)
continue;
if (*u->ut_user)
showinfo(u, longform, maxcmd, from, userlen,
fromlen);
} }
} }
endutent(); endutent();
return 0; return EXIT_SUCCESS;
} }