summaryrefslogtreecommitdiff
path: root/w.c
diff options
context:
space:
mode:
Diffstat (limited to 'w.c')
-rw-r--r--w.c298
1 files changed, 298 insertions, 0 deletions
diff --git a/w.c b/w.c
new file mode 100644
index 0000000..e847d86
--- /dev/null
+++ b/w.c
@@ -0,0 +1,298 @@
+/* w - show what logged in users are doing. 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.
+ */
+#include "proc/version.h"
+#include "proc/whattime.h"
+#include "proc/readproc.h"
+#include "proc/devname.h"
+#include "proc/procps.h"
+#include "proc/sysinfo.h"
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+/* #include <sys/param.h>*/ /* for HZ */
+
+static int ignoreuser = 0; /* for '-u' */
+static proc_t **procs; /* our snapshot of the process table */
+
+typedef struct utmp utmp_t;
+
+#ifdef W_SHOWFROM
+# define FROM_STRING "on"
+#else
+# define FROM_STRING "off"
+#endif
+
+/* Uh... same thing as UT_NAMESIZE */
+#define USERSZ (sizeof u->ut_user)
+
+
+/* This routine is careful since some programs leave utmp strings
+ * unprintable. Always outputs at least 16 chars padded with spaces
+ * on the right if necessary.
+ */
+static void print_host(char* host, int len) {
+ char *last;
+ int width = 0;
+
+ /* FIXME: there should really be a way to configure this... */
+ /* for now, we'll just limit it to the 16 that the libc5 version
+ * of utmp uses.
+ */
+ if (len > 16) len = 16;
+ last = host + len;
+ for ( ; host < last ; host++){
+ if (isprint(*host) && *host != ' ') {
+ fputc(*host, stdout);
+ ++width;
+ } else {
+ break;
+ }
+ }
+ if(!width){ /* blank fields screw up parsers */
+ fputc('-', stdout);
+ ++width;
+ }
+ /* if *any* unprintables(or blanks), replace rest of line with spaces */
+ while (width < 16) {
+ fputc(' ', stdout);
+ ++width;
+ }
+}
+
+/***** compact 7 char format for time intervals (belongs in libproc?) */
+static void print_time_ival7(time_t t, int centi_sec, FILE* fout) {
+ if((long)t < (long)0){ /* system clock changed? */
+ printf(" ? ");
+ return;
+ }
+ if (t >= 48*60*60) /* > 2 days */
+ fprintf(fout, " %2ludays", t/(24*60*60));
+ else if (t >= 60*60) /* > 1 hour */
+ fprintf(fout, " %2lu:%02um", t/(60*60), (unsigned) ((t/60)%60));
+ else if (t > 60) /* > 1 minute */
+ fprintf(fout, " %2lu:%02u ", t/60, (unsigned) t%60);
+ else
+ fprintf(fout, " %2lu.%02us", t, centi_sec);
+}
+
+/**** stat the device file to get an idle time */
+static time_t idletime(char *tty) {
+ struct stat sbuf;
+ if (stat(tty, &sbuf) != 0)
+ return 0;
+ return time(NULL) - sbuf.st_atime;
+}
+
+/***** 7 character formatted login time */
+static void print_logintime(time_t logt, FILE* fout) {
+ char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
+ *month [] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
+ "Aug", "Sep", "Oct", "Nov", "Dec" };
+ time_t curt;
+ struct tm *logtm, *curtm;
+ int hour;
+ char *merid; /* meridian indicator */
+ int today;
+
+ curt = time(NULL);
+ curtm = localtime(&curt);
+ /* localtime returns a pointer to static memory */
+ today = curtm->tm_yday;
+ logtm = localtime(&logt);
+ hour = logtm->tm_hour;
+ merid = (hour < 12) ? "am" : "pm";
+ if (hour >= 12) hour -= 12;
+ if (hour == 0) hour = 12;
+ if (curt - logt > 12*60*60 && logtm->tm_yday != today) {
+ if (curt - logt > 6*24*60*60)
+ fprintf(fout, " %02d%3s%02d", logtm->tm_mday, month[logtm->tm_mon],
+ logtm->tm_year % 100);
+ else
+ fprintf(fout, " %3s%02d%s", weekday[logtm->tm_wday], hour, merid);
+ } else {
+ fprintf(fout, " %02d:%02d%s", hour, logtm->tm_min, merid);
+ }
+}
+
+
+/* This function scans the process table accumulating total cpu times for
+ * any processes "associated" with this login session. It also searches
+ * for the "best" process to report as "(w)hat" the user for that login
+ * session is doing currently. This the essential core of 'w'.
+ */
+static proc_t *getproc(utmp_t *u, char *tty, int *jcpu, int *found_utpid) {
+ int line;
+ proc_t **p, *best = NULL, *secondbest = NULL;
+ unsigned uid = ~0U;
+
+ if(!ignoreuser){
+ char buf[UT_NAMESIZE+1];
+ struct passwd *passwd_data; /* pointer to static data */
+ strncpy(buf,u->ut_user,UT_NAMESIZE);
+ buf[UT_NAMESIZE] = '\0';
+ passwd_data = getpwnam(buf);
+ if(!passwd_data) return NULL;
+ uid = passwd_data->pw_uid;
+ /* OK to have passwd_data go out of scope here */
+ }
+ line = tty_to_dev(tty);
+ *jcpu = *found_utpid = 0;
+ for(p = procs; *p; p++) {
+ if((**p).pid == u->ut_pid) *found_utpid = 1;
+ if((**p).tty != line) continue;
+ (*jcpu) += (**p).utime + (**p).stime;
+ secondbest = *p;
+ /* same time-logic here as for "best" below */
+ if(! (secondbest && (**p).start_time <= secondbest->start_time) ){
+ secondbest = *p;
+ }
+ if(!ignoreuser && uid != (**p).euid && uid != (**p).ruid) continue;
+ if((**p).pid != (**p).tpgid) continue;
+ if(best && (**p).start_time <= best->start_time) continue;
+ best = *p;
+ }
+ return best ? best : secondbest;
+}
+
+
+/***** showinfo */
+static void showinfo(utmp_t *u, int formtype, int maxcmd, int from) {
+ int jcpu, ut_pid_found;
+ unsigned i;
+ char uname[USERSZ + 1] = "",
+ tty[5 + sizeof u->ut_line + 1] = "/dev/";
+ proc_t *best;
+
+ for (i=0; i < sizeof(u->ut_line); i++) /* clean up tty if garbled */
+ if (isalnum(u->ut_line[i]) || (u->ut_line[i]=='/'))
+ tty[i+5] = u->ut_line[i];
+ else
+ tty[i+5] = '\0';
+
+ 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
+ * prefix of (stale) in front of cmd or something like that.
+ */
+ if (!ut_pid_found)
+ return;
+
+ strncpy(uname, u->ut_user, USERSZ); /* force NUL term for printf */
+ if (formtype) {
+ printf("%-9.8s%-9.8s", uname, u->ut_line);
+ if (from)
+ print_host(u->ut_host, sizeof u->ut_host);
+ print_logintime(u->ut_time, stdout);
+ if (*u->ut_line == ':') /* idle unknown for xdm logins */
+ printf(" ?xdm? ");
+ else
+ print_time_ival7(idletime(tty), 0, stdout);
+ print_time_ival7(jcpu/Hertz, (jcpu%Hertz)*(100./Hertz), stdout);
+ if (best) {
+ int pcpu = best->utime + best->stime;
+ print_time_ival7(pcpu/Hertz, (pcpu%Hertz)*(100./Hertz), stdout);
+ } else
+ printf(" ? ");
+ } else {
+ printf("%-9.8s%-9.8s", u->ut_user, u->ut_line);
+ if (from)
+ print_host(u->ut_host, sizeof u->ut_host);
+ if (*u->ut_line == ':') /* idle unknown for xdm logins */
+ printf(" ?xdm? ");
+ else
+ print_time_ival7(idletime(tty), 0, stdout);
+ }
+ fputs(" ", stdout);
+ if (best) {
+ if (best->cmdline)
+ print_strlist(stdout, best->cmdline, " ", maxcmd);
+ else
+ printf("%*.*s", -maxcmd, maxcmd, best->cmd);
+ } else {
+ printf("-");
+ }
+ fputc('\n', stdout);
+}
+
+/***** main */
+int main(int argc, char **argv) {
+ char *user = NULL;
+ utmp_t *u;
+ struct winsize win;
+ int header=1, longform=1, from=1, args, maxcmd=80, ch;
+
+#ifndef W_SHOWFROM
+ from = 0;
+#endif
+
+ for (args=0; (ch = getopt(argc, argv, "hlusfV")) != EOF; args++)
+ switch (ch) {
+ case 'h': header = 0; break;
+ case 'l': longform = 1; break;
+ case 's': longform = 0; break;
+ case 'f': from = !from; break;
+ case 'V': display_version(); exit(0);
+ case 'u': ignoreuser = 1; break;
+ default:
+ printf("usage: w -hlsufV [user]\n"
+ " -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"
+ " -V display version\n", FROM_STRING);
+ exit(1);
+ }
+
+ if ((argv[optind]))
+ user = (argv[optind]);
+
+ if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
+ maxcmd = win.ws_col;
+ if (maxcmd < 71) {
+ fprintf(stderr, "%d column window is too narrow\n", maxcmd);
+ exit(1);
+ }
+ maxcmd -= 29 + (from ? 16 : 0) + (longform ? 20 : 0);
+ if (maxcmd < 3)
+ fprintf(stderr, "warning: screen width %d suboptimal.\n", win.ws_col);
+
+ procs = readproctab(PROC_FILLCMD | PROC_FILLUSR);
+
+ if (header) { /* print uptime and headers */
+ print_uptime();
+ printf("USER TTY ");
+ if (from)
+ printf("FROM ");
+ if (longform)
+ printf(" LOGIN@ IDLE JCPU PCPU WHAT\n");
+ else
+ printf(" IDLE WHAT\n");
+ }
+
+ utmpname(UTMP_FILE);
+ setutent();
+ while ((u=getutent())) {
+ if (u->ut_type == USER_PROCESS &&
+ (user ? !strncmp(u->ut_user, user, USERSZ) : *u->ut_user))
+ showinfo(u, longform, maxcmd, from);
+ }
+ endutent();
+
+ return 0;
+}