Subject: swho: screen based who (curses, continuous update) From: Paul Pomes Newsgroups: mod.sources Approved: jpn@panda.UUCP Mod.sources: Volume 3, Issue 74 Submitted by: Paul Pomes (Univ of Illinois) #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # README # Makefile # swho.c # swho.1 sed 's/^X//' << 'SHAR_EOF' > README XSwho provides an enhanced who command using curses. Besides displaying Xusercodes, ttys, and login times, swho also provides idle time indicators Xand tty types (dialup, switch, localnet, etc). The program is easy on Xsystem resources as it stats the utmp file on every wakeup and reads it Xonly if it has changed. The screen is cleared every ten minutes and Xre-drawn to eliminate screen confusion from messages, announcements, etc. XThis "feature" can be eliminated by undefining REDRAW. X X Paul Pomes X XUUCP: {ihnp4,pur-ee,convex}!uiucdcs!uiucuxc!paul XARPANET: paul%uiucuxc@a.cs.uiuc.edu CSNET: paul%uiucuxc@uiuc.csnet XICBM: 40 07 N / 88 13 W XUS Mail: Univ of Illinois, CSO, 1304 W Springfield Ave, Urbana, IL 61801 SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > Makefile X# Makefile for swho, a display utility for showing active users and ttys. X# X# Written by Paul Pomes, University of Illinois, Computing Services Office X# Copyright 1985 by Paul Pomes and University of Illinois Board of Trustees X# X# UUCP: {ihnp4,pur-ee,convex}!uiucdcs!uiucuxc!paul X# ARPANET: paul%uiucuxc@uiuc.arpa X# CSNET: paul%uiucuxc@uiuc.csnet X# US Mail: Univ of Illinois, CSO, 1304 W Springfield Ave, Urbana, IL 61801 X# X# $Header$ X XCFLAGS= -O XLDFLAGS= XLIBS = -lcurses -ltermlib XDESTBIN= /usr/local/bin XDESTLIB= /usr/local/lib XMAN = l X X# make depend doesn't work with single file names as grep doesn't prepend X# the "file.c: " string in front of the match. Quick and dirty kludge is X# to put the filename twice on the SRCS line. X XSRCS = swho.c XOBJS = swho.o X X.DEFAULT: X co $< X Xall: swho X X# X# RCS stuff X# Xci: $(SRCS) X -ci $? X @touch ci X Xcoall: X co -l $(SRCS) X Xupdate: X ci -sDist -u -f$(VERS) $(SRCS) X @touch ci X Xswho: ${OBJS} X cc -o swho ${LDFLAGS} ${OBJS} ${LIBS} X Xinstall: swho X install -s swho ${DESTBIN} X cp swho.1 /usr/man/man${MAN}/swho.${MAN} X Xclean: X rm -f swho *.o core a.out make.log lint.out X Xclobber: X make clean X rm -f ${SRCS} X Xlint: ${SRCS} X lint -habx ${SRCS} X Xcompress: X make clean X find . -size +2 \( -name \*.c -o -name \*.f -o -name \*.h \ X -o -name \*.l -o -name \*,v \) -exec compress {} \; X Xuncompress: X uncompressdir . X Xdepend: X grep '^#include' ${SRCS} \ X | sed -e '/"/s/:[^"]*"\([^"]*\)".*/: \1/' \ X -e '/]*\)>.*/: \/usr\/include\/\1/' \ X | sed 's/\.c/.o/' >makedep X echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep X echo '$$r makedep' >>eddep X echo 'w' >>eddep X cp Makefile Makefile.bak X ed - Makefile < eddep X rm eddep makedep X echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile X echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile X echo '# see make depend above' >> Makefile X X# DO NOT DELETE THIS LINE -- make depend uses it X Xswho.o: /usr/include/stdio.h Xswho.o: /usr/include/utmp.h Xswho.o: /usr/include/strings.h Xswho.o: /usr/include/sys/time.h Xswho.o: /usr/include/signal.h Xswho.o: /usr/include/curses.h Xswho.o: /usr/include/sys/types.h Xswho.o: /usr/include/sys/stat.h X# DEPENDENCIES MUST END AT END OF FILE X# IF YOU PUT STUFF HERE IT WILL GO AWAY X# see make depend above SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > swho.c X/* X * swho -- display users, ttys, and login times using curses X * X * usage: swho [-v] X * X * -v Disable use of standout mode (usually reverse video) X * X * Written by Paul Pomes, University of Illinois, Computing Services Office X * Copyright (C) 1985 by Paul Pomes and the University of Illinois Board X * of Trustees X * X * This program is distributed in the hope that it will be useful, X * but without any warranty. No author or distributor accepts X * responsibility to anyone for the consequences of using it or for X * whether it serves any particular purpose or works at all, unless X * s/he says so in writing. X * X * Everyone is granted permission to copy, modify and redistribute X * this program under the following conditions: X * X * Permission is granted to anyone to make or distribute copies X * of program source code, either as received or modified, in any X * medium, provided that all copyright notices, permission and X * nonwarranty notices are preserved, and that the distributor X * grants the recipient permission for further redistribution as X * permitted by this document, and gives him and points out to X * him an exact copy of this document to inform him of his rights. X * X * Permission is granted to distribute this program in compiled X * or executable form under the same conditions applying for X * source code, provided that either X * A. it is accompanied by the corresponding machine-readable X * source code, or X * B. it is accompanied by a written offer, with no time limit, X * to give anyone a machine-readable copy of the corresponding X * source code in return for reimbursement of the cost of X * distribution. This written offer must permit verbatim X * duplication by anyone. X * C. it is distributed by someone who received only the X * executable form, and is accompanied by a copy of the X * written offer of source code which he received along with it. X * X * In other words, you are welcome to use, share and improve this X * program. You are forbidden to forbid anyone else to use, share X * and improve what you give them. Help stamp out software-hoarding! X * X * UUCP: {ihnp4,pur-ee,convex}!uiucdcs!uiucuxc!paul X * ARPANET: paul@uiucuxc.cso.uiuc.edu X * CSNET: paul%uiucuxc@uiuc.csnet X * ICBMS: 88 13 N / 40 07 W X * US Mail: Univ of Illinois, CSO, 1304 W Springfield Ave, Urbana, IL 61801 X * X * $Log: swho.c,v $ X * Revision 1.5 86/01/01 13:17:42 paul X * Made screen re-draw a #ifdef REDRAW option. X * X * Revision 1.4 85/12/09 17:08:02 paul X * Added -v switch and test for termcap entry sg to disable standout mode. X * sg entry indicates standout mode needs a space before and after. Not X * so good for a program that uses every one. X * X * Revision 1.3 85/11/15 15:56:10 paul X * Added feature to clear and re-draw the screen every ten minutes. X * X * Revision 1.2 85/10/30 13:18:17 paul X * Added code to denote ttys with no shell as " ---- " in the name X * field. -pbp X * X * Revision 1.1 85/10/24 17:37:22 paul X * Initial revision X * X */ X X#ifndef lint Xstatic char RcsId[] = "$Header: swho.c,v 1.5 86/01/01 13:17:42 paul Exp $"; X#endif X X#include X#include X X#ifdef SYS5 X#include X#define index strchr X#else X#include X#endif SYS5 X X#include X#include X#include X#include X#include X X#define equal(s1, s2) (strcmp(s1, s2) == 0) X X/* utmp sizes */ X#define NMAX sizeof(utmp.ut_name) X#define LMAX sizeof(utmp.ut_line) X X/* width of 1 display column */ X#define COL_WIDTH 20 X X/* update interval in seconds */ X#define INTERVAL 10 X X/* count of how many INTERVALs before completely re-drawing the screen. X * comment this out to disable screen re-draws. X */ X#define REDRAW 60 X X/* x/60 rounded */ X#define DIV60(t) ((t+30)/60) X X/* number of COL_WIDTH columns on screen */ Xint ncols; X X/* special terminal types and their symbols */ Xstruct special { X char *name; X char symbol; X} special[] = { X "dialup", 'D', X "lnet", 'L', X "telenet", 'T', X "wats", 'W', X "switch", 'S', X NULL, '\0' X}; X X/* set if terminal inserts blanks when standout mode is used */ Xint sg; X X/* current time for idle calculations and display */ Xtime_t clock; X X/* one line of utmp information */ Xstruct utmp utmp; X X/* copy of argv[0] for error messages */ Xchar *self; X X/* standout mode disabled if set */ Xint video_flag = 0; X X/* debug messages printed if set */ Xint debug_flag = 0; X X/* interrupt handler */ Xint quit(); X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X /* a useful counter */ X int i; X X /* line and column manipulation */ X int line, col; X X#ifdef REDRAW X /* re-draw screen when equal to REDRAW */ X int redo_timer = 0; X#endif REDRAW X X /* number of minutes user is idle */ X time_t idle; X X /* copy of utmp.ut_line */ X char tty[LMAX+1]; X X /* copy of utmp.ut_name */ X char name[NMAX+1]; X X /* stat /etc/utmp every INTERVAL seconds; read only when changed */ X struct stat stb; X time_t *mtime = &stb.st_mtime; X time_t lasttime; X X /* stream descriptor for /etc/ttys file */ X register FILE *tfd; X X /* set to '1' if tty line has a shell */ X int ttyon; X X /* stream descriptor for utmp file */ X register FILE *ufd; X X /* pointer to time struct */ X struct tm *tm; X X /* library routines */ X char *malloc(); X X /* X * squirrel a copy of *argv[0] away for use in error messages X */ X self = malloc((unsigned) (strlen(*argv) + 1)); X (void) strcpy(self, *argv); X X /* parse arguments */ X i = 1; X while (i < argc && *argv[i] == '-') { X if (equal(argv[i]+1, "d")) { X /* d - set debug level */ X debug_flag++; X i++; X fprintf(stderr, "%s: debug option enabled\n", self); X } X if (equal(argv[i]+1, "v")) { X /* v - disable use of standout mode */ X video_flag++; X i++; X } X else { X /* command line errors */ X fprintf(stderr, "%s: %s - bad flag\n", self, argv[i]+1); X fprintf(stderr, "Usage: %s [-v]\n", self); X exit(1); X } X } X if ((tfd = fopen("/etc/ttys", "r")) == NULL) { X fprintf(stderr, "%s: ", self); X perror("/etc/ttys"); X exit(1); X } X if ((ufd = fopen("/etc/utmp", "r")) == NULL) { X fprintf(stderr, "%s: ", self); X perror("/etc/utmp"); X exit(1); X } X (void) signal(SIGINT, quit); X (void) signal(SIGHUP, quit); X so_chk(); X initscr(); X ncols = (COLS / COL_WIDTH) - 1; X noecho(); X ttylist(); X *mtime = (time_t) 0; X while (1) { X#ifdef REDRAW X if (redo_timer++ == REDRAW) { X redo_timer = 0; X *mtime = (time_t) 0; X ttylist(); X } X#endif REDRAW X (void) time(&clock); X tm = localtime(&clock); X if (! video_flag) X standout(); X mvprintw(0, 0, "%.19s", asctime(tm)); X if (! video_flag) X standend(); X lasttime = *mtime; X (void) fstat(fileno(ufd), &stb); X if (*mtime > lasttime) { X (void) fread((char *) &utmp, sizeof(utmp), 1, ufd); X ttyon = fgetc(tfd); X for (line = 1, col = 0; ;) { X if (fread((char *) &utmp, sizeof(utmp), 1, ufd) != 1) X break; X if (line == LINES) { X if (col == ncols) X break; X else { X line = 0; X col++; X } X } X if (ttyon == '0' && utmp.ut_name[0] == '\0') X mvprintw(line, col*20, " ---- "); X else if (utmp.ut_name[0] == '\0') { X mvprintw(line, col*20, " "); X mvprintw(line, col*20+13, " "); X if (! sg && ! video_flag) X standout(); X mvprintw(line, col*20+8, " "); X if (! sg && ! video_flag) X standend(); X } X else { X tm = localtime((time_t *) &utmp.ut_time); X X /* utmp strings may not have end null */ X (void) strncpy(tty, utmp.ut_line, LMAX); X (void) strncpy(name, utmp.ut_name, NMAX); X mvprintw(line, col*20, "%8s", name); X mvprintw(line, col*20+13, "%2d:%02d", tm->tm_hour, tm->tm_min); X idle = findidle(); X if (! sg && ! video_flag) X standout(); X if (idle > (time_t) 60) X mvprintw(line, col*20+8, "+"); X else if (idle > (time_t) 30) X mvprintw(line, col*20+8, "-"); X else X mvprintw(line, col*20+8, " "); X if (! sg && ! video_flag) X standend(); X } X /* advance the pointer in /etc/ttys */ X while ((ttyon = fgetc(tfd)) != EOF && ttyon != '\n') X ; X if (ttyon == EOF || (ttyon = fgetc(tfd)) == EOF) X break; X line++; X } X rewind(tfd); X rewind(ufd); X } X refresh(); X sleep(INTERVAL); X } X} X X/* X * quit -- cleanup after interrupt X * X * parameters: X * none X * returns: X * none X * side effects: X * none X * deficiencies: X */ Xquit() X{ X (void) signal(SIGINT, SIG_IGN); X clear(); X refresh(); X endwin(); X exit(0); X} X X/* X * so_chk -- check whether terminal inserts blanks with standout mode X * X * parameters: X * none X * returns: X * none X * side effects: X * sets global variable sg X * deficiencies: X */ Xso_chk() X{ X char tbuf[1024]; X int ret_value; X X char *getenv(); X X if ((ret_value = tgetent(tbuf, getenv("TERM"))) != 1) { X if (ret_value == 0) /* no entry */ X sg = 0; X else { X fprintf(stderr, "%s: so_chk: can't open /etc/termcap\n", self); X exit(1); X } X return; X } X if ((sg = tgetnum("sg")) == -1) X sg = 0; X return; X} X X/* X * ttylist -- display the ttys X * X * parameters: X * none X * returns: X * none X * side effects: X * updates the display X * deficiencies: X */ Xttylist() X{ X X register FILE *fd0, *fd1; X register struct special *ps; X char entry[25]; X register char *pe; X register c; X int line, col; X X /* open files ttys and ttytype. die gracefully otherwise */ X if ((fd0 = fopen("/etc/ttys", "r")) == NULL) { X fprintf(stderr, "%s: ttylist: ", self); X perror("/etc/ttys"); X exit(1); X } X if ((fd1 = fopen("/etc/ttytype", "r")) == NULL) { X fprintf(stderr, "%s: ttylist: ", self); X perror("/etc/ttytype"); X exit(1); X } X X clear(); X /* first line is the console and is a special case */ X pe = entry; X while ((c = getc(fd0)) != '\n') X *pe++ = c; X *pe = '\0'; X pe = entry; X mvprintw(1, 9, "%.3s", pe+2); X X /* first line of ttytype we don't care about */ X while ((c = getc(fd1)) != '\n') X ; X X /* now continue with the rest of the files */ X for (col = 0, line = 2; ; ) { X if (line == LINES) { X if (col == ncols) X break; X else { X line = 0; X col++; X } X } X while ((c = getc(fd0)) != '\n' && c != EOF) X *pe++ = c; X if (c == EOF) X break; X *pe = '\0'; X pe = entry; X mvprintw(line, col*20+10, "%2s", pe + (strlen(pe)-2)); X X /* read the tty type and mark the special ones */ X while ((c = getc(fd1)) != '\n' && c != EOF) X *pe++ = c; X if (c == EOF) X break; X *pe = '\0'; X pe = entry; X if (index(pe, '\t')) X *(index(pe, '\t')) = '\0'; X else if (index(pe, ' ')) X *(index(pe, ' ')) = '\0'; X if (! sg && ! video_flag) X standout(); X mvprintw(line, col*20+8, " "); X for (ps = special; ps->name != NULL; ps++) X if (equal(ps->name, pe)) X mvprintw(line, col*20+8, " %c", ps->symbol); X if (! sg && ! video_flag) X standend(); X line++; X } X (void) fclose(fd0); X (void) fclose(fd1); X X /* clean up and go back */ X refresh(); X} X X/* X * findidle -- find & return number of minutes current tty has been idle X * X * parameters: X * none X * returns: X * idle time in (time_t) minutes X * side effects: X * none X * deficiencies: X * idle time is a slippery idea, this routine checks only the X * access timestamp on the utmp.ut_line tty. X */ Xtime_t Xfindidle() X{ X struct stat stbuf; X time_t lastaction, diff; X char ttyname[20]; X X (void) strcpy(ttyname, "/dev/"); X (void) strcatn(ttyname, utmp.ut_line, LMAX); X (void) stat(ttyname, &stbuf); X lastaction = stbuf.st_atime; X diff = clock - lastaction; X diff = DIV60(diff); X if (diff < 0) X diff = 0; X return(diff); X} SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > swho.1 X.TH SWHO 1 "UofI CSO" X.SH NAME Xswho \- screen based who X.SH SYNOPSIS X.B swho X[ X.B \-v X] X.SH DESCRIPTION X.I Swho Xis a screen based utility that displays X.IR who (1) Xinformation using the X.IR curses (3) Xpackage. XSpecifying the X.B \-v Xflag inhibits the use of standout mode (usually reverse video). X.PP XEach column displays, in order, the user name, an idle time character, Xa tty type character, the last two letters of the tty name, and the Xlogin time. X.PP XIf the tty line is turned off in /etc/ttys, a " \-\-\-\- " is printed Xin the user name field. X.PP XThe idle time character is a '\-' for idle times greater than 30 minutes Xbut less than an hour, and a '+' for idle times over an hour. X.PP XThe tty type character indicates a general class of tty: D for dialup, XS for Gandalf Switch, L for LocalNet, T for Telenet, and W for WATS. X.SH FILES X/etc/utmp, /etc/ttys, /etc/ttytype X.SH "SEE ALSO" Xwho(1), w(1) SHAR_EOF exit 0