Updated vmpic (includes 4.2 networking)
Stephen Hemminger
steveh at hammer.UUCP
Thu Dec 8 02:56:47 AEST 1983
---
Here is an updated version of vmpic, which includes the functions of
netstat. It runs on 4.1c (all we have so far), but should also work on
4.2bsd.
You still need the Maryland window library (winlib), to compile it.
---
/* $Header: /usr/src/local/RCS/vmpic.c,v 1.1 83/12/07 08:54:23 steveh Exp $ */
/*
* vmpic - vmstat using full screen
*
% cc -O -s -o vmpic vmpic.c -lwinlib -ljobs -ltermlib
%i cc -O -s -o /usr/local/vmpic vmpic.c -lwinlib -ljobs -ltermlib
*
* Joe Pallas 5-Mar-1983
* Modified: 5-Mar-1983 ACT for window library, load average
* 7-Mar-1983 ACT for multiple windows
* 8-Mar-1983 ACT highlight system %cpu if >= 40.0
* 18-Mar-1983 ACT added ^L check, minor changes
* 19-Mar-1983 Andrew & Patrick hacked for vmpic login...
* (a REALLY horrible hack!)
* 19-Mar-1983 ACT Cleaned up above mod a bit
* 2-May-1983 ACT Cleaned up yet more (fix from JIP)
* 12-Aug-1983 (sch) pull disk names out of memory.
*/
#include <stdio.h>
#include <ctype.h>
#include <sys/param.h>
#include <sys/vm.h>
#include <sys/dk.h>
#include <sys/stat.h>
#include <nlist.h>
#include <sys/buf.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/mbuf.h>
#include <local/window.h>
#include <vaxuba/ubavar.h>
#include <vaxmba/mbavar.h>
#define LOOPSIZ (LOOPPAGES / CLSIZE)
/* This one is an /etc/config option; not available in h files */
struct nlist nml[] = {
#define X_CPTIME 0
{"_cp_time"},
#define X_RATE 1
{"_rate"},
#define X_TOTAL 2
{"_total"},
#define X_DEFICIT 3
{"_deficit"},
#define X_FORKSTAT 4
{"_forkstat"},
#define X_SUM 5
{"_sum"},
#define X_FIRSTFREE 6
{"_firstfree"},
#define X_MAXFREE 7
{"_maxfree"},
#define X_BOOTIME 8
{"_boottime"},
#define X_DK_XFER 9
{"_dk_xfer"},
#define X_TK_NIN 10
{"_tk_nin"},
#define X_TK_NOUT 11
{"_tk_nout"},
#define X_AVENRUN 12
{"_avenrun"},
#define X_LOTSFREE 13
{"_lotsfree"},
#define X_DESFREE 14
{"_desfree"},
#define X_MINFREE 15
{"_minfree"},
#define X_HZ 16
{"_hz"},
#define X_LP_NOUT 17
{"_lp_nout"},
#define X_MBDINIT 18
{"_mbdinit" },
#define X_UBDINIT 19
{"_ubdinit" },
#define X_MBSTAT 20
{"_mbstat"},
#define X_IFNET 21
{ "_ifnet"},
0
};
char *ctime (), *index ();
double stat1 ();
char hostname[32],
Status_msg[100],
buf[100]; /* general use string buffer */
/* values read from the kernel */
int firstfree, maxfree, minfree, lotsfree, desfree, deficit, hz, kmem;
time_t bootime;
double etime;
double avenrun[3];
struct mbstat mbstat;
/* Net drive stuff */
off_t ifnetaddr;
struct netstat {
int n_ipackets;
int n_ierrors;
int n_opackets;
int n_oerrors;
int n_collisions;
};
/* Disk drive stuff */
static char dr_name[DK_NDRIVE][5];
/* Grouping of stuff so can use structure assignment */
struct {
long time[CPUSTATES];
long xfer[DK_NDRIVE];
struct vmmeter Rate;
struct vmtotal Total;
struct vmmeter Sum;
/* struct forkstat Forkstat; -- not currently used */
long tk_nin;
long tk_nout;
int lp_nout;
struct netstat Net;
} s, s1; /* s1 == the previous s */
/* This structure is used to specify what to read from the kernel. k_item is
the address to read into, k_size is the number of bytes, and k_index is
the index into the namelist "nml" which determines the kernel address. */
struct kvalues {
caddr_t k_item;
off_t k_size;
int k_index;
};
struct kvalues LoopTable[] = {
(caddr_t) s.time, sizeof s.time, X_CPTIME,
(caddr_t) s.xfer, sizeof s.xfer, X_DK_XFER,
(caddr_t) &s.Rate, sizeof s.Rate, X_RATE,
(caddr_t) &s.Sum, sizeof s.Sum, X_SUM,
/* (caddr_t) &s.Forkstat, sizeof s.Forkstat, X_FORKSTAT, */
(caddr_t) &s.Total, sizeof s.Total, X_TOTAL,
(caddr_t) &s.tk_nin, sizeof s.tk_nin, X_TK_NIN,
(caddr_t) &s.tk_nout, sizeof s.tk_nout, X_TK_NOUT,
(caddr_t) &deficit, sizeof deficit, X_DEFICIT,
(caddr_t) avenrun, sizeof avenrun, X_AVENRUN,
(caddr_t) &s.lp_nout, sizeof s.lp_nout, X_LP_NOUT,
(caddr_t) &mbstat, sizeof mbstat, X_MBSTAT,
0, 0, 0
};
struct kvalues InitTable[] = {
(caddr_t) &firstfree, sizeof firstfree, X_FIRSTFREE,
(caddr_t) &maxfree, sizeof maxfree, X_MAXFREE,
(caddr_t) &bootime, sizeof bootime, X_BOOTIME,
(caddr_t) &lotsfree, sizeof lotsfree, X_LOTSFREE,
(caddr_t) &desfree, sizeof desfree, X_DESFREE,
(caddr_t) &minfree, sizeof minfree, X_MINFREE,
(caddr_t) &hz, sizeof hz, X_HZ,
(caddr_t) &ifnetaddr, sizeof ifnetaddr, X_IFNET,
0, 0, 0
};
/* This determines what windows to display */
/* The 4 ints specify the position & size; the two strings are optional, and
are the label and initial contents for the window */
struct initscreen {
int i_x, i_y, i_xe, i_ye;
char *i_lbl, *i_str;
} InitScreen[] = {
#define W_TIME (wins[0])
/*0*/ 18, 0, 38, 1, 0,
Status_msg,
#define W_PROC (wins[1])
/*1*/ 0, 1, 15, 7, "Processes",
"Runnable\nDisk Wait\nPage Wait\nSwapped\nSleeping",
#define W_BOOTIME (wins[2])
/*2*/ 18, 1, 39, 1, 0,
"Last reboot @ ",
#define W_DISK (wins[3])
/*3*/ 20, 2, 12, 7, "Disk Xfers",
0,
#define W_FAULTS (wins[4])
/*4*/ 33, 3, 18, 6, "Faults",
"Interrupts\nDZ Pseudo\nSyscalls\nContext Sw",
#define W_PAGE (wins[5])
/*5*/ 0, 8, 19, 10, "Paging",
"Reclaims\nPage Ins\nPage Outs\nPages Freed\nDeficit\nScan Rate\nSwap Ins\nSwap Outs",
#define W_CPU (wins[6])
/*6*/ 20, 9, 21, 6, "CPU Usage",
"User (normal)\t %\nUser (nice)\t %\nSystem\t\t %\nIdle\t\t %",
#define W_MEM (wins[7])
/*7*/ 20, 15, 22, 5, "Memory",
"Active Virtual pages\nActive Real\t pages\nFree\t\t pages",
#define W_TERM (wins[8])
/*8*/ 0, 18, 15, 5, "Terminals",
"Input\nOutput\nLP Out",
#define W_IFNET (wins[9])
/*9*/ 43, 9, 20, 7, "Network",
"In packets\nIn errors\nOut packets\nOut errors\nCollisions",
#define W_MBUF (wins[10])
/*10*/ 43, 16, 26, 4, "Net buffers",
"Mbufs used, free\nPages used, free",
#define W_UP (wins[11])
/*11*/ 22, 20, 20, 1, 0,
"up",
#define W_LOAD (wins[12])
/*12*/ 22, 21, 34, 1, 0,
"Load average:",
#define W_MAIL (wins[13])
/*13*/ 22, 22, 4, 1, 0,
"$Mail",
};
#define NWINS (sizeof InitScreen/sizeof *InitScreen)
Win *wins[NWINS];
main (argc, argv)
int argc;
char **argv;
{
register int i,j;
register struct initscreen *ip;
register struct vmmeter *v;
time_t now, mtime;
int days, hrs, mins;
int iter, sleeptime, nintv;
char *str;
char mail[300];
double f;
struct stat st;
int NewMail = 0;
int leave();
/* Read the namelist */
nlist ("/vmunix", nml);
if (nml[0].n_type == 0) {
fprintf (stderr, "No /vmunix namelist\n");
exit (1);
}
if (nml[X_LP_NOUT].n_type == 0)
nml[X_LP_NOUT].n_value = 0;
/* Open /dev/kmem */
kmem = open ("/dev/kmem", 0);
if (kmem < 0) {
fprintf (stderr, "Can't open /dev/kmem\n");
exit (1);
}
if (argc < 2) {
iter = 1;
sleeptime = 0;
/* if are name -mpic then we have been called by login */
if (**argv == '-') {
iter = 0;
sleeptime = 5;
}
}
else if (argc == 2)
iter = 0, sleeptime = atoi (argv[1]);
else if (argc >= 3)
iter = atoi (argv[2]), sleeptime = atoi (argv[1]);
if (ReadKernel (InitTable))
exit (1);
read_names();
time (&now);
gethostname(hostname, sizeof hostname);
if(islower(hostname[0]))
hostname[0] = toupper(hostname[0]);
sprintf(Status_msg, "$%s Status", hostname);
nintv = now - bootime;
if (nintv <= 0 || nintv > 60*60*24*365*10) {
fprintf (stderr,
"Time makes no sense: namelist must be wrong\n");
exit (1);
}
/* Initialize windows */
if (Winit (0, 0)) {
fprintf (stderr,
"Sorry, this terminal doesn't support windows\n");
exit (1);
}
for (ip = InitScreen; ip < &InitScreen[NWINS]; ip++) {
register Win *w;
w = Wopen (0, ip->i_x, ip->i_y, ip->i_xe, ip->i_ye, 0, 0);
if (w == 0) {
Wcleanup ();
fprintf (stderr, "Sorry, your screen is too small\n");
exit (1);
}
wins[ip - InitScreen] = w;
Woncursor (w, 0);
Wnewline (w, 1);
Wwrap (w, 0);
if (ip -> i_ye > 1) {
Wframe (w);
if (ip -> i_lbl)
Wlabel (w, ip -> i_lbl, 0, 1);
}
if (str = ip -> i_str) {
if (*str == '$') {/* Use inverse video */
Wsetmode (w, WINVERSE);
str++;
}
Wputs (str, w);
Wsetmode (w, 0);
}
}
WSetRealCursor = 1;
WRCurRow = 0;
WRCurCol = 0;
sprintf (mail, "/usr/spool/mail/%s", getenv ("USER"));
if (stat (mail, &st) >= 0)
mtime = st.st_mtime;
sprintf (buf, "%.24s", ctime (&bootime));
Wputs (buf, W_BOOTIME);
Whide (W_MAIL);
/* Label Disk windows */
for(i=0; i<DK_NDRIVE; i++) {
WAcursor(W_DISK, i, 0);
Wputs(dr_name[i], W_DISK);
}
/* This is the main loop. It should be a for(;;) but then everything
would be way over on the right. */
loop:
time (&now);
/* Read the kernel stuff */
if (ReadKernel (LoopTable))
Wexit (1);
/* Put info into windows */
WAcursor (W_TIME, 0, 14);
sprintf (buf, "%.24s", ctime (&now));
Wputs (buf, W_TIME);
etime = 0.;
for (i = 0; i < CPUSTATES; i++)
etime += s.time[i] - s1.time[i];
if (etime == 0.)
etime = 1.;
#define WPR(w,y,x,fmt,n) WAcursor (w, y, x), \
sprintf (buf, fmt, n), \
Wputs (buf, w)
/* Processes */
WPR (W_PROC, 0, 10, "%3d", s.Total.t_rq);/* Running */
WPR (W_PROC, 1, 10, "%3d", s.Total.t_dw);/* Disk Wait */
WPR (W_PROC, 2, 10, "%3d", s.Total.t_pw);/* Page Wait */
WPR (W_PROC, 3, 10, "%3d", s.Total.t_sw);/* Swapped */
WPR (W_PROC, 4, 10, "%3d", s.Total.t_sl);/* Sleeping */
/* Virtual memory */
WPR (W_MEM, 0, 15, "%5d", s.Total.t_avm / 2);/* AVM */
WPR (W_MEM, 1, 15, "%5d", s.Total.t_rm / 2);/* ARM */
WAcursor (W_MEM, 2, 15);
sprintf (buf, "%5d", i = s.Total.t_free / 2);
if (i < minfree)
Wsetmode (W_MEM, WBLINK|WINVERSE);
else if (i < desfree)
Wsetmode (W_MEM, WINVERSE);
Wputs (buf, W_MEM); /* Free space */
Wsetmode (W_MEM, 0);
v = nintv != 1 ? &s.Sum : &s.Rate;
/* Paging */
WPR (W_PAGE, 0, 12, "%5d",/* Reclaims */
(v -> v_pgrec - (v -> v_xsfrec + v -> v_xifrec)) / nintv);
WPR (W_PAGE, 1, 12, "%5d", v -> v_pgin / nintv);/* Pageins */
WPR (W_PAGE, 2, 12, "%5d", v -> v_pgout / nintv);/* Pageouts */
WPR (W_PAGE, 3, 12, "%5d", v -> v_dfree / nintv);/* Freed per sec */
WPR (W_PAGE, 4, 12, "%5d", deficit / 2);/* Shortfall */
f = (60.0 * v -> v_scan) / (LOOPSIZ * nintv);
if (f >= 0.01)
Wsetmode (W_PAGE, WINVERSE);
WPR (W_PAGE, 5, 12, "%5.2f", f);
Wsetmode (W_PAGE, 0);
WPR (W_PAGE, 6, 12, "%5d", v -> v_swpin / nintv);/* Swap ins */
WPR (W_PAGE, 7, 12, "%5d", v -> v_swpout / nintv);/* Swap outs */
etime /= 60.;
/* Disk transfers */
for(i = 0; i < DK_NDRIVE; i++)
WPR (W_DISK, i, 6, "%4.0f", (s.xfer[i]-s1.xfer[i])/etime);
/* Faults */
WPR (W_FAULTS, 0, 12, "%4d", v -> v_intr / nintv - hz);/* Interrupts */
WPR (W_FAULTS, 1, 12, "%4d", v -> v_pdma / nintv);/* DZ pseudo intr */
WPR (W_FAULTS, 2, 12, "%4d", v -> v_syscall / nintv);/* System calls */
WPR (W_FAULTS, 3, 12, "%4d", v -> v_swtch / nintv);/* Context sws */
/* CPU usage */
WPR (W_CPU, 0, 15, "%3.0f", stat1 (0));/* User mode (normal) */
WPR (W_CPU, 1, 15, "%3.0f", stat1 (1));/* User mode (nice) */
f = stat1 (2);
if (f >= 40.)
Wsetmode (W_CPU, WINVERSE);
WPR (W_CPU, 2, 15, "%3.0f", f);/* System */
Wsetmode (W_CPU, 0);
WPR (W_CPU, 3, 15, "%3.0f", stat1 (3));/* Idle */
/* Terminal I/O */
WPR (W_TERM, 0, 8, "%5.0f", (s.tk_nin-s1.tk_nin)/etime);/* In */
WPR (W_TERM, 1, 8, "%5.0f", (s.tk_nout-s1.tk_nout)/etime);/* Out */
WPR (W_TERM, 2, 8, "%5.0f", (s.lp_nout-s1.lp_nout)/etime);/* LP Out */
/* Network I/O */
netstat(&s.Net);
WPR (W_IFNET, 0, 16, "%4d", s.Net.n_ipackets-s1.Net.n_ipackets);
i = s.Net.n_ierrors-s1.Net.n_ierrors;
if( i > 10)
Wsetmode(W_IFNET, WINVERSE);
WPR (W_IFNET, 1, 16, "%4d", i);
Wsetmode(W_IFNET, 0);
WPR (W_IFNET, 2, 16, "%4d", s.Net.n_opackets-s1.Net.n_opackets);
i = s.Net.n_oerrors-s1.Net.n_oerrors;
if( i > 10)
Wsetmode(W_IFNET, WINVERSE);
WPR (W_IFNET, 3, 16, "%4d", i);
Wsetmode(W_IFNET, 0);
i = s.Net.n_collisions-s1.Net.n_collisions;
if( i > 10)
Wsetmode(W_IFNET, WINVERSE);
WPR (W_IFNET, 4, 16, "%4d", i);
Wsetmode(W_IFNET, 0);
/* Network buffers */
WPR (W_MBUF, 0, 5, "%4d", mbstat.m_mbufs - mbstat.m_mbfree);
if (mbstat.m_mbfree < 10)
Wsetmode(W_MBUF, WINVERSE);
WPR (W_MBUF, 0, 15, "%4d", mbstat.m_mbfree);
Wsetmode(W_MBUF, 0);
WPR (W_MBUF, 1, 5, "%4d", mbstat.m_clusters - mbstat.m_clfree);
if (mbstat.m_clfree < 4)
Wsetmode(W_MBUF, WINVERSE);
WPR (W_MBUF, 1, 15, "%4d", mbstat.m_clfree);
Wsetmode(W_MBUF, 0);
/* Load average */
WAcursor (W_LOAD, 0, 14);
for (i = 0; i < 3; i++) {
sprintf (buf, "%5.2f", avenrun[i]);
if (avenrun[i] > 6.)
Wsetmode (W_LOAD, WINVERSE);
Wputs (buf, W_LOAD);
Wsetmode (W_LOAD, 0);
if (i != 2)
Wputc (' ', W_LOAD);
}
/* Uptime */
WAcursor (W_UP, 0, 3);
Wclear (W_UP, 0);
i = now - bootime;
days = i / (60*60*24);
i -= days * 60*60*24;
hrs = i / (60*60);
i -= hrs * 60*60;
mins = i / 60;
i = 0;
if (days) {
sprintf (buf, "%d day%s", days, days == 1 ? "" : "s");
Wputs (buf, W_UP);
i = 1;
}
if (hrs && mins)
sprintf (buf, "%s%2d:%02d", i ? ", " : "", hrs, mins);
else {
buf[0] = 0;
if (hrs > 0)
sprintf (buf, "%s%d hr%s", i ? ", " : "",
hrs, hrs == 1 ? "" : "s");
if (mins > 0)
sprintf (buf, "%s%d min%s", i ? ", " : "",
mins, mins == 1 ? "" : "s");
}
Wputs (buf, W_UP);
/* Mail */
if (stat (mail, &st) >= 0 && st.st_size) {
if (st.st_mtime < st.st_atime)
NewMail = 0;
else if (st.st_mtime > mtime)
NewMail++, mtime = st.st_mtime;
}
else
NewMail = 0;
if (NewMail)
Wunhide (W_MAIL);
else
Whide (W_MAIL);
/* Update screen and loop if desired */
Wrefresh (0);
if (InputPending) {
char c;
while (InputPending) {
read (0, &c, 1);
if (c == 014)
ScreenGarbaged++;
ioctl (0, FIONREAD, &InputPending);
}
Wrefresh (0);
}
if (--iter == 0)
Wexit (0);
nintv = 1;
s1 = s;
if (sleeptime)
sleep (sleeptime);
goto loop;
}
/* Return the %cpu for s.time[row] */
double
stat1 (row)
{
double t;
register i;
t = 0.;
for (i = 0; i < CPUSTATES; i++)
t += s.time[i] - s1.time[i];
if (t == 0.)
t = 1.;
return (s.time[row] - s1.time[row]) * 100. / t;
}
/* Read kernel values according to table starting at k */
ReadKernel (k)
register struct kvalues *k;
{
while (k -> k_item) {
lseek (kmem, (long) nml[k -> k_index].n_value, 0);
if (read (kmem, (char *) k->k_item, k->k_size) != k->k_size) {
fprintf (stderr, "Error reading kmem at %d\r\n",
nml[k -> k_index].n_value);
return -1;
}
k++;
}
return 0;
}
/* The standard sleep creates problems because it uses longjmp() on an alarm
to return. This one uses a flag instead, so is much safer. */
/* @(#)sleep.c 4.1 (Berkeley) 12/21/80 */
static alarmed;
sleep(n)
unsigned n;
{
int sleepx();
unsigned altime;
int (*alsig)() = SIG_DFL;
int (*sigset())();
if (n==0)
return;
altime = alarm(1000); /* time to maneuver */
if (altime) {
if (altime > n)
altime -= n;
else {
n = altime;
altime = 1;
}
}
alsig = sigset(SIGALRM, sleepx);
alarmed = 0;
alarm(n);
while (!alarmed)
pause();
sigset (SIGALRM, alsig);
alarm (altime);
}
static
sleepx()
{
alarmed++;
}
/*
* Read the drive names out of kmem.
*/
#define steal(where, var) lseek(kmem, where, 0); read(kmem, &var, sizeof var)
read_names()
{
struct mba_device mdev;
register struct mba_device *mp;
struct mba_driver mdrv;
short two_char;
register char *cp = (char *) &two_char;
struct uba_device udev, *up;
struct uba_driver udrv;
mp = (struct mba_device *) nml[X_MBDINIT].n_value;
up = (struct uba_device *) nml[X_UBDINIT].n_value;
if (up == 0) {
fprintf(stderr, "vmstat: Disk init info not in namelist\n");
exit(1);
}
if (mp) for (;;) {
steal(mp++, mdev);
if (mdev.mi_driver == 0)
break;
if (mdev.mi_dk < 0 || mdev.mi_alive == 0)
continue;
steal(mdev.mi_driver, mdrv);
steal(mdrv.md_dname, two_char);
sprintf(dr_name[mdev.mi_dk], "%c%c%d",
cp[0], cp[1], mdev.mi_unit);
}
for (;;) {
steal(up++, udev);
if (udev.ui_driver == 0)
break;
if (udev.ui_dk < 0 || udev.ui_alive == 0)
continue;
steal(udev.ui_driver, udrv);
steal(udrv.ud_dname, two_char);
sprintf(dr_name[udev.ui_dk], "%c%c",
cp[0], cp[1], udev.ui_unit);
}
}
netstat(np)
register struct netstat *np;
{
struct ifnet ifnet;
off_t addr = ifnetaddr;
bzero((char *) np, sizeof (struct netstat));
while(addr) {
steal(addr, ifnet);
np->n_ipackets += ifnet.if_ipackets;
np->n_ierrors += ifnet.if_ierrors;
np->n_opackets += ifnet.if_opackets;
np->n_ierrors += ifnet.if_ierrors;
np->n_collisions += ifnet.if_collisions;
addr = (off_t) ifnet.if_next;
}
}
More information about the Comp.sources.unix
mailing list