From pa.dec.com!decwrl!uunet!sparky!kent Tue Jul 16 09:11:17 PDT 1991
Article: 2485 of comp.sources.misc
Newsgroups: comp.sources.misc
Path: pa.dec.com!decwrl!uunet!sparky!kent
From: Jussi Maki <jmaki@vipunen.hut.fi>
Subject:  v20i094:  aixmon - AIX3.1 system monitor v1.04, Part01/01
Message-ID: <1991Jul12.041824.13055@sparky.IMD.Sterling.COM>
X-Md4-Signature: 95afd991dddf576766ff1ab74ab19eea
Sender: kent@sparky.IMD.Sterling.COM (Kent Landfield)
Organization: Helsinki University of Technology, Finland
Date: Fri, 12 Jul 1991 04:18:24 GMT
Approved: kent@sparky.imd.sterling.com
Lines: 1891

Submitted-by: Jussi Maki <jmaki@vipunen.hut.fi>
Posting-number: Volume 20, Issue 94
Archive-name: aixmon/part01
Environment: AIX3.1

Monitor program is an AIX 3.1 System performance monitor.
Monitor program can be used to display system statistics
of various short time performance values. Version 1.04 of monitor
supports now also top-like display of top cpu processes.

This version of monitor program can also be get from
ftp-server ftp.funet.fi (128.214.6.100) in
file pub/unix/AIX/RS6000/monitor-1.04.tar.Z

Jussi Maki                                 | Internet: jmaki@vipunen.hut.fi
Helsinki University of Technology, Finland | Bitnet:   jmaki@fingate.bitnet
Computing Centre                           | Voice:    + 358 - 0 - 451 4317
Systems Support Division                   | Telefax:  + 358 - 0 - 464 788
----------------------------- cut here -----------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	0README
#	MANIFEST
#	Makefile
#	README
#	README.loadavgd
#	getloadavg.3
#	getloadavg.c
#	getloadavg.h
#	getproc.3
#	getproc.h
#	launch_loadavgd.c
#	loadavg.c
#	loadavgd.c
#	monitor.1
#	monitor.c
#	testgetloadavg.c
#	top.c
#	top.h
#	uptime.c
# This archive created: Wed Jul 10 15:17:03 1991
export PATH; PATH=/bin:$PATH
if test -f '0README'
then
	echo shar: will not over-write existing file "'0README'"
else
cat << \SHAR_EOF > '0README'
Monitor program is an AIX 3.1 System performance monitor.
Monitor program can be used to display system statistics
of various short time performance values. Version 1.04 of monitor
supports now also top-like display of top cpu processes.

This version of monitor program can also be get from
ftp-server ftp.funet.fi in file pub/unix/AIX/RS6000/monitor-1.04.tar.Z

Jussi Maki                                 | Internet: jmaki@vipunen.hut.fi
Helsinki University of Technology, Finland | Bitnet:   jmaki@fingate.bitnet
Computing Centre                           | Voice:    + 358 - 0 - 451 4317
Systems Support Division                   | Telefax:  + 358 - 0 - 464 788
SHAR_EOF
if test 667 -ne "`wc -c < '0README'`"
then
	echo shar: error transmitting "'0README'" '(should have been 667 characters)'
fi
fi # end of overwriting check
if test -f 'MANIFEST'
then
	echo shar: will not over-write existing file "'MANIFEST'"
else
cat << \SHAR_EOF > 'MANIFEST'
0README
MANIFEST
Makefile
README
README.loadavgd
getloadavg.3
getloadavg.c
getloadavg.h
getproc.3
getproc.h
launch_loadavgd.c
loadavg.c
loadavgd.c
monitor.1
monitor.c
testgetloadavg.c
top.c
top.h
uptime.c
SHAR_EOF
if test 205 -ne "`wc -c < 'MANIFEST'`"
then
	echo shar: error transmitting "'MANIFEST'" '(should have been 205 characters)'
fi
fi # end of overwriting check
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
#
# Makefile for loadavgd and monitor -programs
#
# Author: Jussi Maki, jmaki@hut.fi, 31.5.1991
#			modified    10.7.1991
#
# How to install:
#  1. Edit Makefile to contain correct loadavgd-programs position
#  2. Run make
#  3. Install programs by making monitor and loadavgd suid-programs
#     to root user eg.
#        chown root monitor loadavgd
#        chmod 711,u+s monitor loadavgd
#     Copy files to your favourite location (perhaps /usr/local/bin ?)
#     Or type make install
#

LOADAVGD_LOCATION= /usr/local/bin/loadavgd
INSTALLDIR= /usr/local/bin

CFLAGS= -O

MONITOROBJS = monitor.o top.o 

all:	loadavgd monitor uptime

uptime:	uptime.o libgetloadavg.a
	cc $(CFLAGS) -o uptime uptime.o libgetloadavg.a

loadavgd:	loadavgd.o loadavg.o
	cc $(CFLAGS) -o loadavgd loadavgd.o loadavg.o

monitor:	$(MONITOROBJS) libgetloadavg.a
	cc $(CFLAGS) -o monitor $(MONITOROBJS) libgetloadavg.a -lcurses
	
testgetloadavg:	testgetloadavg.o libgetloadavg.a
	cc $(CFLAGS) -o testgetloadavg testgetloadavg.o libgetloadavg.a

launch_loadavgd.o:	launch_loadavgd.c
	cc -c $(CFLAGS) -DLOADAVGD_LOCATION=\"$(LOADAVGD_LOCATION)\" launch_loadavgd.c

libgetloadavg.a:	getloadavg.o launch_loadavgd.o 
	-/bin/rm -f libgetloadavg.a
	ar qc libgetloadavg.a getloadavg.o launch_loadavgd.o
	ranlib libgetloadavg.a

tar:	
	tar cvf monitor-1.04.tar 0README MANIFEST Makefile README README.loadavgd \
	  getloadavg.c getloadavg.h launch_loadavgd.c \
	  loadavg.c loadavgd.c monitor.c  \
	  top.c top.h getproc.h testgetloadavg.c uptime.c \
	  monitor.1 getloadavg.3 getproc.3

clean:
	-/bin/rm $(MONITOROBJS) core

install:
	-/bin/rm -f $(INSTALLDIR)/uptime
	-/bin/rm -f $(INSTALLDIR)/loadavgd
	-/bin/rm -f $(INSTALLDIR)/monitor
	/bin/cp uptime loadavgd monitor $(INSTALLDIR)
	chmod 711 $(INSTALLDIR)/loadavgd
	chmod 711 $(INSTALLDIR)/monitor
	chmod u+s $(INSTALLDIR)/loadavgd
	chmod u+s $(INSTALLDIR)/monitor
SHAR_EOF
if test 1888 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 1888 characters)'
fi
fi # end of overwriting check
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
Monitor program is an AIX 3.1 System performancemonitor program.
Monitor-program can be used to display system statistics
of various short time performance values.

New to version 1.04
	* process info sorted by cpu-usage (in top.c and top.h files)
	  try 'monitor -top'
	  Information shown are process-id, username, priority, nice-value,
	  program size (virtualmemeory), program size in realmemory (RES),
	  process status, used cputime, cputime usage between display refresh.
	* new flags [-top [nprocs]] [-all]
	* changed exit with ctrl-c or q

New to version 1.03
	* included uptime-command that users loadavgd to get loadavgvalues

New to version 1.02
	* start using loadavgd-daemon
	* added ethernet-interface number to name
	
Values showed in monitor are (most of the values are converted
to be showed as units/second).
	* hostname and date
	* percentage distribution of cpu-load (system, wait, user and 
	  idle percentages) with graf where '=' means system,	
	  'W' means wait, ">" means user and "." means idle time.
	* runnable processes value/second and load average values
	  of 1, 5 and 15 minute times 
	* processes waiting to be swapped in
	* both free and total from real and virtual memory,
	  real memory is RAM-memory and virtual memory
	  is paging space
	* paging information; pagefaults, pages to be paged in and out
	  from user space and pages to be paged in and out from
	  paging (swap) space, page size in AIX 3.1 is 4 kB,
	* various process and system events: process (context) switches,
	  system call, read and write -calls, forks and execs,
	  three interrupts rcvint, xmtint, mdmint.
	* File and tty-IO variables; iget, namei and dirblk, amout of read
	  and written kbytes, amount of ttybased characters handled.
	* Disk-information, read and written bytes/second and amount
	  of busy time 
	* network (netw) information; amount read and written kB per second.

See also:
	AIX commands vmstat(1), sar(1), netstat(1)
	/usr/include/sys/{sysinfo.h, vminfo.h, iostat.h}
	/usr/include/sys/if.h
	
Plan:
	* X11-userinterface

Sample output from monitor program version 1.01 in one of
our IBM RISC System/6000 model 540.
------------------------------------------------------------------------------
System monitor v1.03: leka.hut.fi                 Fri May 31 14:24:58 1991

Sys  1.0% Wait  0.0% User 99.0% Idle  0.0%
0%             25%              50%               75%              100%
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Runnable processes  6.00  load average:  6.03,  6.03,  5.38
Swap-in processes   0.00
                                Paging (4kB)    Process events   FILE/TTY-IO
Memory    Real     Virtual        0.0 pgfaults    125 pswitch       0 iget
free     29.8 MB  154.7 MB        0.0 pgin         36 syscall       0 namei
total   192.0 MB  320.0 MB        0.0 pgout         9 read          0 dirblk
                                  0.0 pgsin         2 write       662 readch
DiskIO   read    write   busy     0.0 pgsout        0 fork        143 writech
hdisk2    0.0   0.0 kB/s   0%                       0 exec          0 ttyrawch
hdisk3    0.0   0.0 kB/s   0%                       0 rcvint        0 ttycanch
hdisk4    0.0   0.0 kB/s   0%                       0 xmtint        0 ttyoutch
hdisk0    0.0   0.0 kB/s   0%                       0 mdmint
hdisk1    0.0   0.0 kB/s   0%
cd0       0.0   0.0 kB/s   0%                       Netw  read   write
                                                    lo0    0.0   0.0 kB/s
                                                    en0    0.2   0.2 kB/s

------------------------------------------------------------------------------
Author: Jussi Maki
        Helsinki University of Technology
        Computing Centre
        Otakaari 1
        SF-02150 ESPOO
        FINLAND, EUROPE

Internet-mail: jmaki@vipunen.hut.fi	(or)  jussi.maki@hut.fi
Telephone:	+358-0-451 4317
Telefax:	+358-0-464 788
SHAR_EOF
if test 3928 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 3928 characters)'
fi
fi # end of overwriting check
if test -f 'README.loadavgd'
then
	echo shar: will not over-write existing file "'README.loadavgd'"
else
cat << \SHAR_EOF > 'README.loadavgd'
Loadavgd is a load average information daemon. Program can ask current
load average values (1min, 5min and 15 minutes average) from
loadavgd-program using datagram (udp) service.  This program is needed
to maintain loadavg-values because AIX3.1 (3005) doesn't maintain
loadavg-values in kernel.

loadavgd should be installed as suid-root-program to be able to read
/dev/kmem. Also the installation directory-name and loadavg portnumber
should be defined in getloadavg.h (currently they are
"/usr/local/bin/loadavgd" and 2112).

loadavg-values can be read with getloadavg-subroutine which
uses similar interface as bsd4.3-tahoe.
	getloadavg(double loadv[], int nelem);

getloadavg-subroutine tries to get response from loadavgd-server if it
can't getloadavg will start a server with launch_loadavgd()- 
subroutine.

If somebody want to start loadavg-daemon directly it can
be done like eg.
	./loadavgd 2112
where 2112 is the udp-service port-number.

------------------------------------------------------------------------------
Date:	31.5.1991
Author: Jussi Maki
        Helsinki University of Technology
        Computing Centre
        Otakaari 1
        SF-02150 ESPOO
        FINLAND, EUROPE

Internet-mail: jmaki@hut.fi
Telephone:	+358-0-451 4317
Telefax:	+358-0-464 788
SHAR_EOF
if test 1277 -ne "`wc -c < 'README.loadavgd'`"
then
	echo shar: error transmitting "'README.loadavgd'" '(should have been 1277 characters)'
fi
fi # end of overwriting check
if test -f 'getloadavg.3'
then
	echo shar: will not over-write existing file "'getloadavg.3'"
else
cat << \SHAR_EOF > 'getloadavg.3'
GETLOADAVG(3)		UNIX Programmer's Manual		GETLOADAVG(3)

NAME
	getloadavg - subroutine to get system load average
	values

SYNTAX
	getloadavg(double loadv[],int nelems)
	
DESCRIPTION
	loadv is the array of loadvalues, which is usually
	defined as double loadv[3] and nelems is the
	number of loadvalues which is usually 3.

	This getloadavg version is meant to be used
	in AIX 3.1 which doesn't have loadaverage values
	in kernel. These values are calculated by external
	process called loadavgd. 

	getloadavg can be used by linking it with libgetloadavg.a
	library and including getloadavg.h include file.
SHAR_EOF
if test 607 -ne "`wc -c < 'getloadavg.3'`"
then
	echo shar: error transmitting "'getloadavg.3'" '(should have been 607 characters)'
fi
fi # end of overwriting check
if test -f 'getloadavg.c'
then
	echo shar: will not over-write existing file "'getloadavg.c'"
else
cat << \SHAR_EOF > 'getloadavg.c'
/* getloadavg.c -- routine to get loadavgd-values
** this version is used with loadavgd-server in aix3.1
** 
*/

#include <sys/errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <signal.h>

#include "getloadavg.h"

#define MIN(a,b) ((a)<(b)?(a):(b))

int signal_handler(sig, code, scp)
int sig;
int code;
struct sigcontext *scp;
{
    /* set this signal-handler again */
    signal(sig,signal_handler);
}

getloadavg(loadv,nelem)
double loadv[];
int nelem;
{
    static int initted=0;
    static int sock;
    static struct sockaddr_in server;
    static struct hostent *hp;
    static long alarm_sig;
    char buf[80];
    int status;
    int server_len = sizeof(struct sockaddr_in);
    int errno2;
    int i;

    if (! initted) {
	initted=1;
        sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock < 0) { perror("opening datagram socket"); exit(1); }
	server.sin_family = AF_INET;
	hp = gethostbyname("localhost");
	if (hp == 0) { fprintf(stderr,"localhost unknown\n"); exit(1); }
	bcopy(hp->h_addr, &server.sin_addr, hp->h_length);
	server.sin_port = htons(LOADAVGD_PORT);
    }
    
    /* send something to loadavgd-server to get reply back */
    status = sendto(sock,"\n\000",2,0, &server,sizeof(server));
    if (status == -1) {perror("getloadavg: sendto loadavgd");exit(1);}

    alarm_sig = signal(SIGALRM,signal_handler);
    alarm(2); /* lets wait for 2 second to get respond from server */
    status = recvfrom(sock, buf, 80,0, &server, &server_len);
    errno2 = errno;
    alarm(0);
    signal(SIGALRM,alarm_sig);

    if (status==-1) {
	if (errno2 == EINTR) { /* if recvfrom was interrupter by alarm */
	    launch_loadavgd();
	    fprintf(stderr,"\n***getloadavg: loadavgd-daemon started***\n");
	    fflush(stderr);
	    sleep(1);
	    status = sendto(sock,"\n\000",2,0, &server,sizeof(server));
	    if (status == -1) {perror("getloadavg: sendto loadavgd");exit(1);}
	    status = recvfrom(sock, buf, 80,0, &server, &server_len);
	}
	if (status == -1) {
	    perror("getloadavg: recvfrom from loadavgd daemon");
	    exit(1);
	}
    }
    if (nelem == 3) sscanf(buf,"%lf %lf %lf",&loadv[0],&loadv[1],&loadv[2]);
    if (nelem == 2) sscanf(buf,"%lf %lf",&loadv[0],&loadv[1]);
    if (nelem == 1) sscanf(buf,"%lf",&loadv[0]);
}
SHAR_EOF
if test 2325 -ne "`wc -c < 'getloadavg.c'`"
then
	echo shar: error transmitting "'getloadavg.c'" '(should have been 2325 characters)'
fi
fi # end of overwriting check
if test -f 'getloadavg.h'
then
	echo shar: will not over-write existing file "'getloadavg.h'"
else
cat << \SHAR_EOF > 'getloadavg.h'

extern getloadavg(double loadv[], int nelem);

#ifndef LOADAVGD_PORT
#define LOADAVGD_PORT 2112
#endif
#ifndef LOADAVGD_LOCATION
#define LOADAVGD_LOCATION "/usr/local/bin/loadavgd"
#endif
SHAR_EOF
if test 189 -ne "`wc -c < 'getloadavg.h'`"
then
	echo shar: error transmitting "'getloadavg.h'" '(should have been 189 characters)'
fi
fi # end of overwriting check
if test -f 'getproc.3'
then
	echo shar: will not over-write existing file "'getproc.3'"
else
cat << \SHAR_EOF > 'getproc.3'

You will need to take a look at /usr/include/procinfo.h also...

getproc(struct procinfo *procinfo, int nproc, int sizproc)
/* struct  procinfo *procinfo;   pointer to array of procinfo struct    *
/* int  nproc;                   number of user procinfo struct */
/* int  sizproc;                 size of expected procinfo structure    *

Retrieves an image of the process table

getargs(struct procinfo *procinfo, int plen, char *args, int alen)
/* struct  procinfo *procinfo;   pointer to array of procinfo struct    *
/* int  plen;                    size of expected procinfo struct       *
/* char *args;                   pointer to user array for arguments    *
/* int  alen;                    size of expected argument array        *

getuser(struct procinfo *procinfo, int plen, void *user, int ulen)
/* struct  procinfo *procinfo;   ptr to array of procinfo struct
 * int     plen;                 size of expected procinfo struct
 * void   *user;                 ptr to array of userinfo struct, OR user
 * int     ulen;                 size of expected userinfo struct
 */

SHAR_EOF
if test 1089 -ne "`wc -c < 'getproc.3'`"
then
	echo shar: error transmitting "'getproc.3'" '(should have been 1089 characters)'
fi
fi # end of overwriting check
if test -f 'getproc.h'
then
	echo shar: will not over-write existing file "'getproc.h'"
else
cat << \SHAR_EOF > 'getproc.h'
extern getproc(struct procinfo *procinfo, int nproc, int sizproc);
/* struct  procinfo *procinfo;   pointer to array of procinfo struct    *
/* int  nproc;                   number of user procinfo struct */
/* int  sizproc;                 size of expected procinfo structure    *

extern getargs(struct procinfo *procinfo, int plen, char *args, int alen);
/* struct  procinfo *procinfo;   pointer to array of procinfo struct    *
/* int  plen;                    size of expected procinfo struct       *
/* char *args;                   pointer to user array for arguments    *
/* int  alen;                    size of expected argument array        *

extern getuser(struct procinfo *procinfo, int plen, void *user, int ulen);
/* struct  procinfo *procinfo;   ptr to array of procinfo struct
 * int     plen;                 size of expected procinfo struct
 * void   *user;                 ptr to array of userinfo struct, OR user
 * int     ulen;                 size of expected userinfo struct
 */
SHAR_EOF
if test 1005 -ne "`wc -c < 'getproc.h'`"
then
	echo shar: error transmitting "'getproc.h'" '(should have been 1005 characters)'
fi
fi # end of overwriting check
if test -f 'launch_loadavgd.c'
then
	echo shar: will not over-write existing file "'launch_loadavgd.c'"
else
cat << \SHAR_EOF > 'launch_loadavgd.c'
/* launch_loadavgd.c -- start loadavgd process 
** Called from getloadavg if loadavgd server is not running
** or accepting connections to port.
**
*/

#include "getloadavg.h"

launch_loadavgd()
{
    char buf[32];
    int i;
    
    sprintf(buf,"%d",LOADAVGD_PORT);
    switch (fork()) {
      case 0: /* child */
	execl(LOADAVGD_LOCATION,"loadavgd",buf,0);
	perror("cannot exec");
	exit(2);
	/* never returns */
      case -1: /* error */
	perror("cannot fork loadavgd");
	exit(2);
      default: /*parent */
	break;
    }
}
SHAR_EOF
if test 528 -ne "`wc -c < 'launch_loadavgd.c'`"
then
	echo shar: error transmitting "'launch_loadavgd.c'" '(should have been 528 characters)'
fi
fi # end of overwriting check
if test -f 'loadavg.c'
then
	echo shar: will not over-write existing file "'loadavg.c'"
else
cat << \SHAR_EOF > 'loadavg.c'
/* loadavg.c
** monitor (AIX 3.1 System monitor) - loadavg part
**
**
*/

/* update load-values after this time */
#define LOAD_UPDATE_SECS 5

/* ring buffers for runque and runocc values */
#define SIZE_STORE ((60/LOAD_UPDATE_SECS)*15)
static int store_runque[SIZE_STORE];
static int store_runocc[SIZE_STORE];
/* ring buffer index values */
static int rbi_current=-1;
static int rbi_load1=SIZE_STORE-60/LOAD_UPDATE_SECS;
static int rbi_load5=SIZE_STORE-(5*60)/LOAD_UPDATE_SECS;
static int rbi_load15=SIZE_STORE-(15*60)/LOAD_UPDATE_SECS;

calcloadavg(load1,load5,load15)
double *load1,*load5,*load15;
{
  int runque,runocc;
  
  runque=store_runque[rbi_current];
  runocc=store_runocc[rbi_current];
  *load1 = (double)(runque - store_runque[rbi_load1])
    / (double) (runocc - store_runocc[rbi_load1]);
  *load5 = (double)(runque - store_runque[rbi_load5])
    / (double) (runocc-store_runocc[rbi_load5]);
  *load15 = (double)(runque - store_runque[rbi_load15])
    / (double) (runocc - store_runocc[rbi_load15]);
  /* decrement loadvalue by kproc's (idle process) value */
  *load1 = *load1 - 1.0;
  *load5 = *load5 - 1.0;
  *load15 = *load15 - 1.0;
}

static loadavg_init(runque,runocc)
int runque,runocc;
{
  int i;
  for (i=0;i<SIZE_STORE;i++) {
    store_runque[SIZE_STORE-i-1] = runque-i*LOAD_UPDATE_SECS;
    store_runocc[SIZE_STORE-i-1] = runocc-i*LOAD_UPDATE_SECS;
  }
}

update_loadavg(runque,runocc)
{
  static int prev_update=0;

  if (prev_update==0) loadavg_init(runque,runocc);
  if (time(0)-prev_update > LOAD_UPDATE_SECS) {
    loadavg_put(runque,runocc);
    prev_update=time(0);
  }
}

#define RB_INC(index) ((index+1>=(SIZE_STORE))?(0):(index+1))

loadavg_put(runque,runocc)
int runque,runocc;
{
  rbi_current = RB_INC(rbi_current);
  rbi_load1   = RB_INC(rbi_load1);
  rbi_load5   = RB_INC(rbi_load5);
  rbi_load15  = RB_INC(rbi_load15);
  store_runque[rbi_current]=runque;
  store_runocc[rbi_current]=runocc;
}
SHAR_EOF
if test 1934 -ne "`wc -c < 'loadavg.c'`"
then
	echo shar: error transmitting "'loadavg.c'" '(should have been 1934 characters)'
fi
fi # end of overwriting check
if test -f 'loadavgd.c'
then
	echo shar: will not over-write existing file "'loadavgd.c'"
else
cat << \SHAR_EOF > 'loadavgd.c'
/* loadavgd.c -- load average daemon for AIX 3.1
** Copyright 1991 Jussi M{ki. All Rights reserved.
** NON-COMMERCIAL USE ALLOWED. YOU MAY USE, COPY AND DISTRIBUTE 
** THIS PROGRAM FREELY AS LONG AS ORIGINAL COPYRIGHTS ARE LEFT
** AS THEY ARE.
**
** History:
** Created 27.5.1991 as part of monitor v1.02 program
** first version made as tcp-service. Second version changed to 
** use datagram (udp) service.
**
** Use getloadavg()-subroutine (getloadavg.c) to get 
** currrent loadavg-values. 
*/

/* (This was found in previous version).
** There's bug or feature in aix socket-support. (At least on 3003 and 2004).
** If connection is closed from remote-end and user tries to write to that 
** socket write doesn't die in SIGPIPE as it dies in BSD4.3.
** Very many user programs relay on this feature.
** So there are also many programs which are left
** running on system while disconnection network (telnet) connection.
**
** In AIX3.1 program doesn't fail in write() and it returns -1,
** but in BSD4.3 it simply dies out with signal.
*/

#include <sys/types.h>

#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <sys/sysinfo.h>
#include <sys/nlist.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>

/*
#define DEBUG 1
*/

#define MIN(a,b) ((a)<(b)?(a):(b))

int master_sd;
int master_port;

main(argc,argv)
int argc;
char *argv[];
{
    int tty;
    if (argc<2) {
	fprintf(stderr,"Usage: loadavgd portnumber\n");
	exit(1);
    }
    master_port=atoi(argv[1]);
    if (master_port<1) {
	fprintf(stderr,"Usage: loadavgd portnumber\n");
	exit(1);
    }
#ifndef DEBUG
    close(0);close(1);close(2);
    /* release this process from tty-control */
    setsid();
    if ((tty = open ("/dev/tty", 2)) >= 0) {
      ioctl (tty, TIOCNOTTY, (char *) NULL);
      close (tty);
    }
#endif

    init_signals();
    init_socket();
    handle_loadavg();
    while (1) {
	handle_io();
	handle_loadavg();
    }
}

int signal_handler(sig, code, scp)
int sig;
int code;
struct sigcontext *scp;
{
#ifdef DEBUG
    fprintf(stderr,"Signal %d occured\n",sig);
    fflush(stderr);
#endif
    /* set this signal-handler again */
    signal(sig,signal_handler);
}

init_signals()
{
  int i,old_sig;

  for (i=1; i<32; i++) {
      old_sig = signal(i,signal_handler);
  }
  signal(SIGALRM,signal_handler);
}

init_socket()
{
    int i,namesize;
    int status;
    struct sockaddr_in name_master;
    int port_master;

    master_sd = socket(AF_INET,SOCK_DGRAM,0);
    if (master_sd == -1) { perror("opening socket"); exit(1); }

    name_master.sin_family = AF_INET;
    name_master.sin_addr.s_addr = INADDR_ANY;
    name_master.sin_port = master_port;

    status = bind(master_sd,&name_master,sizeof(name_master));
    if (status) { perror("binding socket"); exit(1); }
}

handle_io()
{
    int status;
    char input_buf[BUFSIZ];
    static char output_buf[BUFSIZ];
    double l1,l5,l15;
    struct sockaddr from_addr;
    int from_len = sizeof(struct sockaddr);

    alarm(5);
    status = recvfrom(master_sd, input_buf, BUFSIZ,0,
		      &from_addr, &from_len);
    if (status != -1) {
	calcloadavg(&l1,&l5,&l15);
	sprintf(output_buf,"%f %f %f\n",l1,l5,l15);
	status = sendto(master_sd,output_buf,strlen(output_buf)+1,0,
			&from_addr,from_len);
    }
}

handle_loadavg()
{
    static int initted=0;
    static int fd;
    static struct sysinfo si;
    static struct nlist kernelnames[] = {
	{"sysinfo", 0, 0, 0, 0, 0},
	{NULL, 0, 0, 0, 0, 0},
        };

    if (!initted) {
	initted = 1;
	fd = open("/dev/kmem", O_RDONLY);
	if (fd < 0) {
	    perror("kmem");
	    exit(1);
	}
	if (knlist(kernelnames,1,sizeof(struct nlist)) == -1) {
	    perror("knlist, entry not found");
	}
    }
    lseek(fd, kernelnames[0].n_value,SEEK_SET);
    read(fd,&si,sizeof(si));
    update_loadavg(si.runque,si.runocc);
}
SHAR_EOF
if test 3895 -ne "`wc -c < 'loadavgd.c'`"
then
	echo shar: error transmitting "'loadavgd.c'" '(should have been 3895 characters)'
fi
fi # end of overwriting check
if test -f 'monitor.1'
then
	echo shar: will not over-write existing file "'monitor.1'"
else
cat << \SHAR_EOF > 'monitor.1'
MONITOR(1)		UNIX Programmer's Manual		MONITOR(1)

NAME
	monitor - display and update information about the
	Aix version 3 system events and optionally top cpu
	processes

SYNOPSIS
	monitor [-s time] [-top [number]] [-all]

DESCRIPTION
	Monitor shows various system variables and updates
	the screen periodically. Monitor can also be used
	to show top cpu processes.

	Monitor program can be exited by typing a letter 'q'
	or by typing cltr-c (keyboard interrupt).

	Monitor uses another process to calculate load average values
	in background. This process is started automatically
	first time some user tries to use this monitor or any
	other program which tries to get loadavgvalues with
	supplied getloadavg-subroutine.

OPTIONS
	-s    Set the delay between screen updates to time
	      seconds. The default delay is 5 seconds after 
	      first initial 1 second delay. Note the smaller
	      the delay is more the monitor will use cputime.
	  
	-top  Shows top cpu processes and shows only summary
	      of system variables. If extra parameter is 
	      given monitor will show that amount of processes.

	-all  Shows both system events and top cpu processes.
	      Note this will need higher window (eg. Xwindows 
	      xterm).


THE DISPLAY
	In normal version monitor will show following information
	* hostname, date and time between display delay.
	* percentage distribution of cpu-load (system, wait, user and 
	  idle percentages) with graf where '=' means system,	
	  'W' means wait, ">" means user and "." means idle time.
	* runnable processes value/second and load average values
	  of 1, 5 and 15 minute times 
	* processes waiting to be swapped in
	* both free and total from real and virtual memory,
	  real memory is RAM-memory and virtual memory
	  is paging space
	* paging information; pagefaults, pages to be paged in and out
	  from user space and pages to be paged in and out from
	  paging (swap) space, page size in AIX 3.1 is 4 kB,
	* various process and system events: process (context) switches,
	  system call, read and write -calls, forks and execs,
	  three interrupts rcvint, xmtint, mdmint.
	* File and tty-IO variables; iget, namei and dirblk, amout of read
	  and written kbytes, amount of ttybased characters handled.
	* Disk-information, read and written bytes/second and amount
	  of busy time 
	* network (netw) information; amount of read and written kB
	  per second.

	When using -top option top lines will show:
	* hostname, load average values of 1, 5 and 15 minutes
	  and date
	* percentage distribution of cpu states (user, system, wait
	  and idle time)
	* for real and virtual memory the amount used, free and total
	  memoory

	Following lines will show values of the top cpu time processes
	sorted by current cpu-usage as first sortkey and by second
	key the total amount cputime used.

	The display of processes is similar to ps(1). PID is the
	process id, USER is the username of the process owner,
	PRI is the current priority of the process, NICE is the
	nice value for the process in the range -20 to 20
	(note for kproc-processes the nice value is 21 which means
	that the process has fixed-priority and the nice value
	doesn't have any meaning), SIZE is the amount virtual
	memory used, RES is the amount of real memory used, 
	STAT is the status of the process, TIME is the amount
	of cputime used, CPU% is the cputime distribution
	between two displays and COMMAND is the short-name of
	the process command. If process was kernel process
	"(kproc)" is added to process name.
	
	Version 1.04 of monitor will display both system and user
	processes in process list. So usually in idle machine process
	named 'wait (kproc)' will use most of the cpu-time.

AUTHOR
	Jussi Maki, Helsinki University of Techonology, Computing Centre
	Email: jmaki@vipunen.hut.fi

DATE	
	10-Jul-1991

SEE ALSO
	vmstat(1), ps(1), sar(1), top
SHAR_EOF
if test 3884 -ne "`wc -c < 'monitor.1'`"
then
	echo shar: error transmitting "'monitor.1'" '(should have been 3884 characters)'
fi
fi # end of overwriting check
if test -f 'monitor.c'
then
	echo shar: will not over-write existing file "'monitor.c'"
else
cat << \SHAR_EOF > 'monitor.c'
/* monitor.c -- AIX 3.1 System monitor 
**
** Copyright (c) 1991 Jussi Maki. All Rights Reserved.
** NON-COMMERCIAL USE ALLOWED. YOU ARE FREE TO DISTRIBUTE
** THIS PROGRAM AND MODIFY IT AS LONG AS YOU KEEP ORIGINAL
** COPYRIGHTS.
**
** Author: Jussi Maki
**         Helsinki University of Technology
**         Computing Centre
**         Otakaari 1
**         SF-02150 ESPOO
**         FINLAND, EUROPE
**
** Phone:  +358-0-451 4317
** Telefax: +358-0-464 788
**
** Internet-mail: jmaki@hut.fi
**
** Compile: with Makefile
** Install: run either as root or make the program
**          owner root and program setuid.
**            chown root monitor
**            chmod u+s monitor
**
** History:
** created:  15.5.1991 v1.0
** modified: 21.5.1991 v1.0beta    added disk-io display
** modified: 24.5.1991 v1.01       added network interface (ifnet) display
**           29.5.1991 v1.01a dhc: added digit after network interface name 
**           30.5.1991 v1.02       created loadavgd-server
**                                 changed getloadavg() interface
**                                 to bsd-tahoe interface
** modified: 05.7.1991 v1.03       added signal handler
**           08.7.1991 v1.04beta   added top cputimeshower (ala top)
**           09.7.1991 v1.04b1     added putenv("LANG=En_US") for curses bug
**           10.7.1991 v1.04       release of v1.04 (changed behaviour of q)
*/

#include <curses.h>
#include <stdio.h>
#include <signal.h>

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>
#include <nlist.h>
#include <time.h>
#include <sys/time.h>

#include <sys/sysinfo.h>
#include <sys/vminfo.h>
#include <sys/iostat.h>
/* include files needed for ifnet-networkinterface */
#include <sys/socket.h>
#include <net/if.h>

#include "getloadavg.h"
#include "top.h"

/* vmker struct is kernelstruct (undocumented) */
/* vmker seems to hold some kernels virtual memeory variables */
struct vmker {
  uint n0,n1,n2,n3,n4,n5,n6,n7,n8;
  uint totalmem;
  uint n10;
  uint freemem;
  uint n12,n13;
  uint totalvmem,freevmem;
};

int dk_cnt;  /* number of disks in system  */

#define TICK_D 60.0

#define NUMBER_OF_KNSTRUCTS 5
#define NLIST_SYSINFO 0
#define NLIST_VMKER 1
#define NLIST_VMINFO 2
#define NLIST_IOSTAT 3
#define NLIST_IFNET 4
struct nlist kernelnames[] = {
    {"sysinfo", 0, 0, 0, 0, 0},
    {"vmker", 0, 0, 0, 0, 0},
    {"vmminfo", 0, 0, 0, 0, 0},
    {"iostat", 0, 0, 0, 0, 0},
    {"ifnet", 0, 0, 0, 0, 0},
    {NULL, 0, 0, 0, 0, 0},
    };
#define N_VALUE(index) (kernelnames[index].n_value)

int sleep_sec=5;

double realtime();

#define MAX_IFNETS 16
#define MAX_IPNAME 32

#define MONITOR_NAME "System monitor v1.04"

sighandler(signum)
int signum;
{
  switch (signum) {
  case SIGTSTP:
    nocbreak();
    endwin();
    kill(getpid(),SIGSTOP);
    /* returned here from sigstop */
    cbreak();
    initscr();
    clear();
    break;
  case SIGINT:
    nocbreak();
    endwin();
    exit(0);
    break;
  default: fprintf(stderr,"unknown signal %d\n",signal); fflush(stderr);
    nocbreak();
    endwin();
    exit(0);
  }
  signal(signum,sighandler);
}

int end_monitor=0;

main(argc,argv)
int argc;
char **argv;
{
    struct sysinfo si1,si2;
    struct vminfo vm1,vm2;
    struct vmker vmk;
    struct dkstat *dk1,*dk2;
    double time1,time2;
    struct ifnet *ifnets1[MAX_IFNETS];
    struct ifnet *ifnets2[MAX_IFNETS];

    int ntop = 17; /* top procs to show by default */
    int top_nproc1,top_nproc2;
    double top_cpusum;
    int show_all=0,show_top=0;

    putenv("LANG=En_US"); /* set language to En_US because there are 
			     problems with printw (and/or) printf
			     while using LANG where decimal-comma
			     is not period "." but is comma "," */
    signal(SIGTSTP,sighandler);
    signal(SIGINT,sighandler);

    clear_ifnets(ifnets1,MAX_IFNETS);
    clear_ifnets(ifnets2,MAX_IFNETS);
    while (argc>1) {
      if (argv[1][0] == '-') {
	switch(argv[1][1]) {
	case 's':
	  sleep_sec = atoi(argv[2]); argc--;argv++;
	  if (sleep_sec < 1) sleep_sec=1;
	  break;
	case 'a': /* -all */
	  show_all = 1;
	  show_top = 1;
	  break;
	case 't': /* -top [nproc] */
	  show_top = 1;
	  if (atoi(argv[2])>1) {
	    ntop=atoi(argv[2]);
	    argc--;argv++;
	  }
	  break;
	default:
	  fprintf(stderr,"Usage: monitor [-s sleep] [-top [nproc]] [-all]\n");
	  fprintf(stderr,"Monitor is a curses-based AIX3 system event monitor\n");
	  fprintf(stderr,"     -s sleep      set refresh time\n");
	  fprintf(stderr,"     -top [nproc]  show top processes sorted by cpu-usage\n");
	  fprintf(stderr,"     -all          show all variable (needs high window)\n");
	  exit(0);
	  break;
	}
      }
      argv++;argc--;
    }
	  
    if (knlist(kernelnames,NUMBER_OF_KNSTRUCTS,sizeof(struct nlist)) == -1){
	perror("knlist, entry not found");
    }

    initscr();
    clear();
    cbreak();

    /* 0--let get_dkstat allocate memeory, 1--do not allocate */
    get_dkstat(&dk1,0); 
    get_dkstat(&dk2,0);
    get_sys_vm_info(&si2,&vmk,&vm2);
    get_ifnet(ifnets2); /* gets and initalizes ifnets-list automativally */
    if (show_top) 
      top_nproc1 = top_getprocinfo(top_proc1,top_user1);
    time2=realtime();
    sleep(1);

    while (! end_monitor) {
	get_sys_vm_info(&si1,&vmk,&vm1);
	if (!(show_top&&!show_all)) {
	  get_dkstat(&dk1,1); get_ifnet(ifnets1);
	}
	if (show_top) {
	  top_nproc2 = top_getprocinfo(top_proc2,top_user2);
	  top_cpusum = top_calcsortinfo(top_proc1,top_user1,top_nproc1,
				  top_proc2,top_user2,top_nproc2,top_sortinfo);
	}
	time1=realtime();
	checkinput();
	if (show_top&&!show_all) {
	  print_summary(time1-time2,&si1,&si2,&vmk);
	} else {
	  print_sysinfo(time1-time2,&si1,&si2,&vmk,&vm1,&vm2);
	  print_dkstat(time1-time2,dk1,dk2);
	  print_ifnet(time1-time2,ifnets1,ifnets2);
	} 
	if (show_top) {
	  top_print(ntop,top_proc2,top_user2,top_sortinfo,top_nproc2,top_cpusum);
	}
	refresh();
	sleep(sleep_sec);

	get_sys_vm_info(&si2,&vmk,&vm2);
	if (!(show_top&&!show_all)) {
	  get_dkstat(&dk2,1); get_ifnet(ifnets2);
	}
	if (show_top) {
	  top_nproc1 = top_getprocinfo(top_proc1,top_user1);
	  top_cpusum = top_calcsortinfo(top_proc2,top_user2,top_nproc2,
				      top_proc1,top_user1,top_nproc1,top_sortinfo);
	}
	time2=realtime();
	checkinput();
	if (show_top&&!show_all) {
	  print_summary(time2-time1,&si2,&si1,&vmk);
	} else {
	  print_sysinfo(time2-time1,&si2,&si1,&vmk,&vm2,&vm1);
	  print_dkstat(time2-time1,dk2,dk1);
	  print_ifnet(time2-time1,ifnets2,ifnets1);
	} 
	if (show_top) {
	  top_print(ntop,top_proc1,top_user1,top_sortinfo,top_nproc1,top_cpusum);
	}
	refresh();
	sleep(sleep_sec);
    }
    endwin();
}

double realtime()
{
  struct timeval tp;
  gettimeofday(&tp,0);
  return((double)tp.tv_sec+tp.tv_usec*1.0e-6);
}

quit_monitor()
{
  nocbreak();
  endwin();
  exit(0);
}

/* checkinput is the subroutine to handle user input */
checkinput()
{
  char inbuf[1024];
  int nbytes;

  if ((nbytes=bytesreadable(fileno(stdin))) > 0) {
    read(fileno(stdin),inbuf,nbytes);
    if (inbuf[0]=='q') {
      quit_monitor();
    }
  }
}

int bytesreadable(in)
int in;
{
  static int bytes;
  ioctl(in,FIONREAD,&bytes);
  return(bytes);
}

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

#define BARLEN 72
#define SIDELTA(a) (si->a - si2->a)
#define VMDELTA(a) (vm->a - vm2->a)

print_sysinfo(refresh_time,si,si2,vmk,vm,vm2)
double refresh_time;
struct sysinfo *si,*si2;
struct vmker *vmk;
struct vminfo *vm,*vm2;
{
    double cpu_sum;
    double str_cpu_sum;
    double swp_proc;
    char bar[BARLEN];
    time_t time1;
    double loadv[3];
    char hostnm[128];
    int x,y;

    gethostname(hostnm,sizeof(hostnm));
    time1=time(0);
    move(0,0); printw("%s: %s",MONITOR_NAME,hostnm);
    move(0,50);printw(ctime(&time1));
    move(1,50);printw("Refresh: %5.2f s",refresh_time);
    cpu_sum = SIDELTA(cpu[CPU_IDLE]) + SIDELTA(cpu[CPU_USER]) 
      + SIDELTA(cpu[CPU_KERNEL]) + SIDELTA(cpu[CPU_WAIT]);
    str_cpu_sum = cpu_sum/(double)BARLEN;
    cpu_sum = cpu_sum/100.0;

    move(2,0);
    printw("Sys %4.1lf%% Wait %4.1lf%% User %4.1lf%% Idle %4.1lf%%\n",
	   SIDELTA(cpu[CPU_KERNEL])/cpu_sum,
	   SIDELTA(cpu[CPU_WAIT])/cpu_sum,
	   SIDELTA(cpu[CPU_USER])/cpu_sum,
	   SIDELTA(cpu[CPU_IDLE])/cpu_sum);
    printw("0%%             25%%              50%%               75%%              100%%\n");
    bar[0]=0;
    strchgen(bar,'=',(int)(SIDELTA(cpu[CPU_KERNEL])/str_cpu_sum));
    strchgen(bar,'W',(int)(SIDELTA(cpu[CPU_WAIT])/str_cpu_sum));
    strchgen(bar,'>',(int)(SIDELTA(cpu[CPU_USER])/str_cpu_sum));
    strchgen(bar,'.',(int)(SIDELTA(cpu[CPU_IDLE])/str_cpu_sum));
    move(4,0); clrtoeol();
    move(4,0); printw("%s",bar);

    getloadavg(loadv,3);
    move(6,0);
    printw("Runnable processes %5.2lf  load average: %5.2lf, %5.2lf, %5.2lf\n",
	   ((double)SIDELTA(runque)/(double)SIDELTA(runocc))-1.0,
	   loadv[0],loadv[1],loadv[2]);

    if (SIDELTA(swpocc) == 0) swp_proc=0.0;
    else swp_proc=(double)SIDELTA(swpque)/SIDELTA(swpocc);
    printw("Swap-in processes   %4.2f\n",swp_proc);

    x=0;y=9;
    move(y+0,x);printw("Memory    Real     Virtual");
    move(y+1,x);printw("free    %5.1lf MB  %5.1lf MB",
	   vmk->freemem*4/1024.0,vmk->freevmem*4/1024.0);
    move(y+2,x);printw("total   %5.1lf MB  %5.1lf MB",
	   vmk->totalmem*4/1024.0,vmk->totalvmem*4/1024.0);

    x=32;y=8;
    move(y+0,x);printw("Paging (4kB)");
    move(y+1,x);printw("%5.1f pgfaults",VMDELTA(pgexct)/refresh_time);
    move(y+2,x);printw("%5.1f pgin",    VMDELTA(pageouts)/refresh_time);
    move(y+3,x);printw("%5.1f pgout",   VMDELTA(pgspgins)/refresh_time);
    move(y+4,x);printw("%5.1f pgsin",   VMDELTA(pgspgins)/refresh_time);
    move(y+5,x);printw("%5.1f pgsout",  VMDELTA(pgspgouts)/refresh_time);

    x=48;y=8;
    move(y+0,x);printw("Process events");
    move(y+1,x);printw("%5.0f pswitch", SIDELTA(pswitch)/refresh_time);
    move(y+2,x);printw("%5.0f syscall", SIDELTA(syscall)/refresh_time);
    move(y+3,x);printw("%5.0f read",    SIDELTA(sysread)/refresh_time);
    move(y+4,x);printw("%5.0f write",   SIDELTA(syswrite)/refresh_time);
    move(y+5,x);printw("%5.0f fork",    SIDELTA(sysfork)/refresh_time);
    move(y+6,x);printw("%5.0f exec",    SIDELTA(sysexec)/refresh_time);
    move(y+7,x);printw("%5.0f rcvint",  SIDELTA(rcvint)/refresh_time);
    move(y+8,x);printw("%5.0f xmtint",  SIDELTA(xmtint)/refresh_time);
    move(y+9,x);printw("%5.0f mdmint",  SIDELTA(mdmint)/refresh_time);
    
    x=62;y=8;
    move(y+0,x);printw("   File/TTY-IO");
    move(y+1,x);printw("%7d iget",     (int)(SIDELTA(iget)/refresh_time));
    move(y+2,x);printw("%7d namei",    (int)(SIDELTA(namei)/refresh_time));
    move(y+3,x);printw("%7d dirblk",   (int)(SIDELTA(dirblk)/refresh_time));
    move(y+4,x);printw("%7d readch",   (int)(SIDELTA(readch)/refresh_time));
    move(y+5,x);printw("%7d writech",  (int)(SIDELTA(writech)/refresh_time));
    move(y+6,x);printw("%7d ttyrawch", (int)(SIDELTA(rawch)/refresh_time));
    move(y+7,x);printw("%7d ttycanch", (int)(SIDELTA(canch)/refresh_time));
    move(y+8,x);printw("%7d ttyoutch", (int)(SIDELTA(outch)/refresh_time));
}

print_summary(refresh_time,si,si2,vmk,vm,vm2)
double refresh_time;
struct sysinfo *si,*si2;
struct vmker *vmk;
struct vminfo *vm,*vm2;
{
  time_t time1;
  double loadv[3];
  char hostnm[128];
  double cpu_sum;

  /* hostname loadavg date-time */
  /* cpu-states */
  /* memory-states */
  gethostname(hostnm,sizeof(hostnm));
  getloadavg(loadv,3);
  time1=time(0);
  cpu_sum = SIDELTA(cpu[CPU_IDLE]) + SIDELTA(cpu[CPU_USER]) 
      + SIDELTA(cpu[CPU_KERNEL]) + SIDELTA(cpu[CPU_WAIT]);
  cpu_sum = cpu_sum/100.0;
  move(0,0); printw("%s",hostnm);
  move(0,17);printw("load averages: %5.2lf, %5.2lf, %5.2lf",loadv[0],loadv[1],loadv[2]);
  move(0,56);printw(ctime(&time1));
  move(1,0);
  printw("Cpu states:      %4.1lf%% user, %4.1lf%% system, %4.1lf%% wait, %4.1lf%% idle\n",
	   SIDELTA(cpu[CPU_USER])/cpu_sum,
	   SIDELTA(cpu[CPU_KERNEL])/cpu_sum,
	   SIDELTA(cpu[CPU_WAIT])/cpu_sum,
	   SIDELTA(cpu[CPU_IDLE])/cpu_sum);
  printw("Real memory:    %5.1lfM used, %5.1lfM free, %5.1lfM total\n",
	 (vmk->totalmem-vmk->freemem)*4/1024.0, vmk->freemem*4/1024.0,
	 vmk->totalmem*4/1024.0);
  printw("Virtual memory: %5.1lfM used, %5.1lfM free, %5.1lfM total\n",
	 (vmk->totalvmem-vmk->freevmem)*4/1024.0, vmk->freevmem*4/1024.0,
	 vmk->totalvmem*4/1024.0);
  printw("\n");
}

#define DKiDELTA(i,a)  ((dk1[i].a) - (dk2[i].a))

print_dkstat(refresh_time,dk1,dk2)
double refresh_time;
struct dkstat *dk1,*dk2;
{
    int i;
    int x,y;
    
    x=0;y=13;
    move(y+0,x);
    printw("DiskIO   read    write   busy");
    for (i=0; i<dk_cnt; i++) {
	move(y+1+i,x);
	printw("%-7s %5.1f %5.1f kB/s %3.0f%%",
	       dk1[i].diskname,
	       DKiDELTA(i,dk_rblks)*dk1[i].dk_bsize/1024.0/refresh_time,
	       DKiDELTA(i,dk_wblks)*dk1[i].dk_bsize/1024.0/refresh_time,
	       DKiDELTA(i,dk_time)/refresh_time);
    }
}

#define IFDELTA(i,a) ((if1[i]->a) - (if2[i]->a))

print_ifnet(refresh_time,if1,if2)
double refresh_time;
struct ifnet *if1[],*if2[];
{
    int i;
    int x,y;

    i=0;
    x=52;y=19;
    move(y+0,x); printw("Netw  read   write");
    while (if1[i]) {
	move(y+1+i,x);
	printw("%-2s%1d  %5.1f %5.1f kB/s\n\n",
	       if1[i]->if_name,
	       if1[i]->if_unit,
	       IFDELTA(i,ifInOctets)/1024.0/refresh_time,
	       IFDELTA(i,ifOutOctets)/1024.0/refresh_time);
	i++;
    }
}

/* generate 'len' characters 'ch' in the end of string 'str' */
strchgen(str,ch,len)
char *str;
char ch;
int len;
{
  while (*str != 0) str++;
  while (len) { len--; *str++ = ch; }
  *str = 0;
}


/****************************************************************************/
/* get_sys_vm_info(struct sysinfo *si,struct vker *vmk, struct vminfo *vm); */
/* get_dkstat(struct dkstat **dk, int initted);  */
/* get_ifnet(struct ifnet *ifnets[]); */
/****/

get_sys_vm_info(si,vmk,vm)
struct sysinfo *si;
struct vmker *vmk;
struct vminfo *vm;
{
    /*
    ** Get the system info structure from the running kernel.
    ** Get the kernel virtual memory vmker structure
    ** Get the kernel virtual memory info structure
    */
    getkmemdata(si,sizeof(struct sysinfo),N_VALUE(NLIST_SYSINFO));
    getkmemdata(vmk,sizeof(struct vmker),N_VALUE(NLIST_VMKER));
    getkmemdata(vm,sizeof(struct vminfo),N_VALUE(NLIST_VMINFO));
}


get_dkstat(dk,initted)
struct dkstat **dk;
int initted;
{
    struct iostat iostat;
    int i;

    getkmemdata(&iostat,sizeof(struct iostat),N_VALUE(NLIST_IOSTAT));
    if (!initted) {
	dk_cnt = iostat.dk_cnt;
	*dk = malloc(sizeof(struct dkstat)*iostat.dk_cnt);
    }
    getkmemdata(&(*dk)[0],sizeof(struct dkstat), iostat.dkstatp);
    for (i=1; i<dk_cnt; i++) {
	getkmemdata(&(*dk)[i],sizeof(struct dkstat), (*dk)[i-1].dknextp);
    }
}

clear_ifnets(ifnets,indexnum)
struct ifnet *ifnets[];
int indexnum;
{
  int i;

  for (i=0; i<indexnum; i++) {
    ifnets[i] = 0;
  }
}

get_ifnet(ifnets)
struct ifnet *ifnets[]; /* pointer to array of struct ifnet pointers */
{
    static int initted;
    static long ifnet_addr_first;
    static char *ifnets_names[MAX_IFNETS];
    char **ifnames;
    long ifnet_next;

    ifnames = ifnets_names;
    if (!initted) {
	initted=1;
	getkmemdata(&ifnet_addr_first,sizeof(long),N_VALUE(NLIST_IFNET));
    }
    ifnet_next = ifnet_addr_first;

    /* index to ifnets[0]-value is used, (*ifnets) could
       be used instead of it */
    while (ifnet_next) {
	if (!ifnets[0]) ifnets[0] = malloc(sizeof(struct ifnet));
	getkmemdata(ifnets[0],sizeof(struct ifnet),ifnet_next);
	if (!ifnames[0]) ifnames[0]=malloc(MAX_IPNAME);
	getkmemdata(ifnames[0],sizeof(MAX_IPNAME), ifnets[0]->if_name);
	ifnets[0]->if_name = ifnames[0];
	ifnet_next = ifnets[0]->if_next;
	ifnets++;  /* lets increment ifnets ** so that index 0 is always 
		      first one in list */
	ifnames++; /* same for ipnames */
    }
}


/*********************************************************************/
getkmemdata(buf,bufsize,n_value)
char *buf;      /* buffer for kernel data*/
int bufsize;    /* buffers size */
long n_value;    /* seek address to kernels data */
{
    static int fd;
    static initted = 0;
    /*
    ** Do stuff we only need to do once per invocation, like opening
    ** the kmem file and fetching the parts of the symbol table.
    */
    if (!initted) {
	initted = 1;
	fd = open("/dev/kmem", O_RDONLY);
	if (fd < 0) {
	    perror("kmem");
	    exit(1);
	}
    }
    /*
    ** Get the structure from the running kernel.
    */
    lseek(fd, n_value, SEEK_SET);
    read(fd, buf, bufsize);
}

/******* enf of monitor.c **********************************/
SHAR_EOF
if test 16629 -ne "`wc -c < 'monitor.c'`"
then
	echo shar: error transmitting "'monitor.c'" '(should have been 16629 characters)'
fi
fi # end of overwriting check
if test -f 'testgetloadavg.c'
then
	echo shar: will not over-write existing file "'testgetloadavg.c'"
else
cat << \SHAR_EOF > 'testgetloadavg.c'
main()
{
    int i;
    double l1,l2,l3;

    for (i=0; i<100; i++) {
	getloadavg(&l1,&l2,&l3);
	printf("loadavg %f %f %f\n",l1,l2,l3);
	sleep(2);
    }
}
SHAR_EOF
if test 155 -ne "`wc -c < 'testgetloadavg.c'`"
then
	echo shar: error transmitting "'testgetloadavg.c'" '(should have been 155 characters)'
fi
fi # end of overwriting check
if test -f 'top.c'
then
	echo shar: will not over-write existing file "'top.c'"
else
cat << \SHAR_EOF > 'top.c'
/* top.c -- top-like program (subroutines for monitor-program 
** Copyright (c) 1991 Jussi Maki. All Rights Reserved.
** NON-COMMERCIAL USE ALLOWED. YOU ARE FREE TO DISTRIBUTE
** THIS PROGRAM AND MODIFY IT AS LONG AS YOU KEEP ORIGINAL
** COPYRIGHTS.
**/

#include <procinfo.h>
#include <curses.h>
#include <pwd.h>

#include "getproc.h"
#include "top.h"


/* memory allocation doesn't really happen until
 * memory is touched so these large arrays don't 
 * waste memory in AIX 3.1
 */
struct procinfo top_proc1[NPROCS];
struct userinfo top_user1[NPROCS];
struct procinfo top_proc2[NPROCS];
struct userinfo top_user2[NPROCS];
struct procsortinfo top_sortinfo[NPROCS];

/* array of chars which contains process statussymbols */
char *statch[] = {"?","sleep","?","run","T","zombie","sleep"};


#define SLEEPTIME 2

#ifdef TOP_MAIN

sighandler(signum)
int signum;
{
  switch (signum) {
  case SIGTSTP:
    endwin();
    kill(getpid(),SIGSTOP);
    initscr();
    clear();
    break;
  case SIGINT:
    endwin();
    exit(0);
    break;
  default: fprintf(stderr,"unknown signal %d\n",signal); fflush(stderr);
  }
  signal(signum,sighandler);
}


main()
{
  int result;
  int i;
  int top_nproc1,top_nproc2;
  double top_cpusum;
  int nproctoprint=15;

  signal(SIGTSTP,sighandler);
  signal(SIGINT,sighandler);

  initscr();
  clear();

  top_nproc1 = top_getprocinfo(top_proc1,top_user1);
  sleep(SLEEPTIME);
  while (1) {
    top_nproc2 = top_getprocinfo(top_proc2,top_user2);
    top_cpusum = top_calcsortinfo(top_proc1,top_user1,top_nproc1,
				  top_proc2,top_user2,top_nproc2,top_sortinfo);
    top_print(nproctoprint,top_proc2,top_user2,top_sortinfo,top_nproc2,top_cpusum);
    sleep(SLEEPTIME);

    top_nproc1 = top_getprocinfo(top_proc1,top_user1);
    top_cpusum = top_calcsortinfo(top_proc2,top_user2,top_nproc2,
				  top_proc1,top_user1,top_nproc1,top_sortinfo);
    top_print(nproctoprint,top_proc1,top_user1,top_sortinfo,top_nproc1,top_cpusum);
    sleep(SLEEPTIME);
  }
  endwin();
}

#endif /* TOP_MAIN */

void top_print(topproc,proc,user,procsortinfo,nproc,cpusum)
int topproc;
struct procinfo *proc;
struct userinfo *user;
struct procsortinfo *procsortinfo; 
int nproc;
double cpusum;
{
  int i,j;
  char username[32];
  struct passwd *passwd;

#ifdef TOP_MAIN
  move(2,0);
#endif
  printw("   PID USER     PRI NICE   SIZE     RES STAT      TIME   CPU%% COMMAND\n");
  for (j=0; j<min(topproc,nproc); j++) {
    i = procsortinfo[j].index;
    passwd = getpwuid(proc[i].pi_uid);
    strcpy(username,passwd->pw_name);
    printw("%6d %-8s %3d %3d %6dK %6dK %-6s %4d:%02d %5.1f%% %s",
	   proc[i].pi_pid,	   username,
	   proc[i].pi_pri,	   proc[i].pi_nice-20,
	   user[i].ui_tsize/1024+user[i].ui_dvm*4, /* SIZE */
	   (user[i].ui_drss+user[i].ui_trss)*4, /* RES, pagesize is 4kB */
	   statch[proc[i].pi_stat],
	   (int)procsortinfo[j].cputime/60,((int)procsortinfo[j].cputime)%60,
	   procsortinfo[j].deltacputime/cpusum*100.0,
	   user[i].ui_comm);
    if (proc[i].pi_flag&SKPROC) printw(" (kproc)");
    printw("\n");
  }
  refresh();
}

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

/* sort-order by deltacputime, second key is cputime */
int cmp_deltacputime(a,b)
struct procsortinfo *a,*b;
{
  if (a->deltacputime > b->deltacputime) return (-1);
  else if (a->deltacputime < b->deltacputime) return ( 1);
  else if (a->cputime > b->cputime) return (-1);
  else if (a->cputime < b->cputime) return ( 1);
  else return(0);
}

/* lets assume that procinfo-array is kept in order by PID */
double top_calcsortinfo(proc1,user1,nproc1,proc2,user2,nproc2,procsortinfo)
struct procinfo *proc1; /* older values */
struct userinfo *user1;
int nproc1;
struct procinfo *proc2; /* newer values */
struct userinfo *user2;
int nproc2;
struct procsortinfo *procsortinfo;
{
  int i,j;
  double cpusum=0.0;
  /* Oops,this must be changed in 1.04 (non-beta) :-)
   * I mean that to check if some processes are deletet between two samples
   */
  for (i=0; i<nproc2; i++) { 
    if (proc2[i].pi_pid == proc1[i].pi_pid) { 
      j=i;
      procsortinfo[i].cputime =  user2[i].ui_ru.ru_utime.tv_sec
	+ user2[i].ui_ru.ru_utime.tv_usec*1.0e-6
	+ user2[i].ui_ru.ru_stime.tv_sec
	+ user2[i].ui_ru.ru_stime.tv_usec*1.0e-6;
      procsortinfo[i].deltacputime = procsortinfo[i].cputime 
	- (user1[j].ui_ru.ru_utime.tv_sec
	   + user1[j].ui_ru.ru_utime.tv_usec*1.0e-6
	   + user1[j].ui_ru.ru_stime.tv_sec
	   + user1[j].ui_ru.ru_stime.tv_usec*1.0e-6);
      procsortinfo[i].index = i;
      cpusum += procsortinfo[i].deltacputime;
    }
  }

  qsort(procsortinfo,nproc2,sizeof(struct procsortinfo),cmp_deltacputime);
  return(cpusum);
}

int top_getprocinfo(procinfo,userinfo)
struct procinfo *procinfo;
struct userinfo *userinfo;
{
  int nproc;
  char *swappername = "swapper";
  int i;

  nproc = getproc(procinfo,NPROCS,sizeof(struct procinfo));
  for (i=0; i<nproc; i++) {
    getuser(&procinfo[i],sizeof(struct procinfo),
	    &userinfo[i],sizeof(struct userinfo));
  }
  strcpy(userinfo[0].ui_comm,swappername); /* first process is always pid 0 */
  return(nproc);
}
SHAR_EOF
if test 5098 -ne "`wc -c < 'top.c'`"
then
	echo shar: error transmitting "'top.c'" '(should have been 5098 characters)'
fi
fi # end of overwriting check
if test -f 'top.h'
then
	echo shar: will not over-write existing file "'top.h'"
else
cat << \SHAR_EOF > 'top.h'
/* top.h  -- include file for top-like top.c program (used with monitor-program)
 */

#define min(a,b) ((a)<(b)?(a):(b))
#define NPROCS 10000

struct procsortinfo {
     int index; /* index to previous procinfo-array */
     double deltacputime;
     double cputime;
   };

extern struct procinfo top_proc1[];
extern struct userinfo top_user1[];
extern struct procinfo top_proc2[];
extern struct userinfo top_user2[];
extern struct procsortinfo top_sortinfo[];


extern int top_getprocinfo();
extern double top_calcsortinfo();
extern void top_print();
SHAR_EOF
if test 552 -ne "`wc -c < 'top.h'`"
then
	echo shar: error transmitting "'top.h'" '(should have been 552 characters)'
fi
fi # end of overwriting check
if test -f 'uptime.c'
then
	echo shar: will not over-write existing file "'uptime.c'"
else
cat << \SHAR_EOF > 'uptime.c'
/*
 * Implemention of uptime to aix3.1 with using loadavgd to
 * get real load-values. 
 * Jussi M{ki, Helsinki Univ. of Technology, Computing Centre
 * jmaki@hut.fi, 3.7.1991
 */

#include <sys/types.h>
#include <sys/times.h>
#include <sys/param.h>
#include <time.h>
#include <utmp.h>
#include <stdio.h>

#define UTMP_FILENAME "/etc/utmp"

main()
{
  int nusers;
  struct tms tbuf;
  struct tm *tm;
  time_t uptime;
  time_t timeofday;
  int days,hours,minutes;
  double loads[3];

  uptime = (times(&tbuf) / HZ);
  timeofday = time((long *)0);
  tm = localtime(&timeofday);

  nusers = getnusers();
  getloadavg(loads,3);

  convsectime(uptime,&days,&hours,&minutes);
  printf(" %2d:%02d%s  up %2d days,  %2d:%2d,  %d users,  load average: %5.2f,%5.2f,%5.2f\n",
	 tm->tm_hour,tm->tm_min,
	 (tm->tm_hour<12)? "am" : "pm",
	 days,hours,minutes,
	 nusers,
	 loads[0],loads[1],loads[2]);
}

int getnusers()
{
  FILE *utmpf;
  int nusers;
  struct utmp utmp;
  
  nusers=0;
  if (!(utmpf = fopen(UTMP_FILENAME, "r"))) {
    perror(UTMP_FILENAME);
    exit(1);
  }
  while (fread((char *)&utmp,sizeof(utmp),1,utmpf) == 1)
    if (*utmp.ut_name && utmp.ut_type==USER_PROCESS) 
      nusers++;
  return (nusers);
}


#define	MINUTES	60
#define HOURS	(MINUTES * 60)
#define DAYS	(HOURS * 24)

convsectime(secs,days,hours,minutes)
time_t secs;
int *days,*hours,*minutes;
{
	secs -= (*days = secs / DAYS) * DAYS;
	secs -= (*hours = secs / HOURS) * HOURS;
	secs -= (*minutes = secs / MINUTES) * MINUTES;
}
SHAR_EOF
if test 1496 -ne "`wc -c < 'uptime.c'`"
then
	echo shar: error transmitting "'uptime.c'" '(should have been 1496 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0

exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.


