/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident "$Header: monitor.c 1.7 89/08/09 $"

/*
 * monitor.c
 *
 * This program will display on a dumb terminal various performance metrics
 */

#include <sys/tmp_ctl.h>

#include <sys/types.h>
#include <sys/vm.h>
#include <sys/buf.h>
#include <sys/stats_disk.h>
#include <sys/fcntl.h>
#include <sys/flock.h>
#include <stdio.h>
#include <curses.h>
#include "monitor.h"
#include "sslib.h"

extern char *malloc();

/*
 * For getopt
 */
extern char *optarg;
extern int optind;

/*
 * global argv[0] (for error messages)
 */
char *whoiam;

/*
 * Flags for options settable from command line
 */
int interval, processor, p_cols, flip, terse;
/*
 * Saved termio state
 */
struct termio	mytty;
/*
 * Other Global parameters
 */
int p_lines;			/* # of lines of processor bars */
/*
 * Global statistic data structures
 * elements of sstat have hooks into these
 */
struct disk_summary disk_sum;
struct vmmeter rate;	/* rate over last interval */
struct procstate ps;
ulong	pt_user;		/* total user time */
ulong	pt_sys;			/* total sys time */
ulong	pt_total;		/* total time */
ulong	fsreadhit, fswritehit; /* File Sys hit ratios */
double avenrun[3];		/* Load averages */
unsigned deficit;		/* deficit in K */
ulong	pgin, pgpgin;		/* converted from vmmeter */
ulong	pgout, pgpgout;		/* converted from vmmeter */
ulong	freemem, dirtymem;	/* converted from memory stats */
ulong	totalvm;		/* totalvm in K (from memory stats) */
struct flckinfo flckinfo;	/* Raw sampled file/record locking stats */
struct flock_stats fl_stats;	/* File/Record locking stats */
WINDOW *bar_win, *stat_win, *on_win;
char *exitmsg;

main(argc, argv)
	int	argc;
	char	*argv[];
{
	struct vmmeter *sd[2];
	struct disk_stats *ds[2];	/* ptrs to two samples */
	struct disk_stats *ds_rate;	/* ptr to data for the interval */
	struct vmmeter syst[2];		/* totals from per processor data */
	struct mem_stats memstats[2];
	struct ss_cfg sc;
	int i;
	ulong	*procutil;
	int old, new;
	int c, err = 0;

	whoiam = argv[0];
	interval = 1;		/* default to 1 sec intervals */
	processor = -1;		/* default to no affinity */
	flip = 0;		/* default to no flip between screens */
	terse = 0;		/* default to verbose text */
	p_cols = 0;		/* # of cols of processors not forced */

	while ((c = getopt(argc, argv, "c:fi:p:t")) != EOF) {
		switch (c) {
		case 'c':
			p_cols = atoi(optarg);
			if (p_cols < 1 || p_cols > MAX_PROC_COLS) {
				fprintf(stderr, "%s: bad # of columns: %s\n",
					whoiam, optind);
				err++;
			}
			break;
		case 'f':
			flip++;
			break;
		case 'i':
			interval = atoi(optarg);
			if (interval < 0) {
				fprintf(stderr, "%s: invalid interval: %s\n",
					whoiam, optind);
				err++;
			}
			break;
		case 'p':
			processor = atoi(optarg);
			break;
		case 't':
			terse++;
			break;
		case '?':
			err++;
			break;
		}
	}
	if (err || optind < argc) {
		fprintf(stderr,
	   "Usage: %s  [-t] [-f] [-c columns] [-i interval] [-p processor]\n",
			whoiam);
		exit(1);
	}
	/*
	 * Attempt to bind to a processor if -p option specified
	 */
	if (processor != -1)
		(void) tmp_affinity(processor);
	/*
	 * initialize the screen and setup for data gathering
	 */
	get_sscfg(&sc);
	setup(&sc);
	procutil = (ulong *) malloc(sc.Nengine * sizeof(ulong) * V_CPU_STATES);
	ds_rate = (struct disk_stats *)
		malloc(sc.ndisks * sizeof(struct disk_stats));
	if (procutil == (ulong *)NULL ||
	    ds_rate == (struct disk_stats *)NULL ) {
		exitmsg = "Memory allocation failure";
		die(1);
	}
	for (i = 0; i < 2; i++) {
		sd[i] = (struct vmmeter *)
				malloc(sc.Nengine * sizeof(struct vmmeter));
		ds[i] = (struct disk_stats *)
				malloc(sc.ndisks * sizeof(struct disk_stats));
		if (sd[i] == (struct vmmeter *)NULL ||
		    ds[i] == (struct disk_stats *)NULL ) {
			exitmsg = "Memory allocation failure";
			die(1);
		}
	}

	/* moved  to the end of the mallocs because mmap will not move curbrk */
	mapmem();

	/* 
	 * Loop forever sampling and updating the screen
	 */

	old = 0;
	new = 1;
	ss_sample(&sc, sd[old], ds[old], &ps, &memstats[old],
		avenrun, &deficit, &flckinfo);
	ss_sum(&sc, sd[old], &syst[old]);
	while (1) {
		if (interval)
			sleep((unsigned)interval);
		ss_sample(&sc, sd[new], ds[new], &ps, &memstats[new],
			avenrun, &deficit, &flckinfo);
		ss_sum(&sc, sd[new], &syst[new]);
		munge(&sc, &syst[old], &syst[new], &rate,
			ds[old], ds[new], ds_rate, &disk_sum,
			&memstats[old], &memstats[new],
			&flckinfo, &fl_stats);
		perprocrate(&sc, sd[old], sd[new], procutil);
		display(&sc, &rate, procutil, avenrun);
		chars_typed(&sc);
		old = new;
		new = !new;
	}
}

int
chars_typed(sc)
	struct ss_cfg *sc;
{
	int readret;
	int c;

	/* see if anything has been typed at the terminal */
	readret = 0;
	readret = read(0, &c, 1);

	if (readret != 0) {
		switch (c&0x7f) {
		case '\n':
		case '\r':
		case 'q':
			die(0);
		case 'f':
			wclear(on_win);
			wrefresh(on_win);
			if (on_win == bar_win)
				on_win = stat_win;
			else
				on_win = bar_win;
			draw_screen(sc);
			wrefresh(on_win);
			break;
		case '\014':
			wclear(on_win);
			wrefresh(on_win);
			draw_screen(sc);
			wrefresh(on_win);
			break;
		}
	}
}

die(status)
	int status;
{
	if (stdscr != (WINDOW *)NULL) {
		erase();
		refresh();
	}
	endwin();
	if (exitmsg != (char *)NULL)
		fprintf(stderr, "%s: %s\n", whoiam, exitmsg);
	exit(status);
}
