[mod.sources] swho: screen based who

sources-request@panda.UUCP (01/02/86)

Mod.sources:  Volume 3, Issue 74
Submitted by: Paul Pomes (Univ of Illinois) <seismo!uiucdcs!uiucuxc!paul>


#!/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 '/</s/:[^<]*<\([^>]*\)>.*/: \/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	<stdio.h>
X#include	<utmp.h>
X
X#ifdef	SYS5
X#include	<string.h>
X#define		index		strchr
X#else
X#include	<strings.h>
X#endif	SYS5
X
X#include	<sys/time.h>
X#include	<signal.h>
X#include	<curses.h>
X#include	<sys/types.h>
X#include	<sys/stat.h>
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