/************************************************************/
/*                                                          */
/* Module ID  - monstat.                                    */
/*                                                          */
/* Function   - Generate monitor data for the Linux system. */
/*                                                          */
/* Called By  - N/A.                                        */
/*                                                          */
/* Calling To - N/A.                                        */
/*                                                          */
/* Parameters - None.                                       */
/*                                                          */
/* Notes      - (1) Uses /dev/cpmon to communicate.         */
/*                                                          */
/*              (2) ....................................... */
/*                                                          */
/*                                                          */
/* Name       - Neale Ferguson.                             */
/*                                                          */
/* Date       - April, 2000.                                */
/*                                                          */
/*                                                          */
/* Associated    - (1) Refer To ........................... */
/* Documentation                                            */
/*                 (2) Refer To ........................... */
/*                                                          */
/* Based on code found in vmstat.c:                         */
/*    Copyright 1994 by Henry Ware <al172@yfn.ysu.edu>.     */
/*    Copyleft same year.                                   */
/*                                                          */
/************************************************************/

/************************************************************/
/*                                                          */
/*                     DEFINES                              */
/*                     -------                              */
/*                                                          */
/************************************************************/

#define MONDEV  "/dev/cpmon"
#define VERSION Version: 1.00, last modified 20 April 2000
#define PROGNAME "monstat"
#define NDEBUG !DEBUG

#define BUFFSIZE 1024
#define FALSE 0
#define TRUE 1

#ifdef __s390x__
# define APPLMON_START_REC 0x80
# define APPLMON_STOP_REC  0x81
# define APPLMON_GEN_EVENT 0x82
# define APPLMON_START_CON 0x83
# define APPLMON_PUT_SAMP  0x84
#else
# define APPLMON_START_REC 0x00
# define APPLMON_STOP_REC  0x01
# define APPLMON_GEN_EVENT 0x02
# define APPLMON_START_CON 0x03
# define APPLMON_PUT_SAMP  0x04
#endif

/* Use 'l' as magic number */
#define APPLMON_IOC_MAGIC  'l'

#define APPLMON_IOCRESET    _IO(APPLMON_IOC_MAGIC, 0)
#define MONSETPROD          _IOW(APPLMON_IOC_MAGIC, 1, char *)

/*=============== End of Defines ===========================*/

/************************************************************/
/*                                                          */
/*              INCLUDE STATEMENTS                          */
/*              ------------------                          */
/*                                                          */
/************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include "proc/sysinfo.h"
#include "proc/version.h"
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/dir.h>
#include <dirent.h>

/*================== End of Include Statements =============*/

/************************************************************/
/*                                                          */
/*              TYPE DEFINITIONS                            */
/*              ----------------                            */
/*                                                          */
/************************************************************/

/*================== End of Type Definitions ===============*/

/************************************************************/
/*                                                          */
/*             FUNCTION PROTOTYPES                          */
/*             -------------------                          */
/*                                                          */
/************************************************************/

int main(int, char **);
void crash(char *);
void getstat(unsigned *, unsigned *, unsigned *, unsigned long *,
	     unsigned *, unsigned *, unsigned *, unsigned *,
	     unsigned *, unsigned *, unsigned *);
void getmeminfo(unsigned *, unsigned *, unsigned *, unsigned *);
void getrunners(unsigned *, unsigned *, unsigned *);

/*================== End of Prototypes =====================*/

/************************************************************/
/*                                                          */
/*           GLOBAL VARIABLE DECLARATIONS                   */
/*           ----------------------------                   */
/*                                                          */
/************************************************************/

int linux_version_code = LINUX_VERSION(2, 0, 0);
static char buff[BUFFSIZE];

/*============== End of Variable Declarations ==============*/

/************************************************************/
/*                                                          */
/*                 M A I N L I N E                          */
/*                 ---------------                          */
/************************************************************/

int
main(int argc, char **argv)
{
	int fd, l_monitor;
	const char format[] = "~%2u %2u %2u %6u %6u %6u %6u %3u "
	    "%3u %5u %5u %4u %5u %3u %3u %3u~";
	unsigned int i, hz;
	unsigned int running, blocked, swapped;
	unsigned int cpu_use, cpu_nic, cpu_sys;
	unsigned int duse, dsys, didl, div, divo2;
	unsigned int memfree, membuff, swapused, memcache;
	unsigned long cpu_idl;
	unsigned int pgpgin, pgpgout, pswpin, pswpout;
	unsigned int inter, ticks, ctxt;
	unsigned int per = 0;
	unsigned long num = 0;
	struct {
		char fcn;
		char buffer[512];
	} mon;
	char *product_id = "MONSTATDRIVER000";

	fd = open(MONDEV, O_RDWR);
	if (fd < 0)
		crash(MONDEV);
	mon.fcn = APPLMON_GEN_EVENT;
	ioctl(fd, MONSETPROD, product_id);
	per = 10;

	getrunners(&running, &blocked, &swapped);
	getmeminfo(&memfree, &membuff, &swapused, &memcache);
	getstat(&cpu_use, &cpu_nic, &cpu_sys, &cpu_idl,
		&pgpgin, &pgpgout, &pswpin, &pswpout, &inter, &ticks, &ctxt);
	duse = cpu_use + cpu_nic;
	dsys = cpu_sys;
	didl = cpu_idl % UINT_MAX;
	div = (duse + dsys + didl);
	hz = sysconf(_SC_CLK_TCK);	/* get ticks/s from system */
	divo2 = div / 2;
	sprintf(mon.buffer, format,
		running, blocked, swapped,
		swapused, memfree, membuff, memcache,
		(pswpin * 4 * hz + divo2) / div,
		(pswpout * 4 * hz + divo2) / div,
		(pgpgin * hz + divo2) / div,
		(pgpgout * hz + divo2) / div,
		(inter * hz + divo2) / div,
		(ctxt * hz + divo2) / div,
		(100 * duse + divo2) / div,
		(100 * dsys + divo2) / div, (100 * didl + divo2) / div);
	l_monitor = strlen(mon.buffer);
	if (write(fd, (char *) &mon, l_monitor) < 0)
		crash(MONDEV);

    /*-----------------------------------------------------------*/
	/* Main loop...                                              */
    /*-----------------------------------------------------------*/
	for (; i == i;) {
		sleep(per);
		getmeminfo(&memfree, &membuff, &swapused, &memcache);
		getstat(&cpu_use, &cpu_nic, &cpu_sys, &cpu_idl,
			&pgpgin, &pgpgout, &pswpin, &pswpout,
			&inter, &ticks, &ctxt);
		duse = cpu_use + cpu_nic;
		dsys = cpu_sys;
		didl = cpu_idl % UINT_MAX;
		div = (duse + dsys + didl);
		divo2 = div / 2;
		sprintf(mon.buffer, format,
			running, blocked, swapped,
			swapused, memfree, membuff, memcache,
			(pswpin * 4 * hz + divo2) / div,
			(pswpout * 4 * hz + divo2) / div,
			(pgpgin * hz + divo2) / div,
			(pgpgout * hz + divo2) / div,
			(inter * hz + divo2) / div,
			(ctxt * hz + divo2) / div,
			(100 * duse + divo2) / div,
			(100 * dsys + divo2) / div, (100 * didl + divo2) / div);
		l_monitor = strlen(mon.buffer);
		if (write(fd, (char *) &mon, l_monitor) < 0)
			crash(MONDEV);
	}
	if (close(fd) < 0)
		crash(MONDEV);
	exit(EXIT_SUCCESS);
}

/*===================== End of Function ====================*/

/************************************************************/
/*                                                          */
/* Name     -                                               */
/*                                                          */
/* Function -                                               */
/*                                                          */
/************************************************************/

void
crash(char *filename)
{
	perror(filename);
	exit(EXIT_FAILURE);
}

/*===================== End of Function ====================*/

/************************************************************/
/*                                                          */
/* Name     -                                               */
/*                                                          */
/* Function -                                               */
/*                                                          */
/************************************************************/

void
getstat(unsigned *cuse, unsigned *cice, unsigned *csys,
	unsigned long *cide, unsigned *pin, unsigned *pout,
	unsigned *sin, unsigned *sout,
	unsigned *itot, unsigned *i1, unsigned *ct)
{
	static int stat;
	char *b;

	if ((stat = open("/proc/stat", O_RDONLY, 0)) != -1) {
		buff[BUFFSIZE - 1] = 0;	/* ensure null termination in buffer */
		read(stat, buff, BUFFSIZE - 1);
		close(stat);
		*itot = 0;
		*i1 = 1;	/* ensure assert below will fail if the sscanf bombs */
		b = strstr(buff, "cpu ");
		sscanf(b, "cpu  %u %u %u %lu", cuse, cice, csys, cide);
		b = strstr(buff, "page ");
		sscanf(b, "page %u %u", pin, pout);
		b = strstr(buff, "swap ");
		sscanf(b, "swap %u %u", sin, sout);
		b = strstr(buff, "intr ");
		sscanf(b, "intr %u %u", itot, i1);
		b = strstr(buff, "ctxt ");
		sscanf(b, "ctxt %u", ct);
		assert(*itot > *i1);
	} else {
		crash("/proc/stat");
	}
}

/*===================== End of Function ====================*/

/************************************************************/
/*                                                          */
/* Name     -                                               */
/*                                                          */
/* Function -                                               */
/*                                                          */
/************************************************************/

void
getmeminfo(unsigned *memfree, unsigned *membuff,
	   unsigned *swapused, unsigned *memcache)
{
	unsigned **mem;

	if (!(mem = meminfo()))
		crash("/proc/meminfo");

	*memfree = mem[meminfo_main][meminfo_free] >> 10;
	*membuff = mem[meminfo_main][meminfo_buffers] >> 10;
	*swapused = mem[meminfo_swap][meminfo_used] >> 10;
	*memcache = mem[meminfo_main][meminfo_cached] >> 10;
}

/*===================== End of Function ====================*/

/************************************************************/
/*                                                          */
/* Name     -                                               */
/*                                                          */
/* Function -                                               */
/*                                                          */
/************************************************************/

void
getrunners(unsigned int *running, unsigned int *blocked, unsigned int *swapped)
{
	static struct direct *ent;
	static char filename[80];
	static int fd;
	static unsigned size;
	static char c;
	DIR *proc;

	*running = 0;
	*blocked = 0;
	*swapped = 0;

	if ((proc = opendir("/proc")) == NULL)
		crash("/proc");

	while ((ent = readdir(proc))) {
		if (isdigit(ent->d_name[0])) {
			sprintf(filename, "/proc/%s/stat", ent->d_name);
			if ((fd = open(filename, O_RDONLY, 0)) != -1) {
				read(fd, buff, BUFFSIZE - 1);
				sscanf(buff,
				       "%*d %*s %c %*d %*d %*d %*d %*d %*u "
				       "%*u %*u %*u %*u %*d %*d %*d %*d "
				       "%*d %*d %*u %*u %*d %*u %u %*u "
				       "%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u\n",
				       &c, &size);
				close(fd);

				if (c == 'R') {
					if (size > 0)
						(*running)++;
					else
						(*swapped)++;
				} else {
					if (c == 'D') {
						if (size > 0)
							(*blocked)++;
						else
							(*swapped)++;
					}
				}
			}
		}
	}
	closedir(proc);

	(*running)--;
}

/*===================== End of Function ====================*/
