[net.sources] lu.c -- Visual

mikec@reed.UUCP (Michael Cooper) (11/10/84)

This program is a visual (CRT) oriented who(1) command.  It either
constantly displays who's on, their tty, etc.  Or it prints specific
information about a user if that user is specifed on the command line.
For more info, see the manual.

					Michael Cooper

______________________________________________________________________________
UUCP:
{decvax, ucbvax, pur-ee, uw-beaver, masscomp, cbosg,
 mit-ems, psu-cs, uoregon, orstcs, ihnp4, uf-cgrl}!tektronix--+
------------------------------------------	  teneron-----|
|          ||||||||||        LORD        |	  ogcvax------|
>         !||||      |      DIMWIT       <	  muddcs------|
|         ||||    ---|     FLATHEAD      |	  cadic-------+-!reed!mikec
|         |||C     CC \                  |	  oresoft-----|
>          ||||       _\                 < 	  grpwre------|
|           ||| (____|       OUR         |	  isonvax-----|
|            ||      |    EXCESSIVE      |	  nsc-pdc-----+
>             |______|      LEADER       <
---^----^----^----^----^----^----^----^---
#----------C--U--T-----H--E--R--E---------------C--U--T-------H--E--R--E-------
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing sh file.  (Files
# unpacked will be owned by you and have default permissions.)
#
# Date Created: Sat Nov 10 12:30:07 PST 1984
# Contents of archive:
# Makefile README lav.c lu.1 lu.c
echo x - lu.1
sed 's/^x//' >lu.1 <<'!Funky!Stuff!'
x.TH LU 1 
x.SH NAME
xlu \- list users
x.SH SYNOPSIS
x.B lu 
x[ 
x.B \-s
x]
x[
x.B \-f 
xfilename
x] 
x[
x.B user1
x.B user2 ... 
x.B usern
x]
x.br
x.SH DESCRIPTION
x.I lu
xlists the users who are logged on continuously.
xIts display is similiar to that of who(1).
xIt prints the name of the user, the tty which they are using,
xthe time that user logged on, their group name, and the idle time.
xThe bottom status line contains, the current time, the load average, 
xthe number of users and whether or not their is mail.
xTo redraw the screen, type quit (^\\).  To exit, type interupt (^C or ^?).
x.B lu
xrequires that you use a CRT with a known termcap entry, since it 
xuses curses to display the information in the above manner.
x.PP
xIf you supply a user name on the command line, it will print out that
xpersons uid, group name, group id, home directory, and login shell.
x.PP
xThe following options are currently supported:
x.TP 10
x.B "s"
xDon't use standout mode.
x.TP 10
x.B "f filename"		
xUse 'filename' as an alternitive file to /etc/utmp.  If this file cannot
xbe opened, /etc/utmp is used.
x.br
x.sp 1
x.SH "SEE ALSO"
xwho(1)
x.sp 1
x.SH DIAGNOSTICS
xThere are no diagnostics.
x.sp 1
x.SH AUTHORS
xOrignally written by Matt Giger.
x.br
xTotally re-written by Michael Cooper (tektronix!reed!mikec)
x.sp 1
x.SH BUGS
x.PP
xStart-up is VERY slow.
x.PP
xDue to the slowness of some of the routines, lu never 'sleeps'.  It is
xconstantly doing something.  This includes checking for mail, looking up
xgroup names, idle times, etc.  The more loaded the system is, the less
xoften the screen is updated.
!Funky!Stuff!
echo x - Makefile
cat >Makefile <<'!Funky!Stuff!'
#
# Makefile for lu
#
# 11-1-84	reed!mikec
#

CC = cc
LIBS = -lcurses -ltermcap
# destination directory
BIN = /bin
# location of lav program if not using pdp11
LAV = /usr/local/lav

lu: lu.c
	${CC} -s -DLOADAV=\"${LAV}\" lu.c -o lu ${LIBS}

lav: lav.c
	${CC} -s lav.c -o lav

install: lu lav
	mv lu ${BIN}
	mv lav ${LAV}
	chmod 2755 ${LAV}

man: lu.1
	nroff -man lu.1 > lu.man

all: lu lav man
!Funky!Stuff!
echo x - README
cat >README <<'!Funky!Stuff!'
11-3-84							Michael Cooper

lu.c was originally written by Matt Giger.  I have extensively hacked it
into something that resembles nothing of the original program.  In doing
so, I have also fixed almost all of the numorous bugs.  For more info
on what lu does, see the manual.

If you are running under 4.2 BSD:

	I have included a file called lav.c with these sources.  What it does
	is print the load average.  lu then does a fscanf on it to get the
	values of the load.  You say, "What a stupid way of doing it!  Why
	not just put it in lu.c itself?"  Well, under 4.2 bsd, there is no
	load average call.  The only way to get the load average, is to read 
	/dev/kmem, as lav.c does.  On some systems, such as ours, normal 
	users don't have read permissions on kmem for security reasons.
	Thus, they can't get the load average.

	If you are running 4.2bsd and have SU powers, you can either put the
	compiled lav.c (to make it type: make lav) somewhere.  Make it setuid
	root.  Then, change the LOADAV define in the Makefile to the 
	location of lav, if it is not /usr/local/lav.  If you don't want
	to be so messy, you can just add lav to lu.c and change the fscanf's
	and so for.

	If you are running 4.2bsd and DON'T have SU powers, you need
	to have someone with SU powers put the compiled lav somewhere and
	make it setuid root.  Then change the LOADAV define in the Makefile
	to the path of that file.

If you are using a pdp11:

	Make sure 'pdp11' is defined somewhere if its not in whoami.h.
	all you need to do is type make, and away it goes.

If you are using anything else:

	lu should work on most systems.  The only possible problem would
	be the load average calls.  You should be able to figure out what
	to change depending on you system configuration.


			Good Luck!
				Michael Cooper
				tektronix!reed!mikec
!Funky!Stuff!
echo x - lav.c
cat >lav.c <<'!Funky!Stuff!'
/*
 *
 *	la.c -- Print the load average.
 *		lav must be setuid root (or appropriote uid) that can read
 *		/dev/kmem.  On some systems, such as ours, permissions to
 *		kmem are turned off for security reasons.
 *
 */

#include 	<nlist.h>
#include 	<stdio.h>

struct	nlist nl[] = {
	{ "_avenrun" },
	{ "" },
};

int	kmem;
double	avenrun[3];

main()
{
	register int i;

	if ((kmem = open("/dev/kmem", 0)) < 0) {
		fprintf(stderr, "No kmem\n");
		exit(1);
	}

	nlist("/vmunix", nl);

	if (nl[0].n_type==0) {
		fprintf(stderr, "No namelist\n");
		exit(1);
	}

	/*
	 * Print 1, 5, and 15 minute load averages.
	 * (Found by looking in kernel for avenrun).
	 */

	lseek(kmem, (long)nl[0].n_value, 0);
	read(kmem, avenrun, sizeof(avenrun));
	for (i = 0; i < (sizeof(avenrun)/sizeof(avenrun[0])); i++) {
		printf("%.2f ", avenrun[i]);
	}
	printf("\n");
}
!Funky!Stuff!
echo x - lu.c
cat >lu.c <<'!Funky!Stuff!'
/*
 ************************************************************************
 *									*
 * lu.c -- list users command.						*
 *									*
 *	If lu is called without arguments, it goes into an infinite	*
 *	loop of displaying who is logged on, when, etc.			*
 *	If lu is called with a user name as an argument, it		*
 *	displays that users uid, group name, group id, home dir.,	*
 *	and the login shell.						*
 *									*
 *	lu understands the following options:				*
 *	-f filename	use 'filename' as an alternative to /etc/utmp.	*
 *	-s		don't use standout mode of terminal.		*
 *									*
 ************************************************************************
 *									*
 *	Author: Matt Giger						*
 *	Re-written by: Michael Cooper					*
 *									*
 *	Please send comments or changes you make to:			*
 *		tektronix!reed!mikec					*
 *									*
 ************************************************************************
 */

#include	<whoami.h>
#include	<curses.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<signal.h>
#include	<utmp.h>
#include	<grp.h>
#include	<pwd.h>

#define std(s) 		(standout(),printw(s),standend())
#define	DIVIDE60(t)	((t+30)/60)
#define WRITE		0222
#define TBUF 		20

struct utmp     U;
struct stat     stbuf,
                statb;
char   *mailf,				/* name of mailbox */
        outbuf[BUFSIZ],			/* tmp for setbuf */
       *wperm;				/* write perm on or off?? */
char   *getenv (), *getlogin ();
int     ismail,				/* flag for mailbox */
        oldmsize;			/* size of mailbox */

main (argc, argv)
int     argc;
char   *argv[];
{

	register        FILE *fp,
	               *p;
	FILE *popen ();
	register int    users = 0,
	                x,
	                y = 0;
	register struct passwd *ppd;
	register struct group  *ggd;
	char   *a,
	       *b,
	       *c,
	       *tbuf,
	       *linename,
	       *ufile,
		load[80],
		wkbuf[TBUF];
	int     now,
	        i,
		sflg,
	        done (), redraw ();
	time_t ttmp;
#ifdef pdp11
	double vec[3];
#endif
	sflg = 1;

	for(x=1; x<argc; x++) {
		if(argv[x][0] != '-')
			break;
		switch(argv[x][1]) {
			case 'f':	/* name of utmp file */
				ufile = argv[++x];
				break;
			case 's':	/* standout mode flag */
				sflg = 0;
				break;
			default:
				printf("Unknown option: %s", argv[x]);
				break;
		}
	}
	argc -= (x - 1);
	argv += (x - 1);

	if(ufile == NULL || (access(ufile, 4) != 0))
		ufile = "/etc/utmp";

	tbuf = wkbuf;

	if (argc == 1) {		/* continuous display */
		setbuf (stdout, outbuf);
		signal (SIGINT, SIG_IGN);
		initscr ();
		signal (SIGINT, done);
		signal (SIGQUIT, redraw);

		checkmail ();

		for (;;) {	/* this is beginning of continuous loop */
			erase ();
			if(sflg)
				std ("Name      TTY    When   Group     IDLE   Name      TTY    When   Group     IDLE \n");
			else
				printw ("Name      TTY    When   Group     IDLE   Name      TTY    When   Group     IDLE \n");
#ifdef pdp11
			loadav(vec);
			sprintf(load, "Load Average: %.02f %.02f %.02f",
				vec[0], vec[1], vec[2]);
#else
			p = popen (LOADAV, "r");
			fscanf (p, "%f%f%f", &a, &b, &c);
			sprintf(load, "Load Average: %.02f %.02f %.02f",
				a, b, c);
			pclose (p);
#endif
			if ((fp = fopen (ufile, "r")) < 0) {
				fprintf (stderr, "sorry, can't open %s\n",ufile);
				exit (endwin ());
			}
			while (fread ((char *) & U, sizeof (U), 1, fp) == 1) {
				if (U.ut_name[0] == '\0')
					continue;
				ppd = getpwnam (U.ut_name);
				if (strcmp(U.ut_line, "console"))
					linename = U.ut_line + 3;
				else
					linename = "CO";
				checkperm ();
				ggd = getgrgid (ppd -> pw_gid);
				time (&now);
				contime (fidle (now), tbuf);
				mvprintw((++y>LINES)?(y-LINES):y,(y>LINES-1)?41:0,
						"%-9s %s%-3s %-8.6s %-7s %s\n",
						ppd -> pw_name,
						wperm,
						linename,
						ctime (&U.ut_time) + 10,
						ggd -> gr_name,
						tbuf);
				++users;
				for (i = 1; i < TBUF; ++i)
					wkbuf[i] = NULL;
			}
			move (LINES - 1, 0);
			time (&now);
			if(sflg)
				standout ();
			printw ("  Time:%-6.6s   ", ctime (&now) + 10);
			printw (load);
			printw ("   Users: %2d", users);
			checkmail ();
			if (ismail == 1)
				printw ("    You have mail.     ");
			else {
				if (ismail == 2) {
					beep();
					printw ("    You have new mail. ");
				} else
					printw ("         No mail.      ");
			}
			if(sflg)
				standend ();
			refresh ();
			users = 0;
			y = 0;
			fclose (fp);
		}
	}
	else {		/* just print single user information */
		for (x = 1; x < argc; x++) {
			if((ppd = getpwnam (argv[x])) == NULL) {
				printf("Unknown user: %s.\n", argv[x]);
				exit(0);
			}
			if((ggd = getgrgid (ppd -> pw_gid)) == NULL) {
				printf("Unknown group id: %s.\n", ppd->pw_gid);
				exit(0);
			}
			printf ("%-10s  %-5d %s (%d)\t%s\t%s\n", 
				ppd -> pw_name,
				ppd -> pw_uid, 
				ggd -> gr_name, 
				ppd -> pw_gid, 
				ppd -> pw_dir,
				(ppd -> pw_shell[0] == '\0') ? "(standard sh)":
				(strcmp (ppd -> pw_shell, "/bin/csh") == 0) ? 
					"awsome csh" : ppd -> pw_shell);
		}
	}
}

/*
 *
 * fidle() -- find & return number of minutes current tty has been idle
 *
 */

time_t
fidle (now)
long    now;
{
	long    lastaction,
	        diff;
	char    ttyname[20];

	strcpy (ttyname, "/dev/");
	strncat (ttyname, U.ut_line, 8);

	if (stat (ttyname, &stbuf) == -1)
		return (0);

	lastaction = stbuf.st_atime;
	diff = now - lastaction;
	diff = DIVIDE60 (diff);
	if (diff < 0)
		diff = 0;
	return (diff);
}

/*
 * contime() -- converts a time into hours and minutes.
 */
contime (tim, cbuf)
register char  *cbuf;
time_t tim;
{
	if (tim > 0) {
		sprintf (cbuf, "%3ld:%02ld", tim / 60, tim % 60);
	}
	else {
		cbuf = "";
	}
}

/*
 *
 * getmailname -- get name of mail box.
 *
 */

char   *
getmailname () 
{
	static char     mailname[32];
	register char  *p,
	               *username;

	if ((p = getenv ("MAIL")) != NULL)
		return p;
	if ((username = getenv ("USER")) == NULL)
		username = getlogin ();
	if (username == NULL || strlen (username) > 10)
		return NULL;
	sprintf (mailname, "/usr/spool/mail/%s", username);
	return mailname;
}

/*
 *
 * checkmail() -- check to see if there is mail or if new mail has arrived.
 *
 */

checkmail () 
{
	mailf = getmailname ();
	ismail = 0;
	if (mailf != NULL && stat (mailf, &statb) >= 0 && statb.st_size > 0L) {
		ismail = 1;
		if (oldmsize < statb.st_size)
			ismail = 2;
	}
	else
		statb.st_size;
	oldmsize = statb.st_size;
}

/*
 *
 * done() -- exit the program.
 *
 */

done () 
{
	alarm (0);
	signal (SIGINT, SIG_IGN);
	signal (SIGQUIT, SIG_DFL);
	standend ();	/* make sure we're not in standout mode */
	endwin ();
	putchar ('\n');
	exit (0);
}

/*
 *
 * redraw() -- redraw the screen on interrupt (^\).
 *
 */

redraw () 
{
	unsigned        alm;

	alm = alarm (0);
	signal (SIGQUIT, SIG_IGN);
	wrefresh (curscr);
	signal (SIGQUIT, redraw);
	alarm (alm);
}

/*
 *
 * checkperm() -- check to see if write permissions to the terminal
 *		  are turned off.
 *
 */

checkperm () 
{
	if ((stbuf.st_mode & WRITE) == WRITE || !*U.ut_line)
		wperm = " ";
	else
		wperm = "*";
}

/*
 *
 * beep() -- ring the bell on the terminal.
 *
 */

beep()
{
	putchar('\007');
}

#ifdef pdp11
/*
 *
 * loadav() -- returns the load average for 1, 5, & 15 minute jobs,
 *	       respectively.
 *
 */

loadav(vec)
double vec[];
{
	short sv[3];
	int x;

	x = gldav(sv);

	if(x < 0)
		return(x);
	
	vec[0] = ((float) sv[0])/256.0;
	vec[1] = ((float) sv[1])/256.0;
	vec[2] = ((float) sv[2])/256.0;

	return(x);
}
#endif
!Funky!Stuff!