/*
** ATOP - System & Process Monitor
**
** The program 'atop'/'atopsar' offers the possibility to view the activity of 
** the system on system-level as well as process-level.
**
** This source-file contains the 'atopsar'-functionality, that makes use
** of the 'atop'-framework.
** ==========================================================================
** Author:      Gerlof Langeveld
** E-mail:      gerlof.langeveld@atoptool.nl
** Date:        July 2007
** --------------------------------------------------------------------------
** Copyright (C) 2007-2010 Gerlof Langeveld
**
** This program is free software; you can redistribute it and/or modify it
** under the terms of the GNU General Public License as published by the
** Free Software Foundation; either version 2, or (at your option) any
** later version.
**
** This program is distributed in the hope that it will be useful, but
** WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
** See the GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
** --------------------------------------------------------------------------
**
*/

static const char rcsid[] = "$Id: atopsar.c,v 1.27 2010/10/23 14:16:57 gerlof Exp $";

#include <sys/types.h>
#include <sys/param.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/utsname.h>
#include <string.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <regex.h>
#include <sys/ioctl.h>

#include "atop.h"
#include "ifprop.h"
#include "photosyst.h"
#include "photoproc.h"

#define	MAXFL	64      /* maximum number of command-line flags  */


/*
** color definitions
*/
#define	COLSETHEAD 	"\033[30;43m"	/* black on yellow	*/
#define	COLSETMED  	"\033[36m"	/* cyan			*/
#define	COLSETHIGH   	"\033[31m"	/* red			*/
#define	COLRESET    	"\033[00m"	/* reset any color	*/

/*
** miscellaneous values
*/
static unsigned int 	nsamples = 9999999;
static char		stampalways;
static char		usecolors = 't';
static char		usemarkers;
static char		allresources;
static int		numreports;
static unsigned int	repeathead = 9999999;
static unsigned int	summarycnt = 1;
static char		*datemsg = "-------------------------- analysis "
			           "date: %s --------------------------\n";

/*
** structure definition for print-functions
*/
struct pridef { 
	char    wanted;         /* selected option (boolean)              */
	char    *cntcat;        /* used categories of counters            */
	char    flag;           /* flag on command line                   */
	void    (*prihead)();   /* print header of list                   */
	int     (*priline)();   /* print counters per line (excl. time)   */
	char    *about;         /* statistics about what                  */
};

extern struct pridef	pridef[];      /* table of print-functions        */
extern int		pricnt;	       /* total number of print-functions */

static time_t		daylim;        /* last second of day in epoch     */
static int		prinow;        /* current selection               */
static char 		coloron;       /* boolean: colors active now      */

/*
** local prototypes
*/
static void	engine(void);
static void	pratopsaruse(char *);
static void	reportlive(time_t, int, struct sstat *);
static char	reportraw (time_t, int, struct sstat *, struct pstat *,
				int, int, int, int, int, int, int, char);
static void	reportheader(struct utsname *, time_t);
static time_t	daylimit(time_t);

int
atopsar(int argc, char *argv[])
{
	register int	i, c;
	struct rlimit	rlim;
	char		*p, *flaglist;

	/* 
	** interpret command-line arguments & flags 
	*/
	if (argc > 1)
	{
		/* 
		** gather all flags for the print-functions
		*/
		flaglist = malloc(pricnt+32);

		for (i=0; i < pricnt; i++)
			flaglist[i] = pridef[i].flag;

		flaglist[i] = 0;

		/*
		** add generic flags
		*/
		strcat(flaglist, "b:e:SxCMHr:R:aA");

		while ((c=getopt(argc, argv, flaglist)) != EOF)
		{
			switch (c)
			{
			   case '?':		/* usage wanted ?        */
				pratopsaruse(argv[0]);
				break;

                           case 'b':		/* begin time ?          */
				if ( !hhmm2secs(optarg, &begintime) )
					pratopsaruse(argv[0]);
				break;

                           case 'e':		/* end   time ?          */
				if ( !hhmm2secs(optarg, &endtime) )
					pratopsaruse(argv[0]);
				break;

			   case 'r':		/* reading of file data ? */
				strncpy(rawname, optarg, RAWNAMESZ-1);
				rawreadflag++;
				break;

			   case 'R':		/* summarize samples */
				if (!numeric(optarg))
					pratopsaruse(argv[0]);

				summarycnt = atoi(optarg);

				if (summarycnt < 1)
					pratopsaruse(argv[0]);
				break;

			   case 'S':		/* timestamp on every line */
				stampalways = 1;
				break;

			   case 'x':		/* never use colors        */
				usecolors = 0;
				break;

			   case 'C':		/* always use colors       */
				usecolors = 'a';
				break;

			   case 'M':		/* markers for overload    */
				usemarkers = 1;
				break;

			   case 'H':		/* repeat headers          */
				repeathead = 23;	/* define default  */

				if (isatty(1))
				{
					struct winsize wsz;

					if ( ioctl(1, TIOCGWINSZ, &wsz) != -1)
						repeathead = wsz.ws_row - 1;
				}
				break;

			   case 'a':		/* every interval all units */
				allresources = 1;
				break;

			   case 'A':		/* all reports wanted ?  */
				for (i=0; i < pricnt; i++)
					pridef[i].wanted = 1;

				numreports = pricnt;
				break;

			   default:		/* gather report-flags    */
				for (i=0; i < pricnt; i++)
				{
					if (pridef[i].flag   == c && 
					    pridef[i].wanted == 0   )
					{
						pridef[i].wanted = 1;
						numreports++;
						break;
					}
				}

				if (i == pricnt)
					pratopsaruse(argv[0]);
			}
		}

		/*
		** get optional interval-value and
		** optional number of samples	
		*/
		if (optind < argc && optind < MAXFL)
		{
			if (rawreadflag)
				pratopsaruse(argv[0]);

			if (!numeric(argv[optind]))
				pratopsaruse(argv[0]);
	
			interval = atoi(argv[optind]);
	
			optind++;
	
			if (optind < argc)
			{
				if (!numeric(argv[optind]) )
					pratopsaruse(argv[0]);

				if ( (nsamples = atoi(argv[optind])) < 1)
					pratopsaruse(argv[0]);
			}
		}
		else	/* if no interval specified, read from logfile */
		{
			rawreadflag++;
		}
	}
	else	/* if no flags specified at all, read from logfile */
	{
		rawreadflag++;
	}

	/*
	** if no report-flags have been specified, take the first
	** option in the print-list as default
	*/
	if (numreports == 0)
	{
		pridef[0].wanted = 1;
		numreports       = 1;
	}

	/*
	** set stdout output on line-basis
	*/
	setvbuf(stdout, (char *)0, _IOLBF, BUFSIZ);

	/*
	** if only use colors when the output is directed to a tty,
	** be sure that output is directed to a tty
	*/
	if (usecolors == 't')
	{
		if (! isatty(1) )
			usecolors = 0;
	}

	/*
	** check if raw data from a file must be viewed
	*/
	if (rawreadflag)
	{
		/*
		** select own reportraw-function to be called
		** by the rawread function
		*/
		vis.show_samp  = reportraw;

		for (i=0; i < pricnt; i++)
		{
			if ( pridef[i].wanted )
			{
				prinow = i;
				daylim = 0;
				rawread();
				printf("\n");
			}
		}

		cleanstop(0);
	}

	/*
	** live data must be gathered
	**
	** determine the name of this node (without domain-name)
	** and the kernel-version
	*/
	(void) uname(&utsname);

	if ( (p = strchr(utsname.nodename, '.')) )
		*p = '\0';

	sscanf(utsname.release, "%d.%d.%d", &osrel, &osvers, &ossub);

	/*
	** determine the clock rate and memory page size for this machine
	*/
	hertz		= sysconf(_SC_CLK_TCK);
	pagesize	= sysconf(_SC_PAGESIZE);

	/*
	** determine start-time for gathering current statistics
	*/
	curtime = time(0);

        /*
	** regain the root-priviliges that we dropped at the beginning
	** to do some priviliged work now
	*/
        seteuid(0);

	/*
	** lock in memory to get reliable samples (also when
	** memory is low);
	** ignored if not running under superuser privileges!
	*/
	rlim.rlim_cur	= RLIM_INFINITY;
	rlim.rlim_max	= RLIM_INFINITY;
	(void) setrlimit(RLIMIT_MEMLOCK, &rlim);
	(void) mlockall(MCL_CURRENT|MCL_FUTURE);

	/*
	** increment CPU scheduling-priority to get reliable samples (also
	** during heavy CPU load);
	** ignored if not running under superuser privileges!
	*/
	(void) nice(-20);

	/*
	** determine properties (like speed) of all interfaces
	*/
	initifprop();

	/*
	** since privileged activities are finished now, there is no
	** need to keep running under root-privileges, so switch
	** effective user-id to real user-id
	*/
	seteuid ( getuid() );

	/*
	** start live reporting
	*/
	engine();

	return 0;
}

/*
** engine() that drives the main-loop for atopsar
*/
static void
engine(void)
{
	struct sigaction 	sigact;
	void			getusr1(int);

	/*
	** reserve space for system-level statistics
	*/
	static struct sstat	*cursstat; /* current   */
	static struct sstat	*presstat; /* previous  */
	static struct sstat	*devsstat; /* deviation */
	static struct sstat	*hlpsstat;

	/*
	** initialization: allocate required memory dynamically
	*/
	cursstat = calloc(1, sizeof(struct sstat));
	presstat = calloc(1, sizeof(struct sstat));
	devsstat = calloc(1, sizeof(struct sstat));

	/*
	** install the signal-handler for ALARM and SIGUSR1 (both triggers
	** for the next sample)
	*/
	memset(&sigact, 0, sizeof sigact);
	sigact.sa_handler = getusr1;
	sigaction(SIGUSR1, &sigact, (struct sigaction *)0);

	memset(&sigact, 0, sizeof sigact);
	sigact.sa_handler = getalarm;
	sigaction(SIGALRM, &sigact, (struct sigaction *)0);

	if (interval > 0)
		alarm(interval);

	/*
	** print overall report header
	*/
	reportheader(&utsname, time(0));

	/*
	** MAIN-LOOP:
	**    -	Wait for the requested number of seconds or for other trigger
	**
	**    -	System-level counters
	**		get current counters
	**		calculate the differences with the previous sample
	**
	**    -	Call the print-function to visualize the differences
	*/
	for (sampcnt=0; sampcnt < nsamples+1; sampcnt++)
	{
		/*
		** wait for alarm-signal to arrive or
		** wait for SIGUSR1 in case of an interval of 0.
		*/
		if (sampcnt > 0)
			pause();

		/*
		** gather time info for this sample
		*/
		pretime  = curtime;
		curtime  = time(0);		/* seconds since 1-1-1970 */

		/*
		** take a snapshot of the current system-level statistics 
		** and calculate the deviations (i.e. calculate the activity
		** during the last sample)
		*/
		hlpsstat = cursstat;	/* swap current/prev. stats */
		cursstat = presstat;
		presstat = hlpsstat;

		photosyst(cursstat);	/* obtain new counters      */

		deviatsyst(cursstat, presstat, devsstat);

		/*
		** activate the report-function to visualize the deviations
		*/
		reportlive(curtime,
			curtime-pretime > 0 ? curtime-pretime : 1, devsstat);
	} /* end of main-loop */
}

/*
** report function to print a new sample in case of live measurements
*/
static void
reportlive(time_t curtime, int numsecs, struct sstat *ss)
{
	char			timebuf[16], datebuf[16];
	int			i, nr = numreports, rv;
	static unsigned int	curline, headline;

	/*
	** printing more reports needs another way of handling compared
	** to printing one report
	*/
	if (numreports > 1)
	{
		/*
		** skip first sample
		*/
		if (sampcnt == 0)
			return;

		printf(datemsg, convdate(curtime, datebuf));

		for (i=0; i < pricnt && nr > 0; i++)
		{
			if ( !pridef[i].wanted )
				continue;

			nr--;

			/*
			** print header-line
			*/
			if (usecolors)
				printf(COLSETHEAD);

			printf("\n%s  ", convtime(curtime-numsecs, timebuf));
	
			(pridef[i].prihead)(osvers, osrel, ossub);
	
			if (usecolors)
				printf(COLRESET);

			printf("\n");

			/*
			** print line with statistical counters
			*/
			printf("%s  ", convtime(curtime, timebuf));
	
			if ( !(pridef[i].priline)(ss, (struct pstat *)0, 0,
				numsecs, numsecs*hertz, hertz,
				osvers, osrel, ossub,
				stampalways ? timebuf : "        ",
	                        0, 0, 0, 0, 0, 0) )
			{
				/*
				** print line has failed;
				** do not call function again
				*/
				pridef[i].wanted = 0;
	
				if (--numreports == 0)
					cleanstop(1);
			}
		}

		printf("\n");
	}
	else		/* just one report to be printed */
	{
		/*
		** search required report
		*/
		for (i=0; i < pricnt; i++)
			if ( pridef[i].wanted )
				break;

		/*
		** verify if we have passed midnight of some day
		*/
		if (curtime > daylim)
		{
			printf(datemsg, convdate(curtime, datebuf));
			daylim = daylimit(curtime);
			curline++;
		}

		/*
		** print first header
		*/
		if (sampcnt == 0)
		{
			/*
			** print header-line
			*/
			if (usecolors)
				printf(COLSETHEAD);

			printf("\n%s  ", convtime(curtime, timebuf));
	
			(pridef[i].prihead)(osvers, osrel, ossub);

			if (usecolors)
				printf(COLRESET);

			printf("\n");

			curline+=2;

			headline = repeathead;
			return;
		}

		/*
		** print line with statistical counters
		*/
		printf("%s  ", convtime(curtime, timebuf));
	
		if ( !(rv = (pridef[i].priline)(ss, (struct pstat *)0, 0,
					numsecs, numsecs*hertz, hertz,
					osvers, osrel, ossub, 
		                        stampalways ? timebuf : "        ",
	                        	0, 0, 0, 0, 0, 0) ) )
		{
			/*
			** print line has failed;
			** do not call function again
			*/
			cleanstop(1);
		}

		curline+=rv;

		if (curline >= headline)
		{
			headline = curline + repeathead;

			/*
			** print header-line
			*/
			if (usecolors)
				printf(COLSETHEAD);

			printf("\n%s  ", convtime(curtime, timebuf));
	
			(pridef[i].prihead)(osvers, osrel, ossub);

			if (usecolors)
				printf(COLRESET);

			printf("\n");

			curline+=2;
		}
	}
}

/*
** report function to print a new sample in case of logged measurements
*/
static char
reportraw(time_t curtime, int numsecs,
         	struct sstat *ss, struct pstat *ps,
         	int nlist, int npresent, int ntrun, int ntslpi, int ntslpu,
		int nzombie, int nexit, char flags)
{
	static char		firstcall = 1;
	char			timebuf[16], datebuf[16];
	unsigned int		rv;
	static unsigned int	curline, headline, sampsum,
				totalsec, totalexit, lastnpres,
				lastntrun, lastntslpi, lastntslpu, lastnzomb;
	static time_t		lasttime;
	static struct sstat	totsyst;

	/*
	** is this function still wanted?
	*/
	if ( ! pridef[prinow].wanted )
		return '\0';

	/*
	** when this is first call to this function,
	** print overall header with system information
	*/
	if (firstcall)
	{
		reportheader(&utsname, time(0));
		firstcall = 0;
	}

	/*
	** verify if we have passed midnight
	*/
	if (curtime > daylim)
	{
		printf(datemsg, convdate(curtime, datebuf));
		daylim = daylimit(curtime);
		curline++;
	}

	/*
	** when this is the first record for a new report,
	** initialize various variables
	*/
	if (sampcnt == 1)
	{
		/*
		** initialize variables for new report
		*/
		pretime = curtime;

		curline   = 1;
		headline  = 0;

 		sampsum   = summarycnt + 1;
		totalsec  = 0;
		totalexit = 0;
		memset(&totsyst, 0, sizeof totsyst);

		return '\0';
	}

	/*
	** check if a (new) report header needs to be printed
	*/
	if (curline >= headline)
	{
		headline = curline + repeathead;

		/*
		** print header-line
		*/
		if (usecolors)
			printf(COLSETHEAD);

		printf("\n%s  ", convtime(pretime, timebuf));

		(pridef[prinow].prihead)(osvers, osrel, ossub);

		if (usecolors)
			printf(COLRESET);

		printf("\n");

		curline+=2;
	}

	/*
	** when current record contains log-restart indicator,
	** print message and reinitialize variables
	*/
	if (flags & RRBOOT)
	{
		/*
		** when accumulating counters, print results upto
		** the *previous* record
		*/
		if (summarycnt > 1 && sampcnt <= sampsum && totalsec)
		{
			printf("%s  ", convtime(lasttime, timebuf));

			rv = (pridef[prinow].priline)(&totsyst,
				(struct pstat *)0, 0,
				totalsec, totalsec*hertz, hertz,
			        osvers, osrel, ossub,
		                stampalways ? timebuf : "        ",
	                        lastnpres, lastntrun, lastntslpi, lastntslpu,
				totalexit, lastnzomb);

			if (rv == 0)
			{
				curline++;
				pridef[prinow].wanted = 0; /* not call again */

				if (--numreports == 0)
					cleanstop(1);
			}
			else
			{
				curline += rv;
			}
		}

		/*
		** print restart-line in case of logging restarted
		*/
		printf("%s  ", convtime(curtime, timebuf));

		printf("......................... logging restarted "
		       ".........................\n");

		pretime = curtime;
		curline++;

		/*
		** reinitialize variables
		*/
 		sampsum   = summarycnt + sampcnt;
		totalsec  = 0;
		totalexit = 0;
		memset(&totsyst, 0, sizeof totsyst);

		return '\0';
	}

	/*
	** when no accumulation is required,
	** just print current sample
	*/
	if (summarycnt == 1)
	{
		printf("%s  ", convtime(curtime, timebuf));

		rv = (pridef[prinow].priline) (ss, ps, npresent+nexit,
				numsecs, numsecs*hertz, hertz,
				osvers, osrel, ossub,
	               		stampalways ? timebuf : "        ",
				nlist, ntrun, ntslpi, ntslpu, nexit, nzombie);

		if (rv == 0)
		{
			curline++;
			pridef[prinow].wanted = 0; /* not call again */

			if (--numreports == 0)
				cleanstop(1);
		}
		else
		{
			curline += rv;
		}
	}
	else 		/* accumulation is required */
	{
		char  *cp = pridef[prinow].cntcat;

		/*
		** maintain totals per category
		*/
		while (*cp)
		{
			totalsyst(*cp, ss, &totsyst);
			cp++;
		}

		totalsec  += numsecs;
		totalexit += nexit;

		/*
		** remember some values in case the next record
		** contains the log-restart indicator
		*/
		lasttime   = curtime;
		lastnpres  = npresent;
		lastntrun  = ntrun;
		lastntslpi = ntslpi;
		lastntslpu = ntslpu;
		lastnzomb  = nzombie;

		/*
		** print line only if needed
		*/
		if (sampcnt >= sampsum || ( (flags&RRLAST) && totalsec) )
		{
			/*
			** print output line for required report
			*/
			printf("%s  ", convtime(curtime, timebuf));

			rv = (pridef[prinow].priline) (&totsyst,
					(struct pstat *)0, 0,
					totalsec, totalsec*hertz, hertz,
					osvers, osrel, ossub,
					stampalways ? timebuf : "        ",
					nlist, ntrun, ntslpi, ntslpu,
					totalexit, nzombie);

			if (rv == 0)
			{
				curline++;
				pridef[prinow].wanted = 0; /* not call again */

				if (--numreports == 0)
					cleanstop(1);
			}
			else
			{
				curline += rv;
			}

		 	sampsum   = summarycnt + sampcnt;
			totalsec  = 0;
			totalexit = 0;
			memset(&totsyst, 0, sizeof totsyst);
		}
		else
		{
			rv = 1;
		}
	}

	if (!rv)
	{
		/*
		** print for line has failed;
		** never call this function again
		*/
		pridef[prinow].wanted = 0;

		if (--numreports == 0)
			cleanstop(1);
	}

	pretime = curtime;

	return '\0';
}

/*
** print overall header
*/
static void
reportheader(struct utsname *uname, time_t mtime)
{
        char            cdate[16];

        printf("\n%s  %s  %s  %s  %s\n\n",
                uname->nodename,
                uname->release,
                uname->version,
                uname->machine,
        	convdate(mtime, cdate));
}

/*
** print usage of atopsar command
*/
void
pratopsaruse(char *myname)
{
	int	i;

	fprintf(stderr,
		"Usage: %s [-flags] [-r file|date] [-R cnt] [-b hh:mm] [-e hh:mm]\n",
								myname);
	fprintf(stderr, "\t\tor\n");
	fprintf(stderr,
		"Usage: %s [-flags] interval [samples]\n", myname);
	fprintf(stderr, "\n");
	fprintf(stderr,
		"\tToday's atop logfile is used by default!\n");
	fprintf(stderr, "\n");
	fprintf(stderr,
		"\tGeneric flags:\n");
	fprintf(stderr,
		"\t  -r  read statistical data from specific atop logfile\n");
	fprintf(stderr,
		"\t      (pathname, or date in format YYYYMMDD, or y[y..])\n");
	fprintf(stderr,
		"\t  -R  summarize <cnt> samples into one sample\n");
	fprintf(stderr,
		"\t  -b  begin  showing data from  specified time\n");
	fprintf(stderr,
		"\t  -e  finish showing data after specified time\n");
	fprintf(stderr,
		"\t  -S  print timestamp on every line in case of more "
		"resources\n");
	fprintf(stderr,
		"\t  -x  never  use colors to indicate overload"
		" (default: only if tty)\n");
	fprintf(stderr,
		"\t  -C  always use colors to indicate overload"
		" (default: only if tty)\n");
	fprintf(stderr,
		"\t  -M  use markers to indicate overload "
		"(* = critical, + = almost)\n");
	fprintf(stderr,
		"\t  -H  repeat report headers "
		"(in case of tty: depending on screen lines)\n");
	fprintf(stderr,
		"\t  -a  print all resources, even when inactive\n");
	fprintf(stderr, "\n");
	fprintf(stderr,
		"\tSpecific flags to select reports:\n");
	fprintf(stderr,
		"\t  -A  print all available reports\n");

	for (i=0; i < pricnt; i++)
		fprintf(stderr,
		"\t  -%c  %s\n", pridef[i].flag, pridef[i].about);

	cleanstop(1);
}

/*
** calculate the epoch-value for the last second
** of the day given a certain epoch
*/
static time_t
daylimit(time_t timval)
{
	struct tm  *tp = localtime(&timval);

	tp->tm_hour = 23;
	tp->tm_min  = 59;
	tp->tm_sec  = 59;

	return mktime(tp);
}

/*
** function to be called before printing a statistics line
** to switch on colors when necessary
*/
static void
preprint(unsigned int badness)
{
	if (usecolors)
	{
		if (badness >= 100)
		{
			coloron = 1;
			printf(COLSETHIGH);
		}
		else
		{
			if (almostcrit && badness >= almostcrit)
			{
				coloron = 1;
				printf(COLSETMED);
			}
		}
	}
}

/*
** function to be called after printing a statistics line
** to switch off colors when necessary and print a line feed
*/
static void
postprint(unsigned int badness)
{
	if (coloron)
	{
		coloron = 0;
		printf(COLRESET);
	}

	if (usemarkers)
	{
		if (badness >= 100)
		{
			printf(" *");
		}
		else
		{
			if (almostcrit && badness >= almostcrit)
				printf(" +");
		}
	}

	printf("\n");
}

/*
** function the handle the default flags for atopsar as
** read from the file ~/.atoprc
*/
void
do_atopsarflags(char *val)
{
	int     i, j;

	for (i=0; val[i]; i++)
	{
		switch (val[i])
		{
		   case '-':
			break;

		   case 'S':		/* timestamp on every line */
			stampalways = 1;
			break;

		   case 'x':		/* always colors for overload */
			usecolors = 0;
			break;

		   case 'C':		/* always colors for overload */
			usecolors = 'a';
			break;

		   case 'M':		/* markers for overload    */
			usemarkers = 1;
			break;

		   case 'H':		/* repeat headers          */
			repeathead = 23;	/* define default  */

			if (isatty(1))
			{
				struct winsize wsz;

				if ( ioctl(1, TIOCGWINSZ, &wsz) != -1)
					repeathead = wsz.ws_row - 1;
			}
			break;

		   case 'a':		/* every interval all units */
			allresources = 1;
			break;

		   case 'A':		/* all reports wanted ?  */
			for (j=0; j < pricnt; j++)
				pridef[j].wanted = 1;

			numreports = pricnt;
			break;

		   default:		/* gather report-flags    */
			for (j=0; j < pricnt; j++)
			{
				if (pridef[j].flag   == val[i] && 
				    pridef[j].wanted == 0        )
				{
					pridef[j].wanted = 1;
					numreports++;
					break;
				}
			}
		}
	}
}

/**************************************************************************/
/*                 Functions to print statistics                          */
/**************************************************************************/
/*
** CPU statistics
*/
static void
cpuhead(int osvers, int osrel, int ossub)
{
	printf("cpu  %%usr %%nice %%sys %%irq %%softirq  %%steal %%guest "
	       " %%wait %%idle  _cpu_");
}

static int
cpuline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	register int	i, nlines = 1;
	count_t		cputot;
	unsigned int	badness;

	/*
	** print overall statistics
	*/
        cputot = ss->cpu.all.stime + ss->cpu.all.utime +
                 ss->cpu.all.ntime + ss->cpu.all.itime +
                 ss->cpu.all.wtime + ss->cpu.all.Itime +
                 ss->cpu.all.Stime + ss->cpu.all.steal +
                 ss->cpu.all.guest;

	if (cputot == 0)
		cputot = 1;	/* avoid divide-by-zero */

	if (cpubadness)
		badness = ((cputot - ss->cpu.all.itime - ss->cpu.all.wtime) *
                            100.0 / cputot) * 100 / cpubadness;
	else
		badness = 0;

	preprint(badness);

	printf("all %5.0lf %5.0lf %4.0lf %4.0lf %8.0lf %7.0f %6.0f %6.0lf %5.0lf",
                (double) (ss->cpu.all.utime * 100.0) / cputot * ss->cpu.nrcpu,
                (double) (ss->cpu.all.ntime * 100.0) / cputot * ss->cpu.nrcpu,
                (double) (ss->cpu.all.stime * 100.0) / cputot * ss->cpu.nrcpu,
                (double) (ss->cpu.all.Itime * 100.0) / cputot * ss->cpu.nrcpu,
                (double) (ss->cpu.all.Stime * 100.0) / cputot * ss->cpu.nrcpu,
                (double) (ss->cpu.all.steal * 100.0) / cputot * ss->cpu.nrcpu,
                (double) (ss->cpu.all.guest * 100.0) / cputot * ss->cpu.nrcpu,
                (double) (ss->cpu.all.wtime * 100.0) / cputot * ss->cpu.nrcpu,
                (double) (ss->cpu.all.itime * 100.0) / cputot * ss->cpu.nrcpu);

	postprint(badness);

	/*
	** print per-cpu statistics
	*/
	if (ss->cpu.nrcpu > 1)
	{
		for (i=0; i < ss->cpu.nrcpu; i++)
		{
        		cputot = ss->cpu.cpu[i].stime + ss->cpu.cpu[i].utime +
                 	         ss->cpu.cpu[i].ntime + ss->cpu.cpu[i].itime +
                 	         ss->cpu.cpu[i].wtime + ss->cpu.cpu[i].Itime +
                 	         ss->cpu.cpu[i].Stime + ss->cpu.cpu[i].steal +
                 	         ss->cpu.cpu[i].guest;

			if (cputot == 0)
				cputot = 1;	/* avoid divide-by-zero */

			if (cpubadness)
				badness = ((cputot - ss->cpu.cpu[i].itime -
					    ss->cpu.cpu[i].wtime) * 100.0 /
				            cputot) * 100 / cpubadness;
			else
				badness = 0;

			printf("%s ", tstamp);

			preprint(badness);

			printf("%4d %5.0lf %5.0lf %4.0lf %4.0lf %8.0lf "
			       "%7.0f %6.0lf %6.0lf %5.0lf",
			     i,
                 	     (double)(ss->cpu.cpu[i].utime * 100.0) / cputot,
                	     (double)(ss->cpu.cpu[i].ntime * 100.0) / cputot,
                	     (double)(ss->cpu.cpu[i].stime * 100.0) / cputot,
                	     (double)(ss->cpu.cpu[i].Itime * 100.0) / cputot,
                	     (double)(ss->cpu.cpu[i].Stime * 100.0) / cputot,
                	     (double)(ss->cpu.cpu[i].steal * 100.0) / cputot,
                	     (double)(ss->cpu.cpu[i].guest * 100.0) / cputot,
                	     (double)(ss->cpu.cpu[i].wtime * 100.0) / cputot,
                	     (double)(ss->cpu.cpu[i].itime * 100.0) / cputot);

			postprint(badness);

			nlines++;
		}
	}

	return nlines;
}

/*
** other processor statistics
*/
static void
prochead(int osvers, int osrel, int ossub)
{
	printf("pswch/s devintr/s  clones/s  loadavg1 loadavg5 loadavg15  "
	       "     _load_");
}

static int
procline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	printf("%7.0lf %9.0lf %9.2lf  %8.2lf %8.2lf  %8.2lf\n",
		(double)ss->cpu.csw    / deltasec,
		(double)ss->cpu.devint / deltasec,
		(double)ss->cpu.nprocs / deltasec,
		ss->cpu.lavg1, ss->cpu.lavg5, ss->cpu.lavg15);
	return 1;
}

/*
** process statistics
*/
static void
taskhead(int osvers, int osrel, int ossub)
{
	printf("clones/s pexit/s  curproc curzomb    thrrun thrslpi thrslpu "
	       "_procthr_");
}

static int
taskline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	if (ppres == 0)
	{
		printf("report not available for live measurements.....\n");
		return 0;
	}

	if (ps)		/* process statistics available */
	{
		printf("%8.2lf %7.2lf  %7d %7d    %6d %7d %7d\n",
			(double)ss->cpu.nprocs / deltasec,
			(double)pexit          / deltasec,
			ppres, pzombie, ntrun, ntslpi, ntslpu);
	}
	else
	{
		printf("%8.2lf %7.2lf  %7d %7d\n",
			(double)ss->cpu.nprocs / deltasec,
			(double)pexit          / deltasec,
			ppres, pzombie);
	}

	return 1;
}

/*
** memory- & swap-usage
*/
static void
memhead(int osvers, int osrel, int ossub)
{
	printf("memtotal memfree buffers cached dirty slabmem"
	       "  swptotal swpfree _mem_"             );
}

static int
memline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	unsigned int	mbadness, sbadness;

	if (membadness)
		mbadness = ((ss->mem.physmem  - ss->mem.freemem -
	                     ss->mem.cachemem - ss->mem.buffermem)
	                               * 100.0 / ss->mem.physmem) 
	                               * 100   / membadness;
	else
		mbadness = 0;

        if (swpbadness)
        	sbadness = ((ss->mem.totswap - ss->mem.freeswap)
	                               * 100.0 / ss->mem.totswap)
                                       * 100   / swpbadness;
        else
                sbadness = 0;

	preprint(mbadness >= sbadness ? mbadness : sbadness);

	printf("%7lldM %6lldM %6lldM %5lldM %4lldM %6lldM  %7lldM %6lldM",
		ss->mem.physmem   * (pagesize / 1024) /1024,
		ss->mem.freemem   * (pagesize / 1024) /1024,
		ss->mem.buffermem * (pagesize / 1024) /1024,
		ss->mem.cachemem  * (pagesize / 1024) /1024,
		ss->mem.cachedrt  * (pagesize / 1024) /1024,
		ss->mem.slabmem   * (pagesize / 1024) /1024,
		ss->mem.totswap   * (pagesize / 1024) /1024,
		ss->mem.freeswap  * (pagesize / 1024) /1024);

	postprint(mbadness >= sbadness ? mbadness : sbadness);

	return 1;
}

/*
** swapping statistics
*/
static void
swaphead(int osvers, int osrel, int ossub)
{
	printf("pagescan/s   swapin/s  swapout/s      "
	       "  commitspc  commitlim   _swap_");
}

static int
swapline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	unsigned int	badness;

	if (membadness)
                badness = (ss->mem.swouts / deltasec * pagbadness)
				* 100 / membadness;
	else
		badness = 0;

	/*
	** take care that this line is anyhow colored for
	** 'almost critical' in case of swapouts > 1 per second
	*/
	if (ss->mem.swouts / deltasec > 0  &&
	    pagbadness && almostcrit && badness < almostcrit)
		badness = almostcrit;

	if (ss->mem.commitlim && ss->mem.committed > ss->mem.commitlim)
		badness = 100;         /* force colored output */

	preprint(badness);

	printf("%10.2lf  %9.2lf  %9.2lf       %9lluM %9lluM",
		(double)ss->mem.pgscans / deltasec,
		(double)ss->mem.swins   / deltasec,
		(double)ss->mem.swouts  / deltasec,
		        ss->mem.committed * (pagesize / 1024) / 1024,
		        ss->mem.commitlim * (pagesize / 1024) / 1024);

	postprint(badness);

	return 1;
}

/*
** disk statistics
*/
static void
lvmhead(int osvers, int osrel, int ossub)
{
	printf("disk           busy read/s KB/read  "
	       "writ/s KB/writ avque avserv _lvm_");
}

static void
mddhead(int osvers, int osrel, int ossub)
{
	printf("disk           busy read/s KB/read  "
	       "writ/s KB/writ avque avserv _mdd_");
}

static void
dskhead(int osvers, int osrel, int ossub)
{
	printf("disk           busy read/s KB/read  "
	       "writ/s KB/writ avque avserv _dsk_");
}

static int
gendskline(struct sstat *ss, char *tstamp, char selector)
{
	static char	firstcall = 1;
	register int	i, nlines = 0, nunit = 0;
	count_t		mstot, iotot;
	struct perdsk 	*dp;
	unsigned int	badness;

	switch (selector)
	{
	   case 'l':
		dp 	= ss->dsk.lvm;
		nunit	= ss->dsk.nlvm;
		break;

	   case 'm':
		dp 	= ss->dsk.mdd;
		nunit	= ss->dsk.nmdd;
		break;

	   case 'd':
		dp 	= ss->dsk.dsk;
		nunit	= ss->dsk.ndsk;
		break;

	   default:
		return 0;
	}

        mstot  = (ss->cpu.all.stime + ss->cpu.all.utime +
                  ss->cpu.all.ntime + ss->cpu.all.itime +
                  ss->cpu.all.wtime + ss->cpu.all.Itime +
                  ss->cpu.all.Stime + ss->cpu.all.steal +
                  ss->cpu.all.guest                      )
				* (count_t)1000 / hertz / ss->cpu.nrcpu;

	for (i=0; i < nunit; i++, dp++)
	{
		char	buf[32];
		char	*pn;
		int	len;

		if ( (iotot = dp->nread + dp->nwrite) == 0 &&
 		     !firstcall && !allresources             )
			continue;	/* no activity on this disk */

		/*
		** disk was active during last interval; print info
		*/
		if (nlines++)
			printf("%s  ", tstamp);

		if (dskbadness)
			badness = (dp->io_ms * 100.0 / mstot) * 100/dskbadness;
                else
			badness = 0;

		preprint(badness);

		if ( (len = strlen(dp->name)) > 14)
			pn = dp->name + len - 14;
		else
			pn = dp->name;

        sprintf(buf, "%12.12s", pn);
		printf("%-14s %3.0lf%% %6.1lf %7.1lf %7.1lf %7.1lf "
		       "%5.1lf %6.2lf ms",
		    	pn,
			mstot ? (double)dp->io_ms  *  100.0 / mstot   : 0.0,
			mstot ? (double)dp->nread  * 1000.0 / mstot   : 0.0,
			dp->nread  ?
			        (double)dp->nrsect / dp->nread / 2.0  : 0.0,
			mstot ? (double)dp->nwrite * 1000.0 / mstot   : 0.0,
			dp->nwrite ?
  			        (double)dp->nwsect / dp->nwrite / 2.0 : 0.0,
			dp->io_ms  ? (double)dp->avque / dp->io_ms    : 0.0,
		      	iotot ? (double)dp->io_ms  / iotot            : 0.0);

		postprint(badness);
	}

	if (nlines == 0)
	{
		printf("\n");
		nlines++;
	}

	firstcall = 0;

	return nlines;
}

static int
lvmline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	return gendskline(ss, tstamp, 'l');
}

static int
mddline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	return gendskline(ss, tstamp, 'm');
}

static int
dskline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	return gendskline(ss, tstamp, 'd');
}

/*
** network-interface statistics
*/
static void
ifhead(int osvers, int osrel, int ossub)
{
	printf("interf busy ipack/s opack/s iKbyte/s oKbyte/s "
	       "imbps ombps maxmbps_if_");
}

static int
ifline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	static char	firstcall = 1;
	register long	i, nlines = 0;
	double		busy;
	char		busyval[16], dupval;
	unsigned int	badness;

	for (i=0; i < ss->intf.nrintf; i++)	/* per interface */
	{
		count_t ival, oval;

		/*
		** print for the first sample all interfaces which
		** are found; afterwards print only the interfaces
		** which were really active during the interval
		*/
		if (!firstcall && !allresources &&
		    !ss->intf.intf[i].rpack && !ss->intf.intf[i].spack)
			continue;

		/*
		** convert byte-transfers to bit-transfers     (*       8)
		** convert bit-transfers  to megabit-transfers (/ 1000000)
		** per second
		*/
		ival = ss->intf.intf[i].rbyte/125000/deltasec;
		oval = ss->intf.intf[i].sbyte/125000/deltasec;

		/*
		** calculate busy-percentage for interface
		*/
		if (ss->intf.intf[i].speed)	/* speed known? */
		{
			if (ss->intf.intf[i].duplex)
				busy = (ival > oval ? ival*100 : oval*100) /
				        ss->intf.intf[i].speed;
			else
				busy = (ival + oval) * 100 /
				        ss->intf.intf[i].speed;

			snprintf(busyval, sizeof busyval,
						"%3.0lf%%", busy);
		}
		else
		{
			strcpy(busyval, "?"); /* speed unknown */
			busy = 0;
		}

		if (nlines++)
			printf("%s  ", tstamp);

		if (ss->intf.intf[i].speed)
		{
			if (ss->intf.intf[i].duplex)
				dupval = 'f';
			else
				dupval = 'h';
		}
		else
		{
			dupval = ' ';
		}

		if (netbadness)
			badness = busy * 100 / netbadness;
		else
			badness = 0;

		preprint(badness);

		printf("%-6s %4s %7.1lf %7.1lf %8.0lf %8.0lf "
		       "%5lld %5lld %7ld %c", 
			ss->intf.intf[i].name, busyval,
			(double)ss->intf.intf[i].rpack / deltasec,
			(double)ss->intf.intf[i].spack / deltasec,
			(double)ss->intf.intf[i].rbyte / 1024 / deltasec,
			(double)ss->intf.intf[i].sbyte / 1024 / deltasec,
			ival, oval,
			ss->intf.intf[i].speed, dupval);

		postprint(badness);
	}

	if (nlines == 0)
	{
		printf("\n");
		nlines++;
	}

	firstcall = 0;
	return nlines;
}

static void
IFhead(int osvers, int osrel, int ossub)
{
	printf("interf ierr/s oerr/s coll/s idrop/s odrop/s "
	       "iframe/s ocarrier/s  _if_");
}

static int
IFline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	static char	firstcall = 1;
	register long	i, nlines = 0;

	for (i=0; i < ss->intf.nrintf; i++)	/* per interface */
	{
		/*
		** print for the first sample all interfaces which
		** are found; afterwards print only the interfaces
		** which were really active during the interval
		*/
		if (!firstcall && !allresources &&
		    !ss->intf.intf[i].rpack && !ss->intf.intf[i].spack)
			continue;

		if (nlines++)
			printf("%s  ", tstamp);

		printf("%-6s %6.2lf %6.2lf %6.2lf %7.2lf %7.2lf "
		       "%8.2lf %10.2lf\n", 
			ss->intf.intf[i].name,
			(double)ss->intf.intf[i].rerrs    / deltasec,
			(double)ss->intf.intf[i].serrs    / deltasec,
			(double)ss->intf.intf[i].scollis  / deltasec,
			(double)ss->intf.intf[i].rdrop    / deltasec,
			(double)ss->intf.intf[i].sdrop    / deltasec,
			(double)ss->intf.intf[i].rframe   / deltasec,
			(double)ss->intf.intf[i].scarrier / deltasec);
	}

	if (nlines == 0)
	{
		printf("\n");
		nlines++;
	}

	firstcall= 0;
	return nlines;
}

/*
** IP version 4 statistics
*/
static void
ipv4head(int osvers, int osrel, int ossub)
{
	printf("inrecv/s outreq/s indeliver/s forward/s "
	       "reasmok/s fragcreat/s  _ipv4_");
}

static int
ipv4line(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	printf("%8.1lf %8.1lf %11.1lf %9.1lf %9.1lf %11.1lf\n", 
		(double)ss->net.ipv4.InReceives  / deltasec,
		(double)ss->net.ipv4.OutRequests / deltasec,
		(double)ss->net.ipv4.InDelivers  / deltasec,
		(double)ss->net.ipv4.Forwarding  / deltasec,
		(double)ss->net.ipv4.ReasmOKs    / deltasec,
		(double)ss->net.ipv4.FragCreates / deltasec);
	return 1;
}

static void
IPv4head(int osvers, int osrel, int ossub)
{
	printf("in: dsc/s hder/s ader/s unkp/s ratim/s rfail/s "
	       "out: dsc/s nrt/s_ipv4_");
}

static int
IPv4line(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	printf("    %5.1lf %6.1lf %6.1lf %6.1lf %7.1lf %7.1lf  "
	       "    %5.1lf %5.1lf\n", 
		(double)ss->net.ipv4.InDiscards      / deltasec,
		(double)ss->net.ipv4.InHdrErrors     / deltasec,
		(double)ss->net.ipv4.InAddrErrors    / deltasec,
		(double)ss->net.ipv4.InUnknownProtos / deltasec,
		(double)ss->net.ipv4.ReasmTimeout    / deltasec,
		(double)ss->net.ipv4.ReasmFails      / deltasec,
		(double)ss->net.ipv4.OutDiscards     / deltasec,
		(double)ss->net.ipv4.OutNoRoutes     / deltasec);
	return 1;
}

/*
** ICMP version 4 statistics
*/
static void
icmpv4head(int osvers, int osrel, int ossub)
{
	printf("intot/s outtot/s  inecho/s inerep/s  "
	       "otecho/s oterep/s       _icmpv4_"   );
}

static int
icmpv4line(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	printf("%7.1lf %8.1lf  %8.2lf %8.2lf  %8.2lf %8.2lf\n", 
		(double)ss->net.icmpv4.InMsgs      / deltasec, 
		(double)ss->net.icmpv4.OutMsgs     / deltasec, 
		(double)ss->net.icmpv4.InEchos     / deltasec, 
		(double)ss->net.icmpv4.OutEchos    / deltasec, 
		(double)ss->net.icmpv4.InEchoReps  / deltasec, 
		(double)ss->net.icmpv4.OutEchoReps / deltasec);
	return 1;
}

static void
ICMPv4head(int osvers, int osrel, int ossub)
{
	printf("ierr/s isq/s ird/s idu/s ite/s "
	       "oerr/s osq/s ord/s odu/s ote/s_icmpv4_");
}

static int
ICMPv4line(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	printf("%6.2lf %5.2lf %5.2lf %5.2lf %5.2lf "
	       "%6.2lf %5.2lf %5.2lf %5.2lf %5.2lf\n", 
		(double)ss->net.icmpv4.InErrors        / deltasec,
		(double)ss->net.icmpv4.InSrcQuenchs    / deltasec,
		(double)ss->net.icmpv4.InRedirects     / deltasec,
		(double)ss->net.icmpv4.InDestUnreachs  / deltasec,
		(double)ss->net.icmpv4.InTimeExcds     / deltasec,
		(double)ss->net.icmpv4.OutErrors       / deltasec,
		(double)ss->net.icmpv4.OutSrcQuenchs   / deltasec,
		(double)ss->net.icmpv4.OutRedirects    / deltasec,
		(double)ss->net.icmpv4.OutDestUnreachs / deltasec,
		(double)ss->net.icmpv4.OutTimeExcds    / deltasec);
	return 1;
}

/*
** UDP version 4 statistics
*/
static void
udpv4head(int osvers, int osrel, int ossub)
{
	printf("indgram/s outdgram/s   inerr/s  noport/s    "
	       "                  _udpv4_");
}

static int
udpv4line(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	printf("%9.1lf %10.1lf   %7.2lf %9.2lf\n",
		(double)ss->net.udpv4.InDatagrams  / deltasec,
		(double)ss->net.udpv4.OutDatagrams / deltasec,
		(double)ss->net.udpv4.InErrors     / deltasec,
		(double)ss->net.udpv4.NoPorts      / deltasec);
	return 1;
}

/*
** IP version 6 statistics
*/
static void
ipv6head(int osvers, int osrel, int ossub)
{
	printf("inrecv/s outreq/s inmc/s outmc/s indeliv/s "
	       "reasmok/s fragcre/s _ipv6_");
}

static int
ipv6line(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	printf("%8.1lf %8.1lf %6.1lf %7.1lf %9.1lf %9.1lf %9.1lf\n", 
		(double)ss->net.ipv6.Ip6InReceives   / deltasec,
		(double)ss->net.ipv6.Ip6OutRequests  / deltasec,
		(double)ss->net.ipv6.Ip6InMcastPkts  / deltasec,
		(double)ss->net.ipv6.Ip6OutMcastPkts / deltasec,
		(double)ss->net.ipv6.Ip6InDelivers   / deltasec,
		(double)ss->net.ipv6.Ip6ReasmOKs     / deltasec,
		(double)ss->net.ipv6.Ip6FragCreates  / deltasec);
	return 1;
}

static void
IPv6head(int osvers, int osrel, int ossub)
{
	printf("in: dsc/s hder/s ader/s unkp/s ratim/s rfail/s "
	       "out: dsc/s nrt/s_ipv6_");
}

static int
IPv6line(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	printf("    %5.1lf %6.1lf %6.1lf %6.1lf %7.1lf %7.1lf  "
	       "    %5.1lf %5.1lf\n", 
		(double)ss->net.ipv6.Ip6InDiscards      / deltasec,
		(double)ss->net.ipv6.Ip6InHdrErrors     / deltasec,
		(double)ss->net.ipv6.Ip6InAddrErrors    / deltasec,
		(double)ss->net.ipv6.Ip6InUnknownProtos / deltasec,
		(double)ss->net.ipv6.Ip6ReasmTimeout    / deltasec,
		(double)ss->net.ipv6.Ip6ReasmFails      / deltasec,
		(double)ss->net.ipv6.Ip6OutDiscards  / deltasec,
		(double)ss->net.ipv6.Ip6OutNoRoutes     / deltasec);
	return 1;
}

/*
** ICMP version 6 statistics
*/
static void
icmpv6head(int osvers, int osrel, int ossub)
{
	printf("intot/s outtot/s inerr/s innsol/s innadv/s "
	       "otnsol/s otnadv/s  _icmp6_"   );
}

static int
icmpv6line(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	printf("%7.1lf %8.1lf %7.2lf %8.2lf %8.2lf %8.2lf %8.2lf\n", 
		(double)ss->net.icmpv6.Icmp6InMsgs                  / deltasec, 
		(double)ss->net.icmpv6.Icmp6OutMsgs                 / deltasec, 
		(double)ss->net.icmpv6.Icmp6InErrors                / deltasec, 
		(double)ss->net.icmpv6.Icmp6InNeighborSolicits      / deltasec, 
		(double)ss->net.icmpv6.Icmp6InNeighborAdvertisements/ deltasec, 
		(double)ss->net.icmpv6.Icmp6OutNeighborSolicits     / deltasec, 
		(double)ss->net.icmpv6.Icmp6OutNeighborAdvertisements
								/deltasec);
	return 1;
}

static void
ICMPv6head(int osvers, int osrel, int ossub)
{
	printf("iecho/s ierep/s oerep/s idu/s odu/s ird/s ord/s ite/s "
	       "ote/s  _icmpv6_");
}

static int
ICMPv6line(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	printf("%7.2lf %7.2lf %7.2lf %5.2lf %5.2lf "
	       "%5.2lf %5.2lf %5.2lf %5.2lf\n", 
		(double)ss->net.icmpv6.Icmp6InEchos         / deltasec,
		(double)ss->net.icmpv6.Icmp6InEchoReplies   / deltasec,
		(double)ss->net.icmpv6.Icmp6OutEchoReplies  / deltasec,
		(double)ss->net.icmpv6.Icmp6InDestUnreachs  / deltasec,
		(double)ss->net.icmpv6.Icmp6OutDestUnreachs / deltasec,
		(double)ss->net.icmpv6.Icmp6InRedirects     / deltasec,
		(double)ss->net.icmpv6.Icmp6OutRedirects    / deltasec,
		(double)ss->net.icmpv6.Icmp6InTimeExcds     / deltasec,
		(double)ss->net.icmpv6.Icmp6OutTimeExcds    / deltasec);
	return 1;
}

/*
** UDP version 6 statistics
*/
static void
udpv6head(int osvers, int osrel, int ossub)
{
	printf("indgram/s outdgram/s   inerr/s  noport/s    "
	       "                  _udpv6_");
}

static int
udpv6line(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	printf("%9.1lf %10.1lf   %7.2lf %9.2lf\n",
		(double)ss->net.udpv6.Udp6InDatagrams  / deltasec,
		(double)ss->net.udpv6.Udp6OutDatagrams / deltasec,
		(double)ss->net.udpv6.Udp6InErrors     / deltasec,
		(double)ss->net.udpv6.Udp6NoPorts      / deltasec);
	return 1;
}

/*
** TCP statistics
*/
static void
tcphead(int osvers, int osrel, int ossub)
{
	printf("insegs/s outsegs/s  actopen/s pasopen/s  "
	       "nowopen                _tcp_");
}

static int
tcpline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	printf("%8.1lf %9.1lf  %9.1lf %9.1lf  %7lld\n",
		(double)ss->net.tcp.InSegs       / deltasec,
		(double)ss->net.tcp.OutSegs      / deltasec,
		(double)ss->net.tcp.ActiveOpens  / deltasec,
		(double)ss->net.tcp.PassiveOpens / deltasec,
		        ss->net.tcp.CurrEstab);
	return 1;
}

static void
TCPhead(int osvers, int osrel, int ossub)
{
	printf("inerr/s  retrans/s  attfail/s  "
	       "estabreset/s  outreset/s         _tcp_");
}

static int
TCPline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	printf("%7.1lf  %9.1lf  %9.1lf  %12.1lf  %10.1lf\n",
		(double)ss->net.tcp.InErrs       / deltasec,
		(double)ss->net.tcp.RetransSegs  / deltasec,
		(double)ss->net.tcp.AttemptFails / deltasec,
		(double)ss->net.tcp.EstabResets  / deltasec,
		(double)ss->net.tcp.OutRsts      / deltasec);
	return 1;
}

#if	HTTPSTATS
static void
httphead(int osvers, int osrel, int ossub)
{
	printf("requests/s  Kbytes/s  bytes/req    "
	       "idleworkers busyworkers     _http_");
}

static int
httpline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	printf("%10.2lf  %8.2lf  %9.2lf    %11d %11d\n",
		(double)ss->www.accesses      / deltasec,
		(double)ss->www.totkbytes     / deltasec,
		ss->www.accesses ? 
		    (double)ss->www.totkbytes*1024/ss->www.accesses : 0,
		        ss->www.iworkers,
		        ss->www.bworkers);

	return 1;
}
#endif

/*
** per-process statistics: top-3 processor consumers
*/
static void
topchead(int osvers, int osrel, int ossub)
{
	printf("  pid command  cpu%% |   pid command  cpu%% | "
	       "  pid command  cpu%%_top3_");
}

static int
topcline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	count_t		availcpu;

	if (!ps)
	{
		printf("report not available.....\n");
		return 0;
	}

	/*
	** sort process list in cpu order
	*/
	qsort(ps, ppres, sizeof(struct pstat), compcpu);

	availcpu  = ss->cpu.all.stime + ss->cpu.all.utime +
	            ss->cpu.all.ntime + ss->cpu.all.itime +
	            ss->cpu.all.wtime + ss->cpu.all.Itime +
		    ss->cpu.all.Stime + ss->cpu.all.steal +
		    ss->cpu.all.guest; 

	availcpu /= ss->cpu.nrcpu;

	if (availcpu == 0)
		availcpu = 1;	/* avoid divide-by-zero */

	printf("%5d %-8.8s %3.0lf%% | %5d %-8.8s %3.0lf%% | "
	       "%5d %-8.8s %3.0lf%%\n",
		(ps+0)->gen.pid, (ps+0)->gen.name,
		(double)((ps+0)->cpu.stime + (ps+0)->cpu.utime)*100.0/availcpu,
		(ps+1)->gen.pid, (ps+1)->gen.name,
		(double)((ps+1)->cpu.stime + (ps+1)->cpu.utime)*100.0/availcpu,
		(ps+2)->gen.pid, (ps+2)->gen.name,
		(double)((ps+2)->cpu.stime + (ps+2)->cpu.utime)*100.0/availcpu);

	return 1;
}

/*
** per-process statistics: top-3 memory consumers
*/
static void
topmhead(int osvers, int osrel, int ossub)
{
	printf("  pid command  mem%% |   pid command  mem%% | "
	       "  pid command  mem%%_top3_");
}

static int
topmline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	count_t		availmem;

	if (!ps)
	{
		printf("report not available.....\n");
		return 0;
	}

	/*
	** sort process list in memory order
	*/
	qsort(ps, ppres, sizeof(struct pstat), compmem);

	availmem  = ss->mem.physmem * pagesize/1024;

	printf("%5d %-8.8s %3.0lf%% | %5d %-8.8s %3.0lf%% | "
	       "%5d %-8.8s %3.0lf%%\n",
		(ps+0)->gen.pid, (ps+0)->gen.name,
		(double)(ps+0)->mem.rmem * 100.0 / availmem,
		(ps+1)->gen.pid, (ps+1)->gen.name,
		(double)(ps+1)->mem.rmem * 100.0 / availmem,
		(ps+2)->gen.pid, (ps+2)->gen.name,
		(double)(ps+2)->mem.rmem * 100.0 / availmem);

	return 1;
}

/*
** per-process statistics: top-3 disk consumers
*/
static void
topdhead(int osvers, int osrel, int ossub)
{
	printf("  pid command  dsk%% |   pid command  dsk%% | "
	       "  pid command  dsk%%_top3_");
}

static int
topdline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	int		i;
	count_t		availdsk;

	if (!ps)
	{
		printf("report not available.....\n");
		return 0;
	}

	if ( !(supportflags & (PATCHSTAT | IOSTAT)) )
	{
		printf("no per-process disk counters available.....\n");
		return 0;
	}

	/*
	** determine total disk accesses for all processes
	*/
	for (i=0, availdsk=0; i < nproc; i++)
	{
		availdsk += (ps+i)->dsk.rio + (ps+i)->dsk.wio;
	}

	if (availdsk == 0)
		availdsk = 1;

	/*
	** sort process list in disk order
	*/
	qsort(ps, ppres, sizeof(struct pstat), compdsk);

	printf("%5d %-8.8s %3.0lf%% | %5d %-8.8s %3.0lf%% | "
	       "%5d %-8.8s %3.0lf%%\n",
		(ps+0)->gen.pid, (ps+0)->gen.name,
	     	(double)((ps+0)->dsk.rio+(ps+0)->dsk.wio) *100.0/availdsk,
		(ps+1)->gen.pid, (ps+1)->gen.name,
		(double)((ps+1)->dsk.rio+(ps+1)->dsk.wio) *100.0/availdsk,
		(ps+2)->gen.pid, (ps+2)->gen.name,
		(double)((ps+2)->dsk.rio+(ps+2)->dsk.wio) *100.0/availdsk);

	return 1;
}

/*
** per-process statistics: top-3 network consumers
*/
static void
topnhead(int osvers, int osrel, int ossub)
{
	printf("  pid command  net%% |   pid command  net%% | "
	       "  pid command  net%%_top3_");
}

static int
topnline(struct sstat *ss, struct pstat *ps, int nproc,
        time_t deltasec, time_t deltatic, time_t hz,
        int osvers, int osrel, int ossub, char *tstamp,
        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
{
	int		i;
	count_t		availnet;

	if (!ps)
	{
		printf("report not available.....\n");
		return 0;
	}

	if ( !(supportflags & PATCHSTAT))
	{
		printf("no per-process network counters available.....\n");
		return 0;
	}

	/*
	** determine total network accesses for all processes
	*/
	for (i=0, availnet=0; i < nproc; i++)
	{
		availnet += (ps+i)->net.tcpsnd + (ps+i)->net.tcprcv +
		            (ps+i)->net.udpsnd + (ps+i)->net.udprcv +
		            (ps+i)->net.rawsnd + (ps+i)->net.rawrcv;
	}

	if (availnet == 0)
		availnet = 1;

	/*
	** sort process list in network order
	*/
	qsort(ps, ppres, sizeof(struct pstat), compnet);

	printf("%5d %-8.8s %3.0lf%% | %5d %-8.8s %3.0lf%% | "
	       "%5d %-8.8s %3.0lf%%\n",
		(ps+0)->gen.pid, (ps+0)->gen.name,
		(double)((ps+0)->net.tcpsnd + (ps+0)->net.tcprcv +
		         (ps+0)->net.udpsnd + (ps+0)->net.udprcv +
		         (ps+0)->net.rawsnd + (ps+0)->net.rawrcv)
							* 100.0 / availnet,
		(ps+1)->gen.pid, (ps+1)->gen.name,
		(double)((ps+1)->net.tcpsnd + (ps+1)->net.tcprcv +
		         (ps+1)->net.udpsnd + (ps+1)->net.udprcv +
		         (ps+1)->net.rawsnd + (ps+1)->net.rawrcv)
							* 100.0 / availnet,
		(ps+2)->gen.pid, (ps+2)->gen.name,
		(double)((ps+2)->net.tcpsnd + (ps+2)->net.tcprcv +
		         (ps+2)->net.udpsnd + (ps+2)->net.udprcv +
		         (ps+2)->net.rawsnd + (ps+2)->net.rawrcv)
							* 100.0 / availnet);

	return 1;
}

/*********************************************************************/
/* Function definition table.                                        */
/*                                                                   */
/* The layout of this table is as follows:                           */
/*     Column 1:                                                     */
/*        Boolean which indicates if the specified function is       */
/*        active during a run of 'atopsar'. When started,            */
/*        this boolean will be defined 'true' for all entries for    */
/*        which the command-line flag has been specified. Initially  */
/*        this column should contain 0 (false), unless this function */
/*        is always required.                                        */
/*        If no flags are specified for 'atopsar', the first entry   */
/*        in this table is defined active (default flag).            */
/*                                                                   */
/*     Column 2:                                                     */
/*        Categories of counters used by this function.              */
/*           c = cpu  counters,    m = memory  counters,             */
/*           d = disk counters,    n = network counters              */
/*                                                                   */
/*     Column 3:                                                     */
/*        Flag which can be used as command-line argument to         */
/*        select the function defined in this table-entry. Be sure   */
/*        that a unique character is choosen.                        */
/*        Notice that certain flags are reserved!                    */
/*                                                                   */
/*     Column 4:                                                     */
/*        Entry-point of the 'printhead' function.                   */
/*                                                                   */
/*     Column 5:                                                     */
/*        Entry-point of the 'printline' function.                   */
/*                                                                   */
/*     Column 6:                                                     */
/*        Information about the statistics shown by the function     */
/*        specified by the table-entry. This text is printed as      */
/*        command-usage.                                             */
/*********************************************************************/
struct pridef pridef[] =
{
   {0,  "c",  'c',  cpuhead,	cpuline,  	"cpu utilization",        },
   {0,  "c",  'p',  prochead,	procline,  	"process(or) load",       },
   {0,  "c",  'P',  taskhead,	taskline,  	"processes & threads",    },
   {0,  "m",  'm',  memhead,	memline,	"memory & swapspace",     },
   {0,  "m",  's',  swaphead,	swapline,	"swap rate",              },
   {0,  "cd", 'l',  lvmhead,	lvmline,	"logical volume activity", },
   {0,  "cd", 'f',  mddhead,	mddline,	"multiple device activity",},
   {0,  "cd", 'd',  dskhead,	dskline,	"disk activity",          },
   {0,  "n",  'i',  ifhead,	ifline,		"net-interf (general)",   },
   {0,  "n",  'I',  IFhead,	IFline,		"net-interf (errors)",    },
   {0,  "n",  'w',  ipv4head,	ipv4line,	"ip   v4    (general)",   },
   {0,  "n",  'W',  IPv4head,	IPv4line,	"ip   v4    (errors)",    },
   {0,  "n",  'y',  icmpv4head,	icmpv4line,	"icmp v4    (general)",   },
   {0,  "n",  'Y',  ICMPv4head,	ICMPv4line,	"icmp v4    (per type)",  },
   {0,  "n",  'u',  udpv4head,	udpv4line,  	"udp  v4",                },
   {0,  "n",  'z',  ipv6head,	ipv6line,	"ip   v6    (general)",   },
   {0,  "n",  'Z',  IPv6head,	IPv6line,	"ip   v6    (errors)",    },
   {0,  "n",  'k',  icmpv6head,	icmpv6line,	"icmp v6    (general)",   },
   {0,  "n",  'K',  ICMPv6head,	ICMPv6line,	"icmp v6    (per type)",  },
   {0,  "n",  'U',  udpv6head,	udpv6line,  	"udp  v6",                },
   {0,  "n",  't',  tcphead,	tcpline,  	"tcp        (general)",   },
   {0,  "n",  'T',  TCPhead,	TCPline,  	"tcp        (errors)",    },
#if	HTTPSTATS
   {0,  "n",  'h',  httphead,	httpline,  	"HTTP activity",          },
#endif
   {0,  "",   'O',  topchead,	topcline,  	"top-3 processes cpu",    },
   {0,  "",   'G',  topmhead,	topmline,  	"top-3 processes memory", },
   {0,  "",   'D',  topdhead,	topdline,  	"top-3 processes disk",   },
   {0,  "",   'N',  topnhead,	topnline,  	"top-3 processes network",},
};

int	pricnt = sizeof(pridef)/sizeof(struct pridef);
