/*		Statistics logging package.
 *
 *	Code packages can register statistics logging routines with
 * this package.  This provides a consistent interface to getting at
 * the statistics of the gateway.
 */

/*
 *------------------------------------------------------------------
 *
 * $Source: /achilles/u1/jis/gw/cgw/gw/src/RCS/status.c,v $
 * $Revision: 1.2 $
 * $Date: 87/10/06 18:03:53 $
 * $State: Exp $
 * $Author: jon $
 * $Locker:  $
 *
 * $Log:	status.c,v $
 * Revision 1.2  87/10/06  18:03:53  jon
 * Adds free buffer info to the "stat sys" command.  Doesn't not change
 * the status packet structure.  Perhaps it would be useful to do so.
 * 
 * 
 *------------------------------------------------------------------
 */

#ifndef lint
static char *rcsid_status_c = "$Header: status.c,v 1.2+ 87/10/06 18:03:53 jon Exp $";
#endif	lint

#include	<types.h>
#include	<sys.h>
#include	"const.h"
#include	"param.h"
#include	"defs.h"
#include	"macs.h"
#include	"net.h"
#include	"ext.h"
#include	"status.h"
#include	"../opcon/opcon.h"
#include        "../in/bsd_time.h"

char *last_bughalt = "Reloaded";

ext char gwbafl[];  /* ugh, see gate.c */
ext int  ip_send_redirects;
ext int  DEFAULT_ROUTE_RIP_METRIC;

#define STLEN 10
struct stat {
    char *name;
    void (*rtn)();
} stra[STLEN];

/* Status command structures. */
void stat_gw(), stat_sys();

static subcommand status_scmd[] = {
    { "gw", "Network counters", stat_gw, 0 },
    { "sys", "System stats", stat_sys, 0 },
    { NULL, NULL, NULL, 0 },
};
static command status_cmd = {
    NULL, "stat", "System and network status", status_scmd,
};

/* Status initialization.  If stat_period is non zero, stat_gw()
 * is called every stat_period seconds.
 */
STAT(stat_period)
int stat_period;
{
    int i;
    void stat_dir(), stat_sys();
    
    for (i = 0; i < STLEN; i++) {
	stra[i].name = NULL;
	stra[i].rtn = NULL;
    }
    opcon_register(&status_cmd);
    
    if (stat_period) {
	itime(stat_sys, 0, stat_period, STATPRI);
	addtsk(syshnd, STATPRI, stat_sys, 0);
    }
}

/* Print network statistics. */
stat_gw(devp)
dct *devp;
{
    reg int i;
    reg net *netp;
    int cnt_pkti, cnt_byti, cnt_pkto, cnt_byto, cnt_ioin, cnt_ioout,
	cnt_ovfin, cnt_ovfout, cnt_disc;

    cnt_pkti = cnt_byti = cnt_pkto = cnt_byto = cnt_ioin = cnt_ioout = cnt_ovfin = cnt_ovfout = cnt_disc = 0;
    fprintf(devp, "  Interface%15.0%INPUT%24.0%OUTPUT\n");
    fprintf(devp, "Net    Type     (bytes/pkts     errs  ovfl)   (bytes/pkts     errs  ovfl)  disc\n");
    for (i = 0; i < nnets; i++) {
	netp = &nets[i];
	fprintf(devp, "%1u %8.8s/%1u %9u/%-7u %5u %5u %9u/%-7u %5u %5u  %5u\n",
		mksnet(netp),
		netp->n_byti, netp->n_pkti,
		netp->n_ioin, netp->n_ovfin,
		netp->n_byto, netp->n_pkto,
		netp->n_ioout, netp->n_ovfout,
		netp->n_disc);
	cnt_pkti += nets[i].n_pkti;
	cnt_byti += nets[i].n_byti;
	cnt_pkto += nets[i].n_pkto;
	cnt_byto += nets[i].n_byto;
	cnt_ioin += nets[i].n_ioin;
	cnt_ioout += nets[i].n_ioout;
	cnt_ovfin += nets[i].n_ovfin;
	cnt_ovfout += nets[i].n_ovfout;
	cnt_disc += nets[i].n_disc;
    }
    fprintf(devp, "     TOTAL:  %9u/%-7u %5u %5u %9u/%-7u %5u %5u  %5u\n",
	    cnt_byti, cnt_pkti,
	    cnt_ioin, cnt_ovfin,
	    cnt_byto, cnt_pkto,
	    cnt_ioout, cnt_ovfout,
	    cnt_disc);
}

/* Print system statistics.  If devp is NULL, then statistics are
 * logged instead of printed to a device.  This is a kludge.  Should
 * really have a log device which is used instead. */
stat_sys(devp)
dct *devp;
{
    int days, hrs, mins, secs;
    static char ss_name[] =
      "%s C-Gateway %s at %s\nUp %d+%02d:%02d:%02d  restarts: %d\n";
    static char ss_frmem[] = "Free memory %d bytes\n";
    static char ss_tim[] = "Num. timers: %-8d Max. timers: %d\n";
    static char ss_tsk[] = "Num. tsks:   %-8d Max. tsks:   %d\n";
    static char ss_load[] = "Load average: %d.%02d  %d.%02d  %d.%02d\n";
    ext int systod, restarts;
    ext int num_timers, max_timers;
    ext int num_tsks, max_tsks, av1_tsks, av5_tsks, av15_tsks;
    ext unsl himem;
    ext byte *end_mem;
    struct tm *ts;
    unsl clock, time();

    clock = time();
    ts = localtime (&clock);

    secs = systod % 60;
    mins = (systod / 60) % 60;
    hrs = (systod / (60 * 60)) % 24;
    days = (systod / (60 * 60 * 24));
    if (devp)
      fprintf(devp, ss_name, mach_name[mach_type], name, asctime(ts), 
	      days, hrs, mins, secs, restarts);
    else
      dolog(ss_name, mach_name[mach_type], asctime(ts), 
	    name, days, hrs, mins, secs, restarts);
    if (last_bughalt) {
	if (devp)
	  fprintf(devp, "Start msg: %s\n", last_bughalt);
	else
	  dolog("Start msg: %s\n", last_bughalt);
    }

    if (devp) {
	fprintf(devp, ss_frmem, himem - (unsl)end_mem);
	fprintf(devp, ss_tim, num_timers, max_timers);
	fprintf(devp, ss_tsk, num_tsks, max_tsks);
	fprintf(devp, ss_load,
		av1_tsks/1000, (av1_tsks%1000)/10,
		av5_tsks/1000, (av5_tsks%1000)/10,
		av15_tsks/1000, (av15_tsks%1000)/10);
	fprintf (devp, gwbafl, nbufs, nbfree, bufshr, buflow);
	if (!ip_send_redirects) 
	  fprintf (devp, "Not sending IP redirects on any interface.\n");
	if (DEFAULT_ROUTE_RIP_METRIC != 1)
	  fprintf ("IP RIP metric for direct interfaces is %d\n", 
		   DEFAULT_ROUTE_RIP_METRIC);
    }
    else {
	dolog(ss_frmem, himem - (unsl)end_mem);
	dolog(ss_tim, num_timers, max_timers);
	dolog(ss_tsk, num_tsks, max_tsks);
	dolog(ss_load,
	      av1_tsks/1000, (av1_tsks%1000)/10,
	      av5_tsks/1000, (av5_tsks%1000)/10,
	      av15_tsks/1000, (av15_tsks%1000)/10);
	dolog(gwbafl, nbufs, nbfree, bufshr, buflow); 
	if (!ip_send_redirects) 
	  dlog ("Not sending IP redirects on any interface.\n");
	if (DEFAULT_ROUTE_RIP_METRIC != 1)
	  dolog ("IP RIP metric for direct interfaces is %d\n", 
		 DEFAULT_ROUTE_RIP_METRIC);
   }
}

/* Fill in a status packet.  Returns the length of the status data. */
int
stat_fill(pkt)
reg statpkt *pkt;
{
    int len;
    reg net *netp;
    reg netinfo *netip;
    dct *devp;
    ext int systod, restarts;
    ext int num_timers, max_timers;
    ext int num_tsks, max_tsks, av1_tsks, av5_tsks, av15_tsks;
    ext unsl himem;
    ext byte *end_mem;

    pkt->magic = STAT_MAGIC;
    pkt->version = STAT_VERSION;
    pkt->type = mach_type;
    copy(name, pkt->name, STAT_NAME_LEN);
    pkt->uptime = systod;
    pkt->restarts = restarts;
    pkt->free_mem = himem - (unsl)end_mem;
    pkt->num_tsks = num_tsks;
    pkt->max_tsks = max_tsks;
    pkt->num_timers = num_timers;
    pkt->max_timers = max_timers;
    pkt->load1 = av1_tsks;
    pkt->load5 = av5_tsks;
    pkt->load15 = av15_tsks;
    pkt->nnets = nnets;
    pkt->nprots = 0; /* nprots; I havn't figured out how to do
		      * protocol information yet. */
    len = sizeof(statpkt);

    netip = (netinfo *)(pkt + 1);
    for (netp = nets; netp < &nets[nnets]; netp++) {
	netip->type = netp->n_type;
	netip->intnum = netp->n_int;
	netip->netnum = netp->n_net;
	netip->pkti = netp->n_pkti;
	netip->pkto = netp->n_pkto;
	netip->byti = netp->n_byti;
	netip->byto = netp->n_byto;
	netip->ioin = netp->n_ioin;
	netip->ioout = netp->n_ioout;
	netip->ovfin = netp->n_ovfin;
	netip->ovfout = netp->n_ovfout;
	netip->disc = netp->n_disc;
	devp = netp->n_idevp;
	if (devp->d_dev2 != NULL)
	  netip->devlen = ((int (*)())(devp->d_dev2))(devp, netip + 1);
	else
	  netip->devlen = 0;
	len += sizeof(netinfo) + netip->devlen;
	netip = (netinfo *)((byte *)(netip + 1) + netip->devlen);
    }
    return len;
}
