rsalz@bbn.com (Rich Salz) (03/17/91)
Submitted-by: Rich Salz <rsalz@bbn.com> Posting-number: Volume 17, Issue 42 Archive-name: lwho/part01 We don't want to run RWHOD for all the standard reasons, but the information that RWHO and RUPTIME provide is nice to have, so I wrote LWHOD/LWHO, the "low-cost rwho package." All machines have to be able to NFS-mount the same spool directory. A small daemon, LWHOD, on each machine writes a text file containing statistics like boot-time, load average, users, etc. The file is basically self-describing (see lwhod.c), a decision which made this whole thing pretty trivial to do. Thanks to Jon Bentley and his "Programming Pearls" books. The user program, LWHO, reads the files and prints what the user has asked for, in a useful way. It does pretty much everything what rwho and ruptime would do. LWHO reads all the files into memory, so if you run this on hundreds of systems that support hundreds of users, you might lose. I'd call this package a hack, but the ratio of code/functionality is so high that it doesn't seem right. To install, edit the Makefile to get the "install" stuff right for your system. Then edit lwho.h if you want to change the spool directory. When installing a daemon on a host make sure that SPOOLDIR is appropriately NFS-mounted (and symlink'd) on the host. I posted this to alt.sources a year ago. Might as well give it wider distribution. Enjoy! /rich $alz; <rsalz@bbn.com> ------- #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # Contents: README MANIFEST Makefile luptime.1 lwho.1 lwho.c lwho.h # lwhod.8 lwhod.c lwholib.c # Wrapped by rsalz@litchi.bbn.com on Sat Mar 16 18:40:19 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo If this archive is complete, you will see the following message: echo ' "shar: End of archive 1 (of 1)."' if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(1376 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' XLow-cost Remote Who X$Header: /nfs/papaya/source/lwho/RCS/README,v 1.2 91/03/16 18:09:36 rsalz Exp $ XThere is no copyright on this package. X XWe don't want to run RWHOD for all the standard reasons, but the Xinformation that RWHO and RUPTIME provide is nice to have, so I wrote XLWHOD/LWHO, the "low-cost rwho package." X XAll machines have to be able to NFS-mount the same spool directory. A Xsmall daemon, LWHOD, on each machine writes a text file containing Xstatistics like boot-time, load average, users, etc. The file is Xbasically self-describing (see lwhod.c), a decision which made this whole Xthing pretty trivial to do. Thanks to Jon Bentley and his "Programming XPearls" books. X XThe user program, LWHO, reads the files and prints what the user has Xasked for, in a useful way. It does pretty much everything what rwho and Xruptime would do. LWHO reads all the files into memory, so if Xyou run this on hundreds of systems that support hundreds of users, Xyou might lose. X XI'd call this package a hack, but the ratio of code/functionality is so Xhigh that it doesn't seem right. X XTo install, edit the Makefile to get the "install" stuff right for your Xsystem. Then edit lwho.h if you want to change the spool directory. When Xinstalling a daemon on a host make sure that SPOOLDIR is appropriately XNFS-mounted (and symlink'd) on the host. X XEnjoy! X /rich $alz; <rsalz@bbn.com> END_OF_FILE if test 1376 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'MANIFEST' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'MANIFEST'\" else echo shar: Extracting \"'MANIFEST'\" \(632 characters\) sed "s/^X//" >'MANIFEST' <<'END_OF_FILE' X File Name Archive # Description X---------------------------------------------------------- XREADME 1 Introduction XMANIFEST 1 This shipping list XMakefile 1 Guess... Xluptime.1 1 Manpage for luptime (see lwho.1) Xlwho.1 1 Manpage for lwho and luptime Xlwho.c 1 User program to view lwho data Xlwho.h 1 Header file for this package Xlwhod.8 1 Manpage for lwhod Xlwhod.c 1 Daemon to write lwhod data file Xlwholib.c 1 Library routines for this package END_OF_FILE if test 632 -ne `wc -c <'MANIFEST'`; then echo shar: \"'MANIFEST'\" unpacked with wrong size! fi # end of 'MANIFEST' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(1316 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X## X## Low-cost Remote Who X## There is no copyright on this package. X## $Header: /nfs/papaya/source/lwho/RCS/Makefile,v 1.2 91/03/16 18:29:38 rsalz Exp $ X## X X## Installation destinations. XK_GROUP = wheel XBIN = /usr/local/bin XETC = /usr/etc XMAN1 = /usr/man/man1 XMAN8 = /usr/man/man8 X X## Compilation flags. XDEFS = XOPT = -O XCFLAGS = $(OPT) $(DEFS) X X## Xall: lwho lwhod lwho.1 luptime.1 lwhod.8 X Xinstall: all X @echo Install according to local convention X# install lwho.1 $(MAN1) X# install luptime.1 $(MAN1) X# install lwhod.8 $(MAN8) X# @echo Make sure to update rc.local X# install -g $(K_GROUP) -m 2755 -s lwhod $(ETC) X# install -s lwho $(BIN) X# @rm -f $(BIN)/luptime X# ln $(BIN)/lwho $(BIN)/luptime X Xclean: X rm -f *.o lint* foo SHARFILE lwho lwhod X Xlint: lint.u lint.d lint.m X Xshar: X makekit -m X @rm -f MANIFEST.BAK X @mv Part01 SHARFILE X X## Xlint.u: lwho X lint -a -b -h lwho.c lwholib.c >lint.u Xlint.d: lwhod X lint -a -b -h lwhod.c lwholib.c >lint.d Xlint.m: lwho.1 luptime.1 lwhod.8 X checknr -c.BI.IR lwho.1 luptime.1 lwhod.8 >lint.m X X X## Xlwho: lwho.o lwholib.o X @rm -f $@ X $(CC) -o $@ $(OPT) lwho.o lwholib.o X Xlwhod: lwhod.o lwholib.o X @rm -f $@ X $(CC) -o $@ $(OPT) lwhod.o lwholib.o X X X## Xlwho.o lwhod.o lwholib.o: lwho.h X X## X.SUFFIXES: .c. .o .src .obj X.c.src: X #load $(CFLAGS) $< X.c.obj: X #load $(CFLAGS) $< END_OF_FILE if test 1316 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'luptime.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'luptime.1'\" else echo shar: Extracting \"'luptime.1'\" \(103 characters\) sed "s/^X//" >'luptime.1' <<'END_OF_FILE' X.so man1/lwho.1 X.\" $Header: /nfs/papaya/source/lwho/RCS/luptime.1,v 1.2 91/03/16 18:31:56 rsalz Exp $ END_OF_FILE if test 103 -ne `wc -c <'luptime.1'`; then echo shar: \"'luptime.1'\" unpacked with wrong size! fi # end of 'luptime.1' fi if test -f 'lwho.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lwho.1'\" else echo shar: Extracting \"'lwho.1'\" \(2944 characters\) sed "s/^X//" >'lwho.1' <<'END_OF_FILE' X.TH LWHO 1 X.\" $Header: /nfs/papaya/source/lwho/RCS/lwho.1,v 1.2 91/03/16 18:32:05 rsalz Exp $ X.SH NAME Xlwho, luptime \- show who is using local machines X.SH SYNOPSIS X.B lwho X[ X.BI \-u user X] [ X.BI \-m machine X] [ X.B \-x X] [ X.B \-a X] [ X.B \-i X] [ X.B \-r X] X.PP X.B luptime X[ X.BI \-u user X] [ X.BI \-m machine X] [ X.B \-x X] [ X.B \-a X] [ X.B \-b X] [ X.B \-l X] [ X.B \-n X] [ X.B \-u X] [ X.B \-r X] X.PP X.B a.out X.B \-p X[ X.I lwho_options X] X.PP X.B a.out X.B \-h X[ X.I luptime_options X] X.SH DESCRIPTION X.I Lwho Xand X.I luptime Xare the user program for the ``low-cost rwho'' package. XThe read the text files that X.IR lwhod (8) Xcreates in an NFS\-mounted spool directory, and presents the information Xin a variety of useful ways. XThe program is linked under two different names, and the output will Xvary depending on how it is invoked. XTo find host-oriented information, invoke X.I luptime Xor use the ``\-h'' flag. XTo find person-oriented information, invoke X.I lwho Xor use the ``\-p'' flag. X.PP XBy default, the programs Xshow information about all users on all hosts. XTo see information about a specified set of users or hosts, use the ``\-u'' Xflag specify the login names of the desired users, or Xthe ``\-m'' flag to specify the names of the desired hosts. XThese flags may be repeated to specify a collection of hosts or users, Xand may be used together to specify an intersection. XFor example: X.RS X.nf Xlwho \-u rsalz Xlwho \-u root \-m fig.bbn.com \-m pineapple.bbn.com Xlwho \-m fig.bbn.com \-m pineapple.bbn.com X.fi X.RE XThe first line finds all machines where ``rsalz'' is logged in; the Xsecond line finds if ``root'' is logged in on ``fig'' or ``pineapple''; Xthe third line shows all users logged in to either of those machines. X.PP XWhen reporting, output is normally sorted alphabetically by the host or Xuser name; this may be specified by using tyhe ``\-a'' flag. XTo sort by uptime (first machine booted first), use the ``\-b'' flag. XTo sort by the number of users (smallest number first), use the ``\-n'' flag. XTo sort by the load average (most lightly-loaded machine first), use the X\&``\-l'' flag. XTo sort by idle time (most recently used terminal first), use the ``\-i'' flag. XTo reverse the sorting, regardless of the method, use the ``\-r'' flag. XFor X.IR lwho , Xonly the ``\-i'' flag makes sense, if any other flag is given it is ignored. XFor X.IR luptime , Xthe ``\-i'' flag is ignored. X.PP XIf no users (for X.IR lwho ), Xor no hosts (for X.IR luptime ), Xare found, no output is generated. XTo suppress the banner (useful when feeding into a pipeline), Xuse the ``\-x'' flag. X.SH BUGS XThere are no hueristics to tell if a host is up or down, or ignore users Xwho have been idle for a long time the way X.IR rwho (1) Xand X.IR ruptime (1) Xcan and do. X.SH FILES X.ta \w'/usr/spool/lwho/\fIhostname\fP 'u X/usr/spool/lwho/\fIhostname\fP Load and user data X.SH AUTHOR XRich $alz; <rsalz@bbn.com>.uu.net>. X.sp XThere is no copyright on this package. X.SH "SEE ALSO" Xlwhod(8). END_OF_FILE if test 2944 -ne `wc -c <'lwho.1'`; then echo shar: \"'lwho.1'\" unpacked with wrong size! fi # end of 'lwho.1' fi if test -f 'lwho.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lwho.c'\" else echo shar: Extracting \"'lwho.c'\" \(10385 characters\) sed "s/^X//" >'lwho.c' <<'END_OF_FILE' X/* X** Low-cost Remote Who X** There is no copyright on this package. X** User program to read the data files. X*/ X#include "lwho.h" X#include <sys/types.h> X#include <dirent.h> X#if !defined(lint) && !defined(SABER) Xstatic char RCS[] = X "$Header: /nfs/papaya/source/lwho/RCS/lwho.c,v 1.4 91/03/16 18:39:35 rsalz Exp $"; X#endif /* !defined(lint) && !defined(SABER) */ X Xtypedef struct _HOSTDATA { X int Valid; X int Users; X time_t BootTime; X double LoadAve[3]; X char Name[HOSTLEN]; X} HOSTDATA; X Xtypedef struct _USERDATA { X int Valid; X time_t Onsince; X time_t Idle; X char Name[NAMELEN]; X char Line[LINELEN]; X HOSTDATA *Host; X} USERDATA; X X X#define HOST_DELTA 20 XSTATIC HOSTDATA *Hosts; X X#define USER_DELTA 20 XSTATIC USERDATA *Users; X X X/* X** Get a free host entry, growing as needed. X*/ XSTATIC HOSTDATA * XGetHost() X{ X static HOSTDATA *Last; X static int Count; X static int Size; X X /* Give last one back if they didn't fill it in. */ X if (Last && !Last->Valid) X return Last; X X if (Hosts == NULL) { X /* First time here, get space. */ X Count = 0; X Size = HOST_DELTA; X Hosts = NEW(HOSTDATA, Size); X } X else if (Count >= Size - 1) { X /* Need more space. */ X Size += HOST_DELTA; X Hosts = RENEW(Hosts, HOSTDATA, Size); X } X X Hosts[Count].Valid = FALSE; X Last = &Hosts[Count++]; X return Last; X} X X X/* X** Get a free user entry, growing as needed. X*/ XSTATIC USERDATA * XGetUser() X{ X static USERDATA *Last; X static int Count; X static int Size; X X /* Give last one back if they didn't fill it in. */ X if (Last && !Last->Valid) X return Last; X X if (Users == NULL) { X /* First time here, get space. */ X Count = 0; X Size = USER_DELTA; X Users = NEW(USERDATA, Size); X } X else if (Count >= Size - 1) { X /* Need more space. */ X Size += USER_DELTA; X Users = RENEW(Users, USERDATA, Size); X } X X Users[Count].Valid = FALSE; X Last = &Users[Count++]; X return Last; X} X X X/* X** Sort host data by host name. X*/ XSTATIC int XHSalpha(p1, p2) X char *p1; X char *p2; X{ X HOSTDATA *h1; X HOSTDATA *h2; X X h1 = (HOSTDATA *)p1; X h2 = (HOSTDATA *)p2; X return strcmp(h1->Name, h2->Name); X} X X X/* X** Sort host data by time booted. X*/ XSTATIC int XHSboottime(p1, p2) X char *p1; X char *p2; X{ X HOSTDATA *h1; X HOSTDATA *h2; X long l; X X h1 = (HOSTDATA *)p1; X h2 = (HOSTDATA *)p2; X if (l = h1->BootTime - h2->BootTime) X return l < 0 ? -1 : 1; X return 0; X} X X X/* X** Sort host data by host load average. X*/ XSTATIC int XHSloadave(p1, p2) X char *p1; X char *p2; X{ X HOSTDATA *h1; X HOSTDATA *h2; X long l; X X h1 = (HOSTDATA *)p1; X h2 = (HOSTDATA *)p2; X if (l = h1->LoadAve[0] - h2->LoadAve[0]) X return l < 0 ? -1 : 1; X return 0; X} X X X/* X** Sort host data by number of users. X*/ XSTATIC int XHSnumusers(p1, p2) X char *p1; X char *p2; X{ X HOSTDATA *h1; X HOSTDATA *h2; X X h1 = (HOSTDATA *)p1; X h2 = (HOSTDATA *)p2; X return h1->Users - h2->Users; X} X X X/* X** Sort user data alphabetically, than by host, than by idle time. X*/ XSTATIC int XUSalpha(p1, p2) X char *p1; X char *p2; X{ X USERDATA *u1; X USERDATA *u2; X int i; X long l; X X u1 = (USERDATA *)p1; X u2 = (USERDATA *)p2; X if (i = strcmp(u1->Name, u2->Name)) X return i; X if (i = strcmp(u1->Host->Name, u2->Host->Name)) X return i; X if (l = u1->Idle - u2->Idle) X return l < 0 ? -1 : 1; X return 0; X} X X X/* X** Sort user data by idle time. X*/ XSTATIC int XUSidle(p1, p2) X char *p1; X char *p2; X{ X USERDATA *u1; X USERDATA *u2; X long l; X X u1 = (USERDATA *)p1; X u2 = (USERDATA *)p2; X if (l = u1->Idle - u2->Idle) X return l < 0 ? -1 : 1; X return 0; X} X X X/* X** Return a formatted string with how long between a given time and now. X*/ XSTATIC char * XHowLong(t) X time_t t; X{ X static char buff[10]; X int hours; X X /* If out of range, return gunk. */ X if (t < 0 || t > 99 * 24 * 60 * 60) X return "??:??"; X X /* Get minutes and hours from seconds. */ X t = (t + 59) / 60; X hours = t / 60; X X if (hours >= 24) X (void)sprintf(buff, "%02dd ", hours / 24); X else X (void)sprintf(buff, "%02d:%02d", hours, t - hours * 60); X return buff; X} X X X/* X** Split the cruft out of a ctime() result. X*/ XSTATIC char * XMyctime(t) X time_t *t; X{ X char *p; X X p = ctime(t); X p[19] = '\0'; X return p + 4; X} X X X/* X** Do luptime output. X*/ XSTATIC void XShowHostData(Sorter, Reversed, Banner) X int Sorter; X int Reversed; X int Banner; X{ X register HOSTDATA *H; X register int i; X time_t now; X X /* Count those pups. */ X for (H = Hosts, i = 0; H->Valid; H++, i++) X ; X if (i == 0) X return; X X /* Sort that sucker. */ X switch (Sorter) { X default: X case 'a': X case 'i': X qsort((char *)Hosts, i, sizeof *Hosts, HSalpha); X break; X case 'b': X qsort((char *)Hosts, i, sizeof *Hosts, HSboottime); X break; X case 'l': X qsort((char *)Hosts, i, sizeof *Hosts, HSloadave); X break; X case 'n': X qsort((char *)Hosts, i, sizeof *Hosts, HSnumusers); X break; X } X X if (Banner) X printf("%*s Users Load Average Up Booted\n", X -HOSTLEN / 2, "Host"); X X /* Watch the clock. */ X now = time((time_t *)NULL); X X /* Dump the data. */ X if (Reversed) { X Reversed = -1; X H = &Hosts[i - 1]; X } X else { X Reversed = 1; X H = &Hosts[0]; X } X for ( ; --i >= 0; H += Reversed) X printf("%*s %-5d %4.2f %4.2f %4.2f %s %s\n", X -HOSTLEN / 2, H->Name, X H->Users, X H->LoadAve[0], H->LoadAve[1], H->LoadAve[2], X HowLong(now - H->BootTime), Myctime(&H->BootTime)); X} X X X/* X** Do lwho output. X*/ XSTATIC void XShowUserData(Sorter, Reversed, Banner) X int Sorter; X int Reversed; X int Banner; X{ X register USERDATA *U; X register int i; X X /* Count those pups. */ X for (U = Users, i = 0; U->Valid; U++, i++) X ; X if (i == 0) X return; X X /* Sort that sucker. */ X switch (Sorter) { X default: X case 'a': X case 'b': X case 'l': X case 'n': X qsort((char *)Users, i, sizeof *Users, USalpha); X break; X case 'i': X qsort((char *)Users, i, sizeof *Users, USidle); X break; X } X X if (Banner) X printf("%*s %*s %*s %s %s\n", X -NAMELEN, "Name", X -HOSTLEN / 2, "Host", X -LINELEN, "TTY", X "Idle", " On since"); X X /* Dump the data. */ X if (Reversed) { X Reversed = -1; X U = &Users[i - 1]; X } X else { X Reversed = 1; X U = &Users[0]; X } X for ( ; --i >= 0; U += Reversed) X printf("%*s %*s %*s %s %s\n", X -NAMELEN, U->Name, X -HOSTLEN / 2, U->Host->Name, X -LINELEN, U->Line, X HowLong(U->Idle), Myctime(&U->Onsince)); X} X X X/* X** Return TRUE if a string is a member of a NULL-terminated array. X*/ XSTATIC int XFound(argv, p) X register char **argv; X register char *p; X{ X for ( ; *argv; argv++) X if (**argv == *p && strcmp(*argv, p) == 0) X return TRUE; X return FALSE; X} X X Xmain(ac, av) X int ac; X char *av[]; X{ X register HOSTDATA *H; X register USERDATA *U; X register DIR *dp; X register FILE *F; X register int i; X struct dirent *ep; X char buff[BUFFLEN]; X char **Ihosts; X char **Iusers; X int Ihostcount; X int Iusercount; X int DoHosts; X int Reversed; X int SortMode; X int Banner; X long l[3]; X X /* Get program name. */ X if (progname = strrchr(av[0], '/')) X progname++; X else X progname = av[0]; X DoHosts = strcmp(progname, "luptime") == 0; X X /* Set up defaults and arrays of "interesting" hosts/users. */ X Reversed = FALSE; X Banner = TRUE; X SortMode = 'a'; X Ihosts = NEW(char*, ac + 1); X Iusers = NEW(char*, ac + 1); X Ihostcount = 0; X Iusercount = 0; X X /* Parse JCL. */ X while ((i = getopt(ac, av, "abilnrhpxm:u:")) != EOF) X switch (i) { X default: X exit(1); X case 'a': X case 'b': X case 'i': X case 'l': X case 'n': X SortMode = i; X break; X case 'r': X Reversed = TRUE; X break; X case 'h': X DoHosts = TRUE; X break; X case 'p': X DoHosts = FALSE; X break; X case 'x': X Banner = FALSE; X case 'm': X Ihosts[Ihostcount++] = optarg; X break; X case 'u': X Iusers[Iusercount++] = optarg; X break; X } X Iusers[Iusercount] = NULL; X Ihosts[Ihostcount] = NULL; X X /* Go to spool directory, open it. */ X if (chdir(SPOOLDIR) < 0) { X fprintf(stderr, "Can't cd to \"%s\", %s.\n", SPOOLDIR, X strerror(errno)); X exit(1); X } X if ((dp = opendir(".")) == NULL) { X fprintf(stderr, "Can't open directory, %s.\n", strerror(errno)); X exit(1); X } X X /* Read data. */ X while (ep = readdir(dp)) { X /* Is this a file we're interested and that we can access? */ X if (strcmp(ep->d_name, ".") == 0 X || strcmp(ep->d_name, "..") == 0 X || (F = fopen(ep->d_name, "r")) == NULL) X continue; X X /* Check version. */ X if (fgets(buff, sizeof buff, F) == NULL X || strncmp(buff, VERSION, sizeof VERSION - 1) != 0) { X (void)fclose(F); X continue; X } X X /* Is this a host the user is interested in? */ X if (Ihostcount && !Found(Ihosts, ep->d_name)) { X (void)fclose(F); X continue; X } X X /* Get space, store the name. */ X H = GetHost(); X (void)strncpy(H->Name, ep->d_name, sizeof H->Name); X H->Name[sizeof H->Name - 1] = '\0'; X X /* Get boot time. */ X if (fgets(buff, sizeof buff, F) == NULL) { X (void)fclose(F); X continue; X } X H->BootTime = atol(buff); X X /* Get load average. */ X if (fgets(buff, sizeof buff, F) == NULL) { X (void)fclose(F); X continue; X } X if (sscanf(buff, "%ld %ld %ld", &l[0], &l[1], &l[2]) != 3) { X (void)fclose(F); X continue; X } X H->LoadAve[0] = l[0] / 100.; X H->LoadAve[1] = l[1] / 100.; X H->LoadAve[2] = l[2] / 100.; X X /* Get users. */ X for (H->Users = 0; fgets(buff, sizeof buff, F); ) { X X /* Logical end of file? */ X if (strncmp(buff, VERSION, sizeof VERSION - 1) == 0) X break; X X /* Get space, read in a line. */ X U = GetUser(); X if (sscanf(buff, "%s %s %ld %ld", X U->Name, U->Line, &U->Onsince, &U->Idle) != 4) X continue; X H->Users++; X X /* Is this a user the user is interested in? */ X if (Iusercount && !Found(Iusers, U->Name)) X continue; X X U->Host = H; X U->Valid = TRUE; X } X X H->Valid = TRUE; X X (void)fclose(F); X X } X (void)closedir(dp); X X /* Print data. */ X if (DoHosts) X ShowHostData(SortMode, Reversed, Banner); X else X ShowUserData(SortMode, Reversed, Banner); X X /* That's it. */ X exit(0); X /* NOTREACHED */ X} END_OF_FILE if test 10385 -ne `wc -c <'lwho.c'`; then echo shar: \"'lwho.c'\" unpacked with wrong size! fi # end of 'lwho.c' fi if test -f 'lwho.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lwho.h'\" else echo shar: Extracting \"'lwho.h'\" \(1614 characters\) sed "s/^X//" >'lwho.h' <<'END_OF_FILE' X/* X** Low-cost Remote Who X** There is no copyright on this package. X** Header file for the package. X** X** $Header: /nfs/papaya/source/lwho/RCS/lwho.h,v 1.4 91/03/16 18:36:23 rsalz Exp $ X*/ X#include <stdio.h> X#include <sys/types.h> X#include <sys/file.h> X#include <time.h> X X X/* X** Configuration parameters. X*/ X/*#define SIG_CATCHER int /* Older systems */ X#define SIG_CATCHER void /* Modern systems */ X#define NAP_TIME 60 /* How often to update */ X#define SPOOLDIR "/usr/spool/lwho" /* Data directory */ X#define VERSION "R$1" /* Version and EOF mark */ X#undef DEBUG /* Having problems? */ X#undef DAEMON_MUST_RUN_AS_ROOT /* Do setuid(0)? */ X X X/* X** Debugging control. X*/ X#if defined(DEBUG) || defined(SABER) X#define STATIC /* NULL */ X#else X#define STATIC static X#endif /* defined(DEBUG) || defined(SABER) */ X X X/* X** Various constants, you can change these if you reall want to. X*/ X#define TRUE 1 X#define FALSE 0 X X#define BUFFLEN 128 X#define NAMELEN 10 X#define LINELEN 10 X#define HOSTLEN 40 X X X/* X** Syntactic sugar. X*/ X#define NEW(T, i) \ X (T *)xmalloc((unsigned int)i * sizeof (T)) X#define RENEW(p, T, c) \ X ((T *)xrealloc((char *)(p), (unsigned int)(sizeof (T) * (c)))) X X X/* X** From our meager little library. X*/ Xextern char *progname; Xextern int *xmalloc(); Xextern int *xrealloc(); Xextern char *strerror(); X X X/* X** From the C library. X*/ Xextern char *optarg; Xextern int errno; Xextern long atol(); Xextern long lseek(); Xextern time_t time(); Xextern char *sprintf(); /* Painful my ass! */ Xextern char *strrchr(); Xextern char *strcpy(); Xextern char *strncpy(); Xextern void exit(); END_OF_FILE if test 1614 -ne `wc -c <'lwho.h'`; then echo shar: \"'lwho.h'\" unpacked with wrong size! fi # end of 'lwho.h' fi if test -f 'lwhod.8' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lwhod.8'\" else echo shar: Extracting \"'lwhod.8'\" \(1606 characters\) sed "s/^X//" >'lwhod.8' <<'END_OF_FILE' X.\" $Header: /nfs/papaya/source/lwho/RCS/lwhod.8,v 1.3 91/03/16 18:32:49 rsalz Exp $ X.TH LWHOD 8 X.SH NAME Xlwhod \- low-cost rwho daemon X.SH SYNOPSIS X.B /usr/etc/lwhod X.SH DESCRIPTION X.I Lwhod Xis the demon for the ``low-cost rwho'' package. XIt is typically started out of X.I /etc/rc.local Xat system boot-time. X.PP XThe daemon periodically updates a file with the system's host name Xin a spool directory. XThis file is a self-describing text file; a sample entry looks like Xthis: X.RS X.nf XR$1 X606853420 # boottime X56 30 2 # loadave sun-style Xrsalz console 606853499 1024662 # tty user.line.onsince.idle Xrsalz ttyp0 607872590 771 # tty user.line.onsince.idle Xrsalz ttyp1 606853700 766 # tty user.line.onsince.idle Xrsalz ttyp2 606853705 765 # tty user.line.onsince.idle Xrsalz ttyp3 606853705 765 # tty user.line.onsince.idle Xrsalz ttyp5 607797162 0 # tty user.line.onsince.idle XR$1 X.fi X.RE XThe pound signs and the comments are part of the file, but are ignored Xby the user program. XThe system boot time and the user ``onsince'' time are given as the Xnumber of seconds since the standard Unix epoch. XThe load average is the standard system load average, multiplied by 100. XThe ``R$1'' strings that are used to indicate the file version, and Xmark the end of the file. X(Since the daemon keeps the data file open, if people log off the file will Xnot shrink and a logical EOF marker is needed.) X.SH FILES X.ta \w'/usr/spool/lwho/\fIhostname\fP 'u X/usr/spool/lwho/\fIhostname\fP Load and user data for this host. X.SH AUTHOR XRich $alz; <rsalz@bbn.com>. X.sp XThere is no copyright on this package. X.SH "SEE ALSO" Xlwho(1). END_OF_FILE if test 1606 -ne `wc -c <'lwhod.8'`; then echo shar: \"'lwhod.8'\" unpacked with wrong size! fi # end of 'lwhod.8' fi if test -f 'lwhod.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lwhod.c'\" else echo shar: Extracting \"'lwhod.c'\" \(4905 characters\) sed "s/^X//" >'lwhod.c' <<'END_OF_FILE' X/* X** Low-cost Remote Who X** There is no copyright on this package. X** Deamon to write the data file. X*/ X#include "lwho.h" X#include <signal.h> X#include <nlist.h> X#include <utmp.h> X#include <sgtty.h> X#include <sys/stat.h> X#ifdef sun X#include <sys/param.h> X#endif /* sun */ X#if !defined(lint) && !defined(SABER) Xstatic char RCS[] = X "$Header: /nfs/papaya/source/lwho/RCS/lwhod.c,v 1.3 91/03/16 18:29:11 rsalz Exp $"; X#endif /* !defined(lint) && !defined(SABER) */ X XSTATIC FILE *F; XSTATIC char Host[BUFFLEN]; X XSTATIC struct nlist Nlist[] = { X { "_avenrun" }, X#define NL_AVENRUN 0 X { "_boottime" }, X#define NL_BOOTTIME 1 X NULL X}; X X X/* X** We're going down: close open file, remove our data file. X*/ XSTATIC SIG_CATCHER XCatcher() X{ X (void)fclose(F); X (void)unlink(Host); X exit(1); X} X X Xmain() X{ X register FILE *U; X register int i; X register int Kmem; X struct utmp Uentry; X struct stat Sb; X char buff[BUFFLEN]; X char line[20]; X char name[20]; X time_t Now; X int BootTime; X#ifdef vax X double avenrun[3]; X#else X#ifdef sun X int avenrun[3]; X#endif /* sun */ X /* put other types here... */ X#endif /* vax */ X X progname = "rwhod"; X X#ifdef DAEMON_MUST_RUN_AS_ROOT X if (getuid()) { X fprintf(stderr, "%s: permission denied, %s.\n", X progname, strerror(errno)); X exit(1); X } X#endif /* DAEMON_MUST_RUN_AS_ROOT */ X X /* Close stdin, stdout. */ X (void)close(0); X (void)close(1); X X#if !defined(DEBUG) && !defined(SABER) X if (fork() > 0) X exit(0); X#ifdef TIOCNOTTY X if ((i = open("/dev/tty", O_RDWR)) >= 0) { X (void)ioctl(i, TIOCNOTTY, (char *)0); X (void)close(i); X } X#endif /* TIOCNOTTY */ X#endif /* !defined(DEBUG) && !defined(SABER) */ X X if (chdir(SPOOLDIR) < 0) { X fprintf(stderr, "%s: can't cd to \"%s\", %s.\n", X progname, SPOOLDIR, strerror(errno)); X exit(1); X } X X if (gethostname(Host, sizeof Host) < 0) { X fprintf(stderr, "%s: can't get hostname, %s.\n", X progname, strerror(errno)); X exit(1); X } X X /* Read namelist from kernel. */ X Nlist[NL_AVENRUN].n_value = 0; X Nlist[NL_BOOTTIME].n_value = 0; X if (nlist("/vmunix", Nlist) < 0 X || Nlist[NL_AVENRUN].n_value == 0 X || Nlist[NL_BOOTTIME].n_value == 0) { X fprintf(stderr, "%s: can't read namelist, %s.\n", X progname, strerror(errno)); X exit(1); X } X X /* Open kmem, get boot time. */ X if ((Kmem = open("/dev/kmem", O_RDONLY)) < 0) { X fprintf(stderr, "%s: can't read kmem, %s.\n", X progname, strerror(errno)); X exit(1); X } X (void)lseek(Kmem, (long)Nlist[NL_BOOTTIME].n_value, L_SET); X (void)read(Kmem, (char *)&BootTime, sizeof BootTime); X X /* Open utmp. */ X if ((U = fopen("/etc/utmp", "r")) == NULL) { X fprintf(stderr, "%s: can't read utemp, %s.\n", X progname, strerror(errno)); X exit(1); X } X X /* Open our output file, set big buffering. */ X if ((i = open(Host, O_RDWR | O_CREAT | O_TRUNC, 0666)) < 0) { X fprintf(stderr, "%s: can't create \"%s\", %s.\n", X progname, strerror(errno)); X exit(1); X } X F = fdopen(i, "w+"); X setbuf(F, NEW(char, BUFSIZ)); X X /* Arrange to remove file on a termination signal. */ X (void)signal(SIGHUP, Catcher); X (void)signal(SIGINT, Catcher); X (void)signal(SIGQUIT, Catcher); X (void)signal(SIGALRM, Catcher); X (void)signal(SIGTERM, Catcher); X X /* Enter the land of for-ever. */ X for ( ; ; ) { X rewind(F); X fprintf(F, "%s\n", VERSION); X fprintf(F, "%ld # boottime\n", (long)BootTime); X X /* Do the load average. */ X (void)lseek(Kmem, (long)Nlist[NL_AVENRUN].n_value, L_SET); X (void)read(Kmem, (char *)avenrun, sizeof avenrun); X#ifdef vax X for (i = 0; i < 3; i++) X fprintf(F, "%ld ", (long)(avenrun[i] * 100)); X fprintf(F, "# loadave vax-style\n"); X#else X#ifdef sun X for (i = 0; i < 3; i++) X fprintf(F, "%ld ", (long)(avenrun[i] / (FSCALE / 100))); X fprintf(F, "# loadave sun-style\n"); X#else X /* put other types here... */ X fprintf(F, "0 0 0 # loadave unknown\n"); X#endif /* sun */ X#endif /* vax */ X X /* Write utmp entries. */ X rewind(U); X (void)time(&Now); X while (fread((char *)&Uentry, sizeof Uentry, 1, U) == 1) { X if (Uentry.ut_time == 0 || Uentry.ut_name[0] == '\0') X continue; X X#if 0 X#ifdef nonuser X /* List multiple entries for window-systems users? */ X if (nonuser(Uentry)) X continue; X#endif /* nonuser */ X#endif /* 0 */ X X /* Get user name and the line they're on. */ X (void)strncpy(name, Uentry.ut_name, sizeof name); X name[sizeof Uentry.ut_name - 1] = '\0'; X (void)strncpy(line, Uentry.ut_line, sizeof line); X line[sizeof Uentry.ut_line - 1] = '\0'; X X (void)sprintf(buff, "/dev/%s", line); X X fprintf(F, "%s %s %ld %ld # tty user.line.onsince.idle\n", X name, line, (long)Uentry.ut_time, X stat(buff, &Sb) < 0 ? 0L : (long)Now - Sb.st_atime); X } X X fprintf(F, "%s\n", VERSION); X /* Force updates, to take a nap. */ X (void)fflush(F); X (void)sleep(NAP_TIME); X } X} END_OF_FILE if test 4905 -ne `wc -c <'lwhod.c'`; then echo shar: \"'lwhod.c'\" unpacked with wrong size! fi # end of 'lwhod.c' fi if test -f 'lwholib.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lwholib.c'\" else echo shar: Extracting \"'lwholib.c'\" \(1188 characters\) sed "s/^X//" >'lwholib.c' <<'END_OF_FILE' X/* X** Low-cost Remote Who X** There is no copyright on this package. X** Library routines. X*/ X/* LINTLIBRARY */ X#include "lwho.h" X#if !defined(lint) && !defined(SABER) Xstatic char RCS[] = X "$Header: /nfs/papaya/source/lwho/RCS/lwholib.c,v 1.2 91/03/16 18:29:13 rsalz Exp $"; X#endif /* !defined(lint) && !defined(SABER) */ X X Xchar *progname = "a.out"; X X X/* X** Print error message text. X*/ Xchar * Xstrerror(e) X{ X extern int sys_nerr; X extern char *sys_errlist[]; X static char buff[30]; X X if (e > 0 && e < sys_nerr) X return sys_errlist[e]; X (void)sprintf(buff, "Error code %d", e); X return buff; X} X X Xint * Xxmalloc(i) X unsigned int i; X{ X extern char *malloc(); X char *p; X X if ((p = malloc(i)) == NULL) { X /* Print as normal int so bogus values show up easily. */ X fprintf(stderr, "%s: Can't malloc %d bytes\n", progname, i); X exit(1); X } X return (int *)p; X} X X Xint * Xxrealloc(p, i) X char *p; X unsigned int i; X{ X extern char *realloc(); X X if ((p = realloc(p, i)) == NULL) { X /* Print as normal int so bogus values show up easily. */ X fprintf(stderr, "%s: Can't realloc %d bytes\n", progname, i); X exit(1); X } X return (int *)p; X} END_OF_FILE if test 1188 -ne `wc -c <'lwholib.c'`; then echo shar: \"'lwholib.c'\" unpacked with wrong size! fi # end of 'lwholib.c' fi echo shar: End of archive 1 \(of 1\). cp /dev/null ark1isdone MISSING="" for I in 1 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have the archive. rm -f ark[1-9]isdone else echo You still must unpack the following archives: echo " " ${MISSING} fi exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.