v01i020: sysinfo version 4.5, Part01/01

Floyd Davidson floyd at ims.alaska.edu
Sun Apr 7 14:47:38 AEST 1991


Submitted-by: Floyd Davidson <floyd at ims.alaska.edu>
Posting-number: Volume 1, Issue 20
Archive-name: sysinfo4.5/part01

# This is sysinfo version 4.5 for the UnixPc (7300/3B1).  Version
# 2.0 was posted by Lenny Tropiano in early 1988.  Version 4.4 was
# posted to the unix-pc sources group just prior to the new 3b1
# groups starting.  This version is posted to make it available on
# the osu-cis archives, and also to fix a couple bugs.  A floppy
# file system wouldn't be displayed unless it was at least the 4th
# filesystem, and ldaoff would dump core if lda was not running.  A
# few other minor changes, and a few more notes on how to use it
# have been added.
# 
# This is an example of the display:
#
# Mem:  1.636Mb    Swap:  4.352Mb    Load: 0.97 0.70 0.50    Tue Jan 8, 03:06
# [/ 3Mb 11%] [/u 4Mb 43%] [/usr1 4Mb 32%] [/usr2 3Mb 26%] [/usr3 11Mb 27%]
#
# It can have a slightly different format on the second line to get one
# more file system into the display.  It also goes to inverse video for
# any parameter that gets past a set limit.  See the README file and the
# notes in sysinfo.c for other details.
#
# Floyd
#
# ------------------------------ cut here -------------------------------

cat << \SHAR_EOF > 'README'

	This set of programs is a fun hack that evolved from Lenny
Tropiano's sysinfo program dated January 23, 1988, (c) 1988 by ICUS
Software Systems.  Along with Lenny's sysinfo.c was loadavgd.c, a
modification of ldavg.c by Phil Budne (Boston U / DSG).  In September
1989 John Campbell (jdc at naucse) posted a patch to sysinfo.c that
protected labels of other programs from sysinfo, and that basic idea is
incorporated in this version.  Another borrowed idea (how to sum up the
swap space) came from mapmem.c which was originally my Michael J.
Young and ported to the UnixPc by Mike Dopaz.

	Lda is derived from loadavgd.c.  Lda runs as a daemon process
accessing the kernel to get load information.  The information is
averaged and periodically placed in a shared memory segment for access
by other programs, such as sysinfo.  A compile time option allows lda
to open the system window normally used by the Phone Manager and print
the information there.

	Ldaoff is a utility to delete the lda process and remove the
shared memory segment.  If lda (or the original loadavgd) is killed,
for any reason, it will not restart because the shared memory segment
already exists, and lda wants exclusive ownership.  Ldaoff kills the
process that owns the shared memory segment and/or deletes the shared
memory segment.  Lda can then be restarted.

	Clrlabels is a very simple cleanup tool.  It erases the
software label lines for the current window.  Any system used for
program development (and maybe others??  :-) certainly has experienced
an aborted program that left text on the software label lines.  Sysinfo
then refuses to write the label lines.  Run clrlabels to clear the
labels and allow sysinfo to re-write them.

	Sysinfo will run as a daemon process if stdout is a bitmapped
window device (the console) or will print a single report to stdout if
the output goes to a non-bitmapped device (serial port, etc.).  It will
also display a single line of output to stdout if invoked with the
option '-x'.

	Sysinfo displays space available on multiple mounted file
systems.  The space on one line limits how many can be displayed.  Two
options are available for formatting the display line.  One easy to
read with each file system placed in brackets, the other gives more
space for displaying one more file system, by using only a colon (':')
as a separator.

	Sysinfo goes though the entire mount table, but truncates the
display when out of space.  All directories are shortened to their base
name:  /usr/spool/news is listed as ./news.  And no "tmp" file system
is displayed.  Generally that means the last of one or more possible
file systems will not be displayed.  Since the last file system may
well be a mounted floppy disk, and may be the one of most interest, a
floppy files system is always displayed first.  Also the floppy will be
labeled "floppy" instead of showing the file system mount position (eg.
/mnt).

	Each parameter display (except the date/time and a floppy file
system) has a compile time limit which causes the display to be inverse
video.  The "alarm" levels are minimum levels for free memory, free
swap space, and file system space.  The load average limit is a maximum
level.  A separate limit is set for the root file system.  All other
file systems use the same limit.  A floppy disk mounted file system
does not have an alarm limit.  The free memory limit is computed
according to available memory.  All other limits are set in config.h at
compile time.

	Here is an example of the display:

Mem:  1.636Mb    Swap:  4.352Mb    Load: 0.97 0.70 0.50    Tue Jan 8, 03:06
[/ 3Mb 11%] [/u 4Mb 43%] [/usr1 4Mb 32%] [/usr2 3Mb 26%] [/usr3 11Mb 27%]

	This is not a polished program or guaranteed to be useful or
correct.  It is just a hack that changed everytime my perceived needs
change.

floyd at ims.alaska.edu	
Sat Mar 30 15:52:11 1991

Floyd L. Davidson
8347 Richardson Hwy.
Salcha, AK 99714
hm: (907) 488-3294
wk: (907) 456-9700

SHAR_EOF
echo "README"
cat << \SHAR_EOF > 'Makefile'
#
# makefile for sysinfo v4.5
#
#

# uncomment to use gcc
#CC=gcc
#LDFLAGS=-s -shlib
#LLIBS=
#LD=gcc

# uncomment to use cc
CC=cc
LDFLAGS=-s
LLIBS=/lib/crt0s.o /lib/shlib.ifile
LD=ld

CFLAGS=-O
DEST=/etc
LOCAL=/usr/local/bin
DAEMONS=/etc/daemons
SRCS=sysinfo.c lda.c clrlabels.c ldaoff.c
DOCS=README

all:		sysinfo lda clrlabels ldaoff

sysinfo:	sysinfo.c config.h
	$(CC) $(CFLAGS) -c sysinfo.c
	$(LD) $(LDFLAGS) $(LLIBS) sysinfo.o -o sysinfo.bin
	mv sysinfo.bin sysinfo
	chmod 4755 sysinfo

lda:		lda.c config.h
	$(CC) $(CFLAGS) -c lda.c
	$(LD) $(LDFLAGS) $(LLIBS) lda.o -lm -o lda

clrlabels:	clrlabels.c config.h
	$(CC) $(CFLAGS) -c clrlabels.c
	$(LD) $(LDFLAGS) $(LLIBS) clrlabels.o -o clrlabels

ldaoff:		ldaoff.c config.h
	$(CC) $(CFLAGS) -c ldaoff.c
	$(LD) $(LDFLAGS) $(LLIBS) ldaoff.o -o ldaoff

#
# You must to be superuser to do this
#
$(DAEMONS):
	mkdir $(DAEMONS)

install: all $(DAEMONS)
#ldaoff
	-rm -f $(DEST)/ldaoff
	cp ldaoff $(DEST)/ldaoff
	chown root $(DEST)/ldaoff
	chgrp bin  $(DEST)/ldaoff
	chmod 4750 $(DEST)/ldaoff
	-$(DEST)/ldaoff
#clrlabels
	-rm -f $(LOCAL)/clrlabels
	cp clrlabels $(LOCAL)/clrlabels
	chown root $(LOCAL)/clrlabels
	chgrp bin  $(LOCAL)/clrlabels
	chmod 755  $(LOCAL)/clrlabels
	$(LOCAL)/clrlabels
#lda
	-rm -f $(DAEMONS)/lda
	cp lda $(DAEMONS)/lda
	chown root  $(DAEMONS)/lda
	chgrp bin   $(DAEMONS)/lda
	chmod 4750  $(DAEMONS)/lda
	nohup $(DAEMONS)/lda > /dev/null 2>&1 &
#sysinfo
	-zap sysinfo
	-rm -f $(DEST)/sysinfo
	cp sysinfo $(DEST)/sysinfo
	chown root $(DEST)/sysinfo
	chgrp bin  $(DEST)/sysinfo
	chmod 4755 $(DEST)/sysinfo
	$(DEST)/sysinfo

SHAR_EOF
echo "Makefile"
cat << \SHAR_EOF > 'config.h'

/* define NOPMGR to display load averages in phone manager window, or
   undef for sysinfo display only					*/
#undef NOPMGR

/* undefine BRACKET to get more space on the display line		*/
#define BRACKET		/* use brackets in filesystem display	*/

/* SLEEP is the time delay between display updates */
#define	SLEEP	5		/* Sleep time (interval between)	*/

/* alarm points for inverse video display of parameters */
/*				  (swap device blocks are 4k pages)	*/
#define MINPAGE	 400		/* swap device block alarm minimum	*/
#define MAXLDA	 2.0		/* load average alarm maximum		*/
#define MINRFS	 3		/* root device Mb alarm minimum		*/
#define MINUFS	 2		/* user devices Mb alarm minimum	*/



/* things that should never need changing */
#define UNIX	"/unix"		/* kernel			*/
#define KMEM	"/dev/kmem"	/* memory 			*/
#define MNTTAB	"/etc/mnttab"	/* mount table 			*/
#define IPCID	'a'		/* ipc id 			*/


SHAR_EOF
echo "config.h"
cat << \SHAR_EOF > 'lda.c'
/*
 *	lda.c  -- modified loadavgd.c
 *			Sun Feb 25 01:15:12 1990, Floyd L. Davidson
 *		
 *	renamed loadavgd.c by Lenny Tropiano
 *	(lenny at icus.UUCP ICUS Software Systems)
 *
 *	ldavg.c -- compute load averages for System V
 *	Phil Budne @ Boston U / DSG
 *
 *	Forges BSD 4.2 rwhod packets containing system load averages
 *	(#ifdef RWHOD for this, else a shm segment is used, ftok("/unix", 'a'))
 *
 ********************
 * lda.c:
 *
 * Removed things that did not apply to the UnixPc.
 *
 * Modified to put process id number into shared memory segment along
 * with load average numbers.  Ldaoff uses the pid to kill the process
 * and delete the shared memory id and segment.
 *
 * Made it a compile time option whether to show load averages in the
 * system window normally used by the Phone Manager, or to just put
 * values into shared memory segment for other programs to use, eg. sysinfo.
 *
 ******************** 
 */

#include "config.h"			/* local configuration */
#include <sys/sysinfo.h>		/* sysinfo structure */
#include <sys/utsname.h>		/* for uname(2) */
#include <sys/stat.h>			/* for stat(2) */
#include <sys/param.h>			/* for HZ */
#include <stdio.h>
#include <nlist.h>
#include <time.h>
#include <math.h>
#include <utmp.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/window.h>
#include <status.h>

#ifdef NOPMGR
#include <sys/window.h>
struct uwdata avenwin = {
	0,0,288,12,NBORDER,0,0,0
};
#endif

extern struct utmp *getutent();

#define NL_SYSINFO 0

struct nlist nlsym[] = {
        { "sysinfo" },			/* 0 */
        { 0 }
};

key_t aven_key;
int aven_shm;
double *aven_seg;
int fd, memfd;

extern char *sys_errlist[];
extern int errno;

char *unixsys = UNIX;
char *kmem = KMEM;
char *argv0;

void init_nlist(), doit(), getsysinf(), l_lseek();
void r_read(), init_packet(), make_packet();

main(argc, argv)
int  argc;
char *argv[];
{
	switch (fork()) {
	case -1:
		perror("fork");
		exit(1);
	case 0:
		break;
	default:
		exit(0);
	}
	argv0 = argv[0];
	setpgrp();			/* create own pgrp */

#ifdef NOPMGR
	if (freopen("/dev/window", "r+", stdout) == (FILE *) 0) {
		eprintf(ST_OTHER, ST_DISPLAY, (char *) 0, "%s: Can't open load average window:\n%s", argv0, sys_errlist[errno]);
		exit(1);
	}
	ioctl(1, WIOCSETD, &avenwin);
	ioctl(0, WIOCSELECT);
#else
	fclose(stdout);
#endif
	fclose(stdin);
	fclose(stderr);
	init_nlist();			/* get name values, open kmem */
	init_packet();			/* initialize packet prototype */
	doit();				/* never returns */
} /* main */

/*
 * get name values and open kmem
 */
void init_nlist()
{
	nlist(unixsys, nlsym);		/* get system values */

        if(nlsym[NL_SYSINFO].n_value == 0) {
		eprintf(ST_OTHER, ST_DISPLAY, (char *) 0, "%s: Cannot locate `sysinfo' structure", argv0);
                exit(1);
	} /* no value */

	if ((memfd = open(kmem, O_RDONLY)) < 0) {
		eprintf(ST_OTHER, ST_DISPLAY, (char *) 0, "%s: Cannot open kernel memory image:\n%s", argv0, sys_errlist[errno]);
		exit(1);
	} /* could not open kmem */

} /* init_nlist */


#define PERIOD 5			/* sample period (in seconds) */
#define INTERVAL1 60			/* average interval 1 (in seconds) */
#define INTERVAL2 (5*INTERVAL1)		/* average interval 2 (in seconds) */
#define INTERVAL3 (15*INTERVAL1)	/* average interval 3 (in seconds) */
#define PACKINTERVAL 30			/* interval for make_packet */

/*
 * forever loop collecting information
 */
void doit()
{
	struct sysinfo sinf;
	int packt = 0;
	long occ, que, nocc, nque, n, c;
	double avg1, avg2, avg3, new;
	double exp1, exp2, exp3;

	exp1 = exp( - ((double) PERIOD) / INTERVAL1 );
	exp2 = exp( - ((double) PERIOD) / INTERVAL2 );
	exp3 = exp( - ((double) PERIOD) / INTERVAL3 );

	getsysinf(&sinf);		/* prime the pump */
	occ = sinf.runocc;		/* number of samples */
	que = sinf.runque;		/* run queue summation */

	avg1 = avg2 = avg3 = ((double) que) / occ;

/* loop forever */

	for( ; ; ) {
		if( --packt < 0 ) {
			make_packet(avg1, avg2, avg3);
			packt = PACKINTERVAL / PERIOD;
		} /* packet time */

/*		printf("runque: %ld  runocc: %ld\n", que, occ ); /**/

		sleep(PERIOD);
		getsysinf(&sinf);	/* get new info */
		nocc = sinf.runocc;
		nque = sinf.runque;

		n = nocc - occ;		/* get number of times updated */
		if( n <= 0 ) continue;
		c = nque - que - n;	/* get number of runners w/o us */
		if( c < 0 ) c = 0;	/* mumble! */

		new = ((double) c ) / n; /* new instantaneous avg */

		/************************************************/
		/*   The following formula is used to achieve   */
		/*   exponential decay of old measurements:	*/
		/*	avgN = avgN * expN  +  new * (1 - expN)	*/
		/*						*/
		/*   However, the factorized forms below	*/
		/*   require fewer floating point operations.	*/
		/************************************************/

		avg1 = ((avg1 - new) * exp1) + new;
		avg2 = ((avg2 - new) * exp2) + new;
		avg3 = ((avg3 - new) * exp3) + new;

		occ = nocc;
		que = nque;

	} /* for ever */
} /* doit */

/*
 * get system information
 */
void getsysinf(s)
struct sysinfo *s;
{
	l_lseek(memfd, (long)nlsym[NL_SYSINFO].n_value, 0);
	r_read(memfd, (char *)s, sizeof(struct sysinfo));
}

/*
 * lseek with error checking
 */
void l_lseek(fd, offset, whence)
int fd, whence;
long	offset;
{
	if (lseek(fd, offset, whence) == -1) {
		eprintf(ST_OTHER, ST_DISPLAY, (char *) 0, "%s: Error during seek in kernel memory image:\n%s", argv0, sys_errlist[errno]);
		exit(1);
	}
}

/*
 * read with error checking
 */
void r_read (fd, buf, nbytes)
int	fd, nbytes;
char	*buf;
{
	if (read(fd, buf, nbytes) != nbytes) {
		eprintf(ST_OTHER, ST_DISPLAY, (char *) 0, "%s: Error during read from kernel memory image:\n%s", argv0, sys_errlist[errno]);
		exit(1);
	}
}

/*
 *
 */
void init_packet()
{
	if ((aven_key = ftok(UNIX, IPCID)) == (key_t) -1) {
		eprintf(ST_OTHER, ST_DISPLAY, (char *) 0, "%s: Can't find shared memory keyfile:\n%s: %s", argv0, UNIX, sys_errlist[errno]);
		exit(1);
	}
	if ((aven_shm = shmget(aven_key, 4 * sizeof (double), IPC_CREAT|IPC_EXCL|0644)) < 0) {
		eprintf(ST_OTHER, ST_DISPLAY, (char *) 0, "%s: Can't create shared memory segment:\n%s", argv0, sys_errlist[errno]);
		exit(1);
	}
	if ((int) (aven_seg = (double *) shmat(aven_shm, (char *) 0, 0)) == -1) {
		eprintf(ST_OTHER, ST_DISPLAY, (char *) 0, "%s: Can't attach shared memory segment:\n%s", argv0, sys_errlist[errno]);
		if (shmctl(aven_shm, IPC_RMID, (struct shmid_ds *) 0) < 0)
			eprintf(ST_OTHER, ST_DISPLAY, (char *) 0, "%s: Can't remove shared memory segment:\n%s", argv0, sys_errlist[errno]);
		exit(1);
	}
	
}

/*
 *
 */
void make_packet(iavg1, iavg2, iavg3)
double iavg1, iavg2, iavg3;
{
	aven_seg[0] = iavg1;
	aven_seg[1] = iavg2;
	aven_seg[2] = iavg3;
	aven_seg[3] = (double) getpid();
#ifdef NOPMGR
	printf("\rLoad average: %4.2f %4.2f %4.2f", iavg1, iavg2, iavg3);
	fflush(stdout);
#endif
}
SHAR_EOF
echo "lda.c"
cat << \SHAR_EOF > 'ldaoff.c'
/*
 * ldaoff.c
 *
 * kill loadavgd process, remove the shared memory identifier, and
 * destroy the shared memory segment and data structure associated
 * with the loadavgd process
 *
 */

#include "config.h"
#include <stdio.h>
#include <status.h>
#include <sys/shm.h>

key_t aven_key;
int aven_shm;
double *aven_seg;

char *unixsys = UNIX;
char *argv0;

extern char *sys_errlist[];
extern int errno;

main(argc, argv)
int  argc;
char *argv[];
{
	int     shm;			/* shared memory identifier	*/
	int	ldapid;			/* process id of loadavgd	*/
	double *shmseg;			/* shared memory segment	*/

	argv0 = argv[0];


	shm = shmget(ftok(UNIX, IPCID), 4 * sizeof (double), 0);

	if (shm < 0) {
		exit(1);
	}
/*
 * get process id of loadavgd from shared memory segment
 */
	shmseg = (double *) shmat(shm, (char *) 0, SHM_RDONLY);
	ldapid = (int) shmseg[3];
	shmdt(shmseg);

	kill(ldapid,9);		/* kill off loadavgd process */
	
	/* get key */
	if((aven_key = ftok(UNIX, IPCID)) == (key_t) -1) {
		eprintf(ST_OTHER, ST_DISPLAY, (char *) 0,
		    "%s: Can't find shared memory keyfile:\n%s: %s",
		     argv0, UNIX, sys_errlist[errno]);
		exit(1);
	}

	if((aven_shm = shmget(aven_key, 3 * sizeof (double), IPC_CREAT)) < 0) {
		eprintf(ST_OTHER, ST_DISPLAY, (char *) 0,
		    "%s: Can't create shared memory segment:\n%s",
		     argv0, sys_errlist[errno]);
		exit(1);
	}

	if((int)(aven_seg = (double *) shmat(aven_shm, (char *) 0, 0)) == -1) {
		eprintf(ST_OTHER, ST_DISPLAY, (char *) 0,
		     "%s: Can't attach shared memory segment:\n%s",
		      argv0, sys_errlist[errno]);
	} else {
		if (shmctl(aven_shm, IPC_RMID, (struct shmid_ds *) 0) < 0) {
			eprintf(ST_OTHER, ST_DISPLAY, (char *) 0,
			     "%s: Can't remove shared memory segment:\n%s",
			      argv0, sys_errlist[errno]);
			exit(1);
		}
	}

	return(0);
}
SHAR_EOF
echo "ldaoff.c"
cat << \SHAR_EOF > 'clrlabels.c'
/************************************************************************\
**                                                                      **
** Program name:  clrlabels						**
**                                                                      **
** 	Clears the software label lines (the bottom two lines) on	**
**	the UnixPc screen.  The lines are cleared for the current	**
**	window only and nothing at all happens if the terminal is	**
**	not a bitmapped device using windows.				**
**                                                                      **
** Sat Feb 24 16:03:10 1990 --  Floyd L. Davidson			**
**                                                                      **
\************************************************************************/

#include <sys/window.h>

main()
{
	struct utdata utd;		/* Window label data structure	*/
	struct uwdata uw;		/* Window user data structure	*/

	if (!ioctl(1,WIOCGETD, &uw)) {
		utd.ut_text[0] = '\0';	/* clear the label area */
		utd.ut_num = WTXTSLK1;
		ioctl(0, WIOCSETTEXT, &utd);	/* line 1 */
		utd.ut_num = WTXTSLK2;
		ioctl(0, WIOCSETTEXT, &utd);	/* line 2 */
	}

	return(0);
}

SHAR_EOF
echo "clrlabels.c"
cat << \SHAR_EOF > 'sysinfo.c'
/*
    This program is a hack.  It's a fun hack though.  It gets hacked
    everytime some need changes what is useful in the display window.
    Therefore it is not polished, has bugs, and for all I know could
    eat kernels for breakfast.

    Known Bugs:

	Can interfere with unmounting a floppy file system.
    	(It probably could do the same with a hard disk filesystem,
    	but it hasn't been observed.)  The mount table is cleared,
    	the drive light stays lit, and an error message says the
    	floppy drive door is open.  Another dismount command will
    	cure the problem.

   	Sometimes the swap space is shown with a bogus high/low
   	value.  I have no idea why.  In order to prevent bogus
	displays, a low (below alarm) value will only be displayed
	if it is low two time periods in a row.  A value that is
	greater than two times the previous value also has to
	persist through two time periods to be displayed.  Even
	then a bogus value is sometimes displayed.

	The display might do strange things if you mount a hard disk
	filesystem on a directory named "floppy".  I haven't tried.

   	The error messages are useless or worse.  They have been
   	removed.


    Sysinfo is designed for use with multiple mounted file systems.
    The output looks like this:

Mem:  1.636Mb    Swap:  4.352Mb    Load: 0.97 0.70 0.50    Tue Jan 8, 03:06
/ 3Mb 11%:/u 4Mb 43%:/usr1 4Mb 32%:/usr2 3Mb 26%:/usr3 11Mb 27%:/usr4 2Mb 52%


    Unless BRACKET is defined, then it would look like this:

Mem:  1.636Mb    Swap:  4.352Mb    Load: 0.97 0.70 0.50    Tue Jan 8, 03:06
[/ 3Mb 11%] [/u 4Mb 43%] [/usr1 4Mb 32%] [/usr2 3Mb 26%] [/usr3 11Mb 27%]

    Notice there is one less disk drive size shown.  For a system with few
    mounted file systems the bracketed version is easier to read, but the
    brackets take up space.  The display will show as many file systems as
    possible on the one line.

    If a floppy disk is mounted as a file system, it looks like this:

Mem:  2.284Mb    Swap:  5.416Mb    Load: 0.14 0.20 0.21    Tue Jan 8, 06:15
[floppy 230Kb 29%] [/ 3Mb 11%] [/u 4Mb 43%] [/usr1 4Mb 32%] [/usr3 11Mb 27%]

    The floppy file system will always be the first fs displayed.

    Note that file systems with the name "tmp" will not be displayed.  Also
    filesystems mounted on directories other than root will not have the
    full name displayed:  "/usr/spool/news" will be "./news".

    Everything but the date and a floppy file system has an alarm value.
    The minimum free space on the root filesystem and all others are set
    separately.  The load average alarm is a maximum, all others are
    minimums.  If a parameter is below minimum the display is in inverse
    video for that parameter.

    Sysinfo presumes (but does not require) that load averages are made
    available from a companion program (lda).  Lda should be placed in
    /etc/daemons and run as a setuid daemon.

    Sysinfo can be started manually or can be in either a user .profile
    or in /etc/profile or /etc/localprofile.  My preference is in
    localprofile:

	# start sysinfo if it is not running

	if [ "`ps -e | grep sysinfo > /dev/null`" = "0" ]
	then
		/etc/sysinfo
	fi

    ---
    floyd at ims.alaska.edu

*/



/************************************************************************\
**                                                                      **
** Program name: sysinfo.c (System Info)                                **
** Programmer:   Lenny Tropiano            UUCP: ...icus!lenny          **
** Organization: ICUS Software Systems     (c)1988                      **
** Date:         January 23, 1988                                       **
** Version 2.0:  June 27, 1988                                          **
** Version 3.0:  May 10, 1989 -- Floyd L. Davidson 			**
** Version 4.0:  Feb 23, 1990 -- Floyd L. Davidson 			**
** Version 4.1:  May 27, 1990 -- Floyd L. Davidson 			**
** Version 4.2:  Sep 10, 1990 -- Floyd L. Davidson 			**
** Version 4.3:  Sep 17, 1990 -- Floyd L. Davidson 			**
** Version 4.4:  Jan 08, 1991 -- Floyd L. Davidson			**
** Version 4.5:  Mar 26, 1991 -- Floyd L. Davidson			**
**									**
**   4.5 Floppy file system wouldn't show if it was in the		**
**	 first four found in /etc/mnttab.  Free swap space		**
**	 sometimes showed a bogus value that was too high.		**
**       Fixed a bug in the inverse video display of load		**
**       averages so that it does not extend to the date.		**
**									**
**   4.4 Added conditional BRACKET for better display with		**
**	 fewer file systems.						**
**									**
**   4.3 Added time display and alarms to all other displays.		**
**	 Alarms cause highlighted text for that display.		**
**									**
**   4.2 Added swap device info to display and command line		**
**       option '-x' to give a one time status display.			**
**									**
**   4.1 Changed lseek and read errors in filestatus() to skip to	**
**	 the next mount table entry on error instead of calling		**
**	 the terminate function.					**
**									**
**   4.0 Major revision.  Handles the larger mount table in SysV R3.51m **
**	 (Fixdisk 2) and includes the functionality of John Campbell's  **
**	 patch to protect labels for other programs.  Execution from a	**
**	 a non-bitmapped terminal produces a one time status display.	**
**									**
**   3.0 Removed uptime and mail displays, added support for multiple	**
**	 mounted file systems.						**
**									**
**************************************************************************
*
*	  The free disk space percentage is calculated assuming the
*	default inode allocation used by /etc/mkfs.  If you do it
*	different the numbers will be slightly off.
*
*         Any device whose character special file name is /dev/rfp02n
*       is assumed to be a floppy disk.
*
**************************************************************************
**   Notes from Lenny Tropiano's original program:			**
**************************************************************************
**                                                                      **
** Credits:      The idea of displaying the file system information     **
**               came from Scott Hazen Mueller's  newmgr, the replace-  **
**               ment to the smgr.                                      **
**                                                                      **
**************************************************************************
**                                                                      **
** Program use:  Sysinfo is run from /etc/localprofile.  Sysinfo	**
**               checks file systems and memory, then displays          **
**               pertinent information on the bottom of the screen      **
**               where the software labels would normally be.           **
**               Load averages and optionally a mail waiting count      **
**		 are also displayed.				        **
**                                                                      **
**************************************************************************
** Permission is granted to distribute this program by any means as     **
** long as credit is given for my work.     I would also like to see    **
** any modifications or enhancements to this program.                   **
**************************************************************************
** Modifications made for Version 4.0 are Public Domain.		**
\************************************************************************/

#include "config.h"
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/window.h>
#include <sys/filsys.h>
#include <mnttab.h>
#include <sys/param.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <nlist.h>
#include <setjmp.h>
#include <sys/map.h>
#include <time.h>
#include <sys/sysmacros.h>

extern int ioctl(), fork(), exit(), nice(), nlist();
extern int open(), setjmp(), close(), sleep(), sprintf();
extern int printf(), longjmp(), shmat(), shmget();
extern int ftok(), shmdt();
void terminate(), refrsh();
int  line_free();

/*
 *  LINE_1 is the format for the first label line.
 *  FSDATA is the format for each entry on the second label line.
 */

#define LINE_1 "\
Mem: %2.1d.%3.3dMb    Swap: %2.1d.%3.3dMb    Load: %s    %s"
/*
Mem:  1.716Mb    Swap:  4.772Mb    Load: 00.00 00.00 00.00  Sept 17,  04:11
*/

#ifdef BRACKET
#define FSDATA "[%s %d%cb %d%%] "
#else
#define FSDATA "%s %d%cb %d%%:"
#endif

#define STATFS	8	       /* max number filesystems to stat */


#define	NICE	15		/* Niceness value		*/
#define LONGSLEEP (SLEEP + SLEEP)/* When we don't own the labels*/
#define MEGABYTE 1024000
#define KILOBYTE 1024


#define N_SWAPMAP	0
#define N_NSWAP		1
#define N_FREEMEM	2
#define N_MAXMEM	3
#define N_PHYSMEM	4

struct nlist unixsym[] = {
	{"swapmap", 0, 0, 0, 0, 0 },
	{"nswap",   0, 0, 0, 0, 0 },
	{"freemem", 0, 0, 0, 0, 0 },
	{"maxmem",  0, 0, 0, 0, 0 },
	{"physmem", 0, 0, 0, 0, 0 },
	{"",        0, 0, 0, 0, 0 },
	{NULL}
};

struct mountd {
	int	pcntfre;	/* percent free disk space	*/
	int	spcfre;		/* megs free disk space		*/
	char	freesiz;	/* Mb or Kb marker		*/
	char	fsname[11];	/* file system name		*/
	char	dskdat[20];	/* disk data string		*/
};

char	*month[] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
};

char	*day[]   = {
	"Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat",
};

char	*progname;		/* program name			*/
char	buf_text[255];		/* printout string buffer	*/
char	head_buf[5];		/* start of last printout	*/
int	detached = 0;		/* running detached		*/
int	nmnt     = 0;		/* count of mounted file systems*/
int	wd;			/* window descriptor		*/
int	kmem;			/* kmem file descriptor		*/
int	minfmem;		/* low mark for free memory alarm*/
int	fmemalarm;		/* low memory alarm 		*/
int	pagealarm;		/* low space alarm for page dev	*/
int	rootalarm;		/* low space alarm for root fs	*/
int	useralarm;		/* low space alarm for other fs	*/
int	ldavalarm;		/* high loadaverage alarm	*/
struct	utdata utd;		/* Window data structure	*/
struct	filsys fs;		/* Filesystem superblock struct	*/
struct	mountd mt[STATFS + 1];	/* file system info struct	*/
jmp_buf	jmpbuf;			/* save the current environ	*/
void	loadaverage();
void	gettime();
void	filestatus();
void	info_process();
void	open_current_window();
void	highlight();
int	memory();
int	phys_memory();
int	page();

extern long	lseek();
extern int	read();
extern time_t	time();


/************************************************************************/

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

{
	struct uwdata	uw;
	int		xflag = 1;

	if ( argc == 2 && !strcmp(argv[1],"-x") ) {
		xflag = 0;
	}

/* check for a bitmapped windowed device and detach if it is */
	if (xflag && !ioctl(1,WIOCGETD, &uw)) {
		if (fork() != 0) {	/* detach process-info		*/
			exit(0);
		} else {
			detached = 1;
			nice(NICE);	/* Be a little nice	 */
			signal(SIGINT,	SIG_IGN);
			signal(SIGQUIT,	SIG_IGN);
			signal(SIGHUP,	terminate);
			signal(SIGTERM,	terminate);
		}
	}


	progname = *argv;

/*
 * Look up addresses of variables.
 */
	if (nlist(UNIX, unixsym) != 0) {
/*		fprintf(stderr, "\n[%s] %s: no namelist.\n", progname, UNIX); */
		exit(1);
	}
/*
 * Open kernel memory.
 */
	if ((kmem = open(KMEM, O_RDONLY)) < 0) {
/*		fprintf(stderr, "\n[%s] ", progname); */
/*		perror(KMEM); */
		exit(1);
	}

/*
 * set alarm level for free memory
 */
	minfmem = 310000 + ((phys_memory() / 500000) * 90000);
	info_process();
	terminate();
	return(0);	/* never reached */
}


/*
 *  This is the forever loop that does all the work.
 *
 *  get and format info for screen label area
 */
void    info_process()
{
	int		freemem;
	int		freepage;
	char		loadbuf[40];
	char		timebuf[40];
	unsigned int	i, j;
	char		*s1;

	if (detached) {
		signal(SIGWIND, refrsh);	/* trap window change signal */
	}

	while (1) {

		if (detached) {
			setjmp(jmpbuf);
			open_current_window();	/* get current window	*/

			/* loop if we don't own the labels */
			if (!line_free(wd, WTXTSLK1, head_buf)) {
				close(wd);
				sleep(LONGSLEEP);/* wait longer interval... */
				continue;
			}
		}


/* OK... either we own the labels or we are not using them. */

		filestatus();
		loadaverage(loadbuf);

		freemem  = memory();
		freepage = 4 * page();

		gettime(timebuf);

		sprintf(buf_text, LINE_1,
		    ((int)freemem/MEGABYTE),((int)(freemem%MEGABYTE)/KILOBYTE),
		    (freepage / 1000), (freepage % 1000),
		    loadbuf,
		    timebuf);

		if (fmemalarm) {
			highlight(buf_text,buf_text + 12);
		}

		if (pagealarm) {
			highlight(buf_text + 17,buf_text + 30);
		}

		if (ldavalarm) {
			highlight(buf_text + 35, strrchr(buf_text + 51, '.') + 2);
		}

		strncpy(utd.ut_text, buf_text, 79);
		strncpy(head_buf,buf_text,4);
		head_buf[4] = '\0';
		utd.ut_text[79] = '\0';

		if (detached) {
			utd.ut_num = WTXTSLK1;
			ioctl(wd, WIOCSETTEXT, &utd);
		} else {
			printf("\n%s", utd.ut_text);
		}

/*
 * Fill out disk status line.  The entire mount table is scanned
 * and then the buffer is truncated to a size that can be displayed.
 *
 * i is the count of mount table entries.
 * j is the count of filesystems actually stat'ed
 *
 * mt[0] is reserved for a floppy file system
 */

		buf_text[0]     = '\0';

		for (i = 0, j = 0;j <= STATFS, i <= nmnt; ++i) {

			mt[i].dskdat[0] = '\0';

			if (mt[i].fsname[0] != '\0') {
				s1 = strrchr(&mt[i].fsname[1], '/');
			} else {
				s1 = (char *) NULL;
			}

		/* cut it down to the base name */
			if (s1) {
				loadbuf[0] = '.';
				loadbuf[1] = '\0';
				strncat(loadbuf, s1, 10);

			/* delete any "./tmp" name */
				if (strncmp(loadbuf, "./tmp", 5)) {
					strncpy(mt[i].fsname, loadbuf, 10);
					mt[i].fsname[10] = '\0';
#if DEBUG
					fprintf(stderr,"fs=[%s]\n",mt[i].fsname);
#endif
				} else {
					mt[i].fsname[0] = '\0';
				}
			}

			if (!strcmp(mt[i].fsname, "/tmp")) {
				mt[i].fsname[0] = '\0';
			}

		/* copy existing fs name, except "/tmp" */
			if (mt[i].fsname[0] != '\0') {
				sprintf(mt[j].dskdat, FSDATA,
				mt[i].fsname,
				mt[i].spcfre,
				mt[i].freesiz,
				mt[i].pcntfre);

		/* high light user filesystems if below alarm level */
				if ( i > 1 ) {
					if (mt[i].spcfre < MINUFS
						|| mt[i].freesiz == 'K') {
						highlight(mt[j].dskdat,
#ifdef BRACKET
						    strrchr(mt[j].dskdat,']'));
#else
						    strrchr(mt[j].dskdat,':') - 1);
#endif
					}
		/* high light root filesystem if below alarm level */
				} else if ( i == 1 ) {
					if (mt[i].spcfre < MINRFS
						|| mt[i].freesiz == 'K') {
						highlight(mt[j].dskdat,
#ifdef BRACKET
						    strrchr(mt[j].dskdat,']'));
#else
						    strrchr(mt[j].dskdat,':') - 1);
#endif
					}
				}
				strcat(buf_text, mt[j].dskdat);
				++j;
			}
		}

		/* copy only what can be displayed */
		strncpy(utd.ut_text, buf_text, 79);
		utd.ut_text[79] = '\0';

		/* delete truncated data field */
#ifdef BRACKET
		s1 = strrchr(utd.ut_text, ']');
		*(s1 + 1) = '\0';
#else
		s1 = strrchr(utd.ut_text, ':');
		*(s1) = '\0';
#endif

		if ( detached ) {
			utd.ut_num = WTXTSLK2;
			ioctl(wd, WIOCSETTEXT, &utd);
		} else {
			printf("\n%s\n", utd.ut_text);
			terminate();
		}

		close(wd);
		sleep(SLEEP);			/* wait for interval */
	}
}

/*
 * clean up window labels
 */
void     refrsh()
{
	int	window;
	int	dummy = 0;
	char	windev[10];

	signal(SIGWIND, SIG_IGN);
	window = ioctl(0, WIOCGPREV, dummy);
	sprintf(windev, "/dev/w%d", window);

	if ((wd = open(windev, O_RDWR | O_NDELAY)) == -1) {
		wd = 0;
	}

	utd.ut_num = WTXTSLK1;			/* clear the label area     */
	utd.ut_text[0] = '\0';
	ioctl(wd, WIOCSETTEXT, &utd);
	utd.ut_num = WTXTSLK2;
	ioctl(wd, WIOCSETTEXT, &utd);
	close(wd);				/* close previous window    */
	longjmp(jmpbuf, 0);			/* return back to main loop */
}

/*
 * find ourselves
 */
void    open_current_window()
{
	int	window;
	int	dummy = 0;
	char	windev[10];

	window = ioctl(0, WIOCGCURR, dummy);
	sprintf(windev, "/dev/w%d", window);

	if ((wd = open(windev, O_RDWR | O_NDELAY)) == -1) {
		wd = 0;
	}
}

/*
 * exit with grace
 */
void     terminate()
{
	if (detached) {
		utd.ut_num = WTXTSLK1;	/* clear the label area */
		utd.ut_text[0] = '\0';
		ioctl(0, WIOCSETTEXT, &utd);
		utd.ut_num = WTXTSLK2;
		ioctl(0, WIOCSETTEXT, &utd);
	}

	close(kmem);
	exit(0);
}

/*
 * return TRUE if line starts with string in buf
 */
int	line_free(wd, line_num, buf)
int	wd, line_num;
char	*buf;
{
	int	i;
	char	*ptr;

	utd.ut_num = line_num;
	ioctl(wd, WIOCGETTEXT, &utd);

/* Count blanks, if 80 then return 1. */
	for (i = 0, ptr = utd.ut_text; i < 80; ++i, ++ptr) {
		if (*ptr != ' ') {
			break;
		}
	}

	if (i == 80 || *ptr == '\0') {
		return 1;
	}

/* Otherwise see if the string is an expected string... */
	ptr = utd.ut_text;
	while ( (*(buf++) & 0x7f) == (*(ptr++) & 0x7f) ) {
	}

	return (*buf == '\0');
}

/*
 * check mounted file systems for free space.
 */
void    filestatus()
{
	int		mntfd, fsfd;
	struct mnttab	mtable;
	char		rawdevice[13];
	long		s_avail;
	float		s_ratio;
	int		n;

	/* initialize */
	nmnt = 1;

	if ((mntfd = open(MNTTAB, O_RDONLY)) == -1) {
/*		fprintf(stderr, "\n[%s] cannot open %s for read\n", */
/*			progname, MNTTAB); */
		terminate();
	}

	mt[0].fsname[0] = '\0';

	while (read(mntfd, &mtable, sizeof(struct mnttab)) ==
	    sizeof(struct mnttab)) {
		sprintf(rawdevice, "/dev/r%s", mtable.mt_dev);

		if ((fsfd = open(rawdevice, O_RDONLY)) != -1) {
			if (lseek(fsfd, 512, 0) == -1) {
				continue;  /* skip it if we can't find it */
			}

/* retry here if the superblock is locked (mount/umount a filesystem?) */
			for (n = 15; n > 0; n--) {
				if (read(fsfd, &fs, sizeof(struct filsys))
				    == -1) {
					sleep(15);  /* wait, try again */
				} else {
					break;	/* success, stop the loop */
				}
			}

/* if n=0, the loop timed out */
			if (!n) {
				continue;
			}
			close(fsfd);

/*  this could all be done in one statement, ie.:
 *
 * pcntfre = (int)
 * (((float)fs.s_tfree/((float)(fs.s_fsize-3)-((float)fs.s_fsize/64)))+0.005)*100
 *
 *    but it is too confusing. So here it is one step at a time.
 */

			/* adjust filesystem size for boot, superblock,	*/
			/*	and inode space				*/
			s_avail = fs.s_fsize - 3 - (fs.s_fsize / 64);

			/* ratio of free space to space available	*/
			s_ratio = (float) fs.s_tfree / (float) s_avail;

			/* add 1/2 percent to make it round up, and	*/
			/*	times 100 to convert to percent		*/
			mt[nmnt].pcntfre = (int) ((s_ratio + 0.005) * 100.0);

/* A floppy disk gets treated differently:

	Always show size in Kb instead of Mb.

	Always goes into mt[0] structure.

*/
			if (strncmp(rawdevice, "/dev/rfp02", 10)) {

				/* not a floppy */
				mt[nmnt].spcfre = (int) (fs.s_tfree / 1024);

				/* show size of free space in Kb if Mb would be 0 */
				if ( !mt[nmnt].spcfre ) {
					mt[nmnt].spcfre = (int) (fs.s_tfree);
					mt[nmnt].freesiz = 'K';
				} else {
					mt[nmnt].freesiz = 'M';
				}

				strncpy(mt[nmnt].fsname, mtable.mt_filsys, 10);
				mt[nmnt].fsname[10] = '\0';

			} else {
				/* floppy file system			*/
				/* put data in struct mt[0]		*/
				/* change device name to "floppy"	*/

				mt[0].pcntfre = mt[nmnt].pcntfre;
				mt[0].spcfre = (int) (fs.s_tfree);
				mt[0].freesiz = 'K';
				mt[nmnt].fsname[0] = '\0';
				strcpy(mt[0].fsname,"floppy");
			}

			if ( ++nmnt > STATFS ) {
				break;
			}

		}	/* cannot open device */
	}
	close(mntfd);
}

/*
 * get current time
 */
void	gettime(tbuf)
char	*tbuf;
{
	struct tm	*tmbuf;
	time_t		tm;

	time(&tm);
	tmbuf = localtime(&tm);
	sprintf(tbuf,"%s %s %d, %02d:%02d",
		day[tmbuf->tm_wday],
		month[tmbuf->tm_mon],
		tmbuf->tm_mday,
		tmbuf->tm_hour,
		tmbuf->tm_min);
}

/*
 * get load averages from shared memory
 */
void    loadaverage(lbuf)
char   *lbuf;
{
	static int	shm;		/* shared memory identifier	*/
	double 		*shmseg;	/* shared memory segment	*/

	shmseg = (double *)shmat(shm,(char *)0,SHM_RDONLY);

	if ( (int) shmseg == -1) {
		shm = shmget(ftok(UNIX, IPCID),4 * sizeof(double), 0);
		shmseg = (double *)shmat(shm,(char *)0,SHM_RDONLY);
	}

	if ( (int) shmseg == -1) {
		sprintf(lbuf, "%s", "unavailable");
	} else {
		sprintf(lbuf, "%4.2f %4.2f %4.2f", shmseg[0], shmseg[1], shmseg[2]);

		if ( MAXLDA < shmseg[0] ) {
			ldavalarm = 1;
		} else {
		     	ldavalarm = 0;
		}
	}

	shmdt(shmseg);
}

/*
 * get freemem from kernal
 *
 */
int    memory()
{
	int     mem;

/*
 * Read variables.
 */
	lseek(kmem, (long) unixsym[N_FREEMEM].n_value, 0);
	read(kmem, (char *) &mem, sizeof(int));

	mem = ctob(mem);
	if (mem < minfmem) {
		fmemalarm = 1;
	} else {
		fmemalarm = 0;
	}
	return (mem);
}

/*
 * get physmem from kernal
 *
 */
int    phys_memory()
{
	int     mem;

/*
 * Read variables.
 */
	lseek(kmem, (long) unixsym[N_PHYSMEM].n_value, 0);
	read(kmem, (char *) &mem, sizeof(int));

	return (ctob(mem));
}


/*
 * get swap device free block count
 *
 * loops through a copy of the swap device allocation map
 * counting free blocks.  Returns the total number of free blocks.
 */
int page()
{
	int		total_free = 0;
	struct mapent	*smap;
	struct mapent	*next_smap;
	struct mapent	sentry;
	struct map	sheader;
	static int	last_size = 50000;

	smap = (struct mapent *) unixsym[N_SWAPMAP].n_value;

	if (lseek(kmem, smap, 0) != (long)smap) {
	    return(0);
	}

	if (read(kmem, &sheader, sizeof(struct map *)) != sizeof(struct map *)) {
		return(0);
	}

	for (next_smap=smap+1; next_smap != sheader.m_limit; ++next_smap) {

	    if (lseek(kmem, next_smap, 0) != (long)next_smap) {
		return(0);
	    }

	    if (read(kmem,&sentry,sizeof(struct mapent)) != sizeof(struct mapent)) {
		return(0);
	    }

	    total_free += sentry.m_size;
	}


/* There is a bug here somewhere.  Occassional bogus figures
   (very low or very high numbers) show up.  A stupid hack
   that does not fix it or even stop it, but does reduce the
   number of times it is seen, is implemented.  Low values
   are deleted, by repeating the last size the first time it
   is lower than minimum or more than two times the previous
   value.  If the condition exist twice in a row, the value
   is displayed (and sometimes it is bogus). */

	pagealarm  = total_free;	/* temporary save */

	if (total_free < MINPAGE) {

		if (last_size >= MINPAGE ) {
			total_free = last_size;
			last_size  = pagealarm;
			pagealarm  = 0;
		} else {
/*			pagealarm  = 1;       pagealarm is already != 0 */
			last_size  = total_free;
		}

	} else if (total_free > (last_size + last_size)) {
			total_free = last_size;
			last_size  = pagealarm;
			pagealarm  = 0;

	} else {
		pagealarm = 0;
		last_size = total_free;
	}


	return total_free;
}

/*
 * set the high bit on characters of an array.
 */
void highlight(start,end)
char *start, *end;
{
	if (!start || start >= end) {
		return;
	}

	for (;start <= end && *start; ++start) {
		*start |= 0x80;
	}
}
SHAR_EOF
echo "sysinfo.c"
echo "Finished"

exit 0
-- 
David H. Brierley
Home: dave at galaxia.newport.ri.us; Work: dhb at quahog.ssd.ray.com
Send comp.sources.3b1 submissions to comp-sources-3b1 at galaxia.newport.ri.us
%% Can I be excused, my brain is full. **



More information about the Comp.sources.3b1 mailing list