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