/*	$OpenBSD: rstat_proc.c,v 1.10 1998/07/10 08:06:10 deraadt Exp $	*/

/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 *
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 *
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 *
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 *
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 *
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */
#ifndef lint
/*static char sccsid[] = "from: @(#)rpc.rstatd.c 1.1 86/09/25 Copyr 1984 Sun Micro";*/
/*static char sccsid[] = "from: @(#)rstat_proc.c	2.2 88/08/01 4.0 RPCSRC";*/
static char rcsid[] = "$OpenBSD: rstat_proc.c,v 1.10 1998/07/10 08:06:10 deraadt Exp $";
#endif

/*
 * rstat service:  built with rstat.x and derived from rpc.rstatd.c
 *
 * Copyright (c) 1984 by Sun Microsystems, Inc.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>

#include <limits.h>
#include <rpc/rpc.h>
#include <sys/socket.h>
#include <syslog.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <unistd.h>

#if defined(__linux__)

#include <assert.h>
#include <ctype.h>

#define	CP_USER		0
#define	CP_NICE		1
#define	CP_SYS		2
#define	CP_INTR		3
#define	CP_IDLE		4
#define	CPUSTATES	5
static int cp_xlat[4] = { CP_USER, CP_NICE, CP_SYS, CP_IDLE };

/*
 * System wide statistics counters.
 */
struct vmmeter {
	/*
	 * General system activity.
	 */
	u_int v_swtch;		/* context switches */
	u_int v_trap;		/* calls to trap */
	u_int v_syscall;	/* calls to syscall() */
	u_int v_intr;		/* device interrupts */
	u_int v_soft;		/* software interrupts */
	u_int v_faults;		/* total faults taken */
	/*
	 * Virtual memory activity.
	 */
	u_int v_lookups;	/* object cache lookups */
	u_int v_hits;		/* object cache hits */
	u_int v_vm_faults;	/* number of address memory faults */
	u_int v_cow_faults;	/* number of copy-on-writes */
	u_int v_swpin;		/* swapins */
	u_int v_swpout;		/* swapouts */
	u_int v_pswpin;		/* pages swapped in */
	u_int v_pswpout;	/* pages swapped out */
	u_int v_pageins;	/* number of pageins */
	u_int v_pageouts;	/* number of pageouts */
	u_int v_pgpgin;		/* pages paged in */
	u_int v_pgpgout;	/* pages paged out */
	u_int v_intrans;	/* intransit blocking page faults */
	u_int v_reactivated;	/* number of pages reactivated from free list */
	u_int v_rev;		/* revolutions of the hand */
	u_int v_scan;		/* scans in page out daemon */
	u_int v_dfree;		/* pages freed by daemon */
	u_int v_pfree;		/* pages freed by exiting processes */
	u_int v_zfod;		/* pages zero filled on demand */
	u_int v_nzfod;		/* number of zfod's created */
	/*
	 * Distribution of page usages.
	 */
	u_int v_page_size;	/* page size in bytes */
	u_int v_kernel_pages;	/* number of pages in use by kernel */
	u_int v_free_target;	/* number of pages desired free */
	u_int v_free_min;	/* minimum number of pages desired free */
	u_int v_free_count;	/* number of pages free */
	u_int v_wire_count;	/* number of pages wired down */
	u_int v_active_count;	/* number of pages active */
	u_int v_inactive_target; /* number of pages desired inactive */
	u_int v_inactive_count;  /* number of pages inactive */
};
static struct	vmmeter cnt;

/* poseur disk entry to hold the information we're interested in. */
struct _disk {
	int		 *dk_select;	/* Display stats for selected disks. */
	char		**dk_name;	/* Disk names (sd0, wd1, etc). */
	u_int64_t	 *dk_xfer;	/* # of transfers. */
	u_int64_t	 *dk_seek;	/* # of seeks (currently unused). */
	u_int64_t	 *dk_bytes;	/* # of bytes transfered. */
	struct timeval	 *dk_time;	/* Time spent in disk i/o. */
	long	tk_nin;			/* TTY Chars in. */
	long	tk_nout;		/* TTY Chars out. */
	long	cp_time[CPUSTATES];	/* System timer ticks. */
};

#define	DK_NDRIVE	4
static int dk_ndrive = 0;

static int		cdk_select[DK_NDRIVE];
static char *		cdk_name[DK_NDRIVE];
static u_int64_t	cdk_xfer[DK_NDRIVE];
static u_int64_t	cdk_seek[DK_NDRIVE];
static u_int64_t	cdk_bytes[DK_NDRIVE];
static struct timeval	cdk_time[DK_NDRIVE];
static struct _disk cur = {
	cdk_select, cdk_name, cdk_xfer, cdk_seek, cdk_bytes, cdk_time,
	0, 0,
	{0, 0, 0, 0, 0}
};

#if 0
static int		ldk_select[DK_NDRIVE];
static char *		ldk_name[DK_NDRIVE];
static u_int64_t	ldk_xfer[DK_NDRIVE];
static u_int64_t	ldk_seek[DK_NDRIVE];
static u_int64_t	ldk_bytes[DK_NDRIVE];
static struct timeval	ldk_time[DK_NDRIVE];
static struct _disk last = {
	ldk_select, ldk_name, ldk_xfer, ldk_seek, ldk_bytes, ldk_time,
	0, 0,
	{0, 0, 0, 0, 0}
};
#endif

#else	/* __linux__ */

#include <kvm.h>
#include <nlist.h>

#ifdef BSD
#include <sys/vmmeter.h>
#include <sys/dkstat.h>
#include "dkstats.h"
#else
#include <sys/dk.h>
#endif
#endif	/* __linux__ */

#include <net/if.h>

#undef FSHIFT			 /* Use protocol's shift and scale values */
#undef FSCALE
#undef DK_NDRIVE
#undef CPUSTATES
#undef if_ipackets
#undef if_ierrors
#undef if_opackets
#undef if_oerrors
#undef if_collisions
#include <rpcsvc/rstat.h>

#ifdef BSD
#define BSD_CPUSTATES	5	/* Use protocol's idea of CPU states */
int	cp_xlat[CPUSTATES] = { CP_USER, CP_NICE, CP_SYS, CP_IDLE };
#endif

#if defined(__linux__)
struct user_net_device_stats {
    unsigned long rx_packets;	/* total packets received       */
    unsigned long tx_packets;	/* total packets transmitted    */
    unsigned long rx_bytes;	/* total bytes received         */
    unsigned long tx_bytes;	/* total bytes transmitted      */
    unsigned long rx_errors;	/* bad packets received         */
    unsigned long tx_errors;	/* packet transmit problems     */
    unsigned long rx_dropped;	/* no space in linux buffers    */
    unsigned long tx_dropped;	/* no space available in linux  */
    unsigned long rx_multicast;	/* multicast packets received   */
    unsigned long rx_compressed;
    unsigned long tx_compressed;
    unsigned long collisions;

    /* detailed rx_errors: */
    unsigned long rx_length_errors;
    unsigned long rx_over_errors;	/* receiver ring buff overflow  */
    unsigned long rx_crc_errors;	/* recved pkt with crc error    */
    unsigned long rx_frame_errors;	/* recv'd frame alignment error */
    unsigned long rx_fifo_errors;	/* recv'r fifo overrun          */
    unsigned long rx_missed_errors;	/* receiver missed packet     */
    /* detailed tx_errors */
    unsigned long tx_aborted_errors;
    unsigned long tx_carrier_errors;
    unsigned long tx_fifo_errors;
    unsigned long tx_heartbeat_errors;
    unsigned long tx_window_errors;
};

struct interface {
    struct interface *next;

    char name[IFNAMSIZ];	/* interface name        */
    short type;			/* if type               */
    short flags;		/* various flags         */
    int metric;			/* routing metric        */
    int mtu;			/* MTU value             */
    int tx_queue_len;		/* transmit queue length */
    struct ifmap map;		/* hardware setup        */
    struct sockaddr addr;	/* IP address            */
    struct sockaddr dstaddr;	/* P-P IP address        */
    struct sockaddr broadaddr;	/* IP broadcast address  */
    struct sockaddr netmask;	/* IP network mask       */
    struct sockaddr ipxaddr_bb;	/* IPX network address   */
    struct sockaddr ipxaddr_sn;	/* IPX network address   */
    struct sockaddr ipxaddr_e3;	/* IPX network address   */
    struct sockaddr ipxaddr_e2;	/* IPX network address   */
    struct sockaddr ddpaddr;	/* Appletalk DDP address */
    struct sockaddr ecaddr;	/* Econet address        */
    int has_ip;
    int has_ipx_bb;
    int has_ipx_sn;
    int has_ipx_e3;
    int has_ipx_e2;
    int has_ax25;
    int has_ddp;
    int has_econet;
    char hwaddr[32];		/* HW address            */
    int statistics_valid;
    struct user_net_device_stats stats;		/* statistics            */
    int keepalive;		/* keepalive value for SLIP */
    int outfill;		/* outfill value for SLIP */
};

#else	/* __linux__ */

struct nlist nl[] = {
#define	X_CNT		0
	{ "_cnt" },
#define	X_IFNET		1
	{ "_ifnet" },
#define	X_BOOTTIME	2
	{ "_boottime" },
#ifndef BSD
#define	X_HZ		3
	{ "_hz" },
#define	X_CPTIME	4
	{ "_cp_time" },
#define	X_DKXFER	5
	{ "_dk_xfer" },
#endif
	{ NULL },
};

#ifdef BSD
extern int dk_ndrive;		/* from dkstats.c */
extern struct _disk cur, last;
char *memf = NULL, *nlistf = NULL;
#endif
int hz;

struct ifnet_head ifnetq;	/* chain of ethernet interfaces */
int numintfs;
int stats_service();
kvm_t *kfd;

#endif	/* __linux__ */

extern int from_inetd;
int sincelastreq = 0;		/* number of alarms since last request */
extern int closedown;

union {
	struct stats s1;
	struct statsswtch s2;
	struct statstime s3;
} stats_all;

static void setup(void);
static void updatestat(int signo);
static int havedisk(void);
void	rstat_service(struct svc_req *rqstp, SVCXPRT *transp);

static int stat_is_init = 0;
extern int errno;

#ifndef FSCALE
#define FSCALE (1 << 8)
#endif

static void
stat_init(void)
{
	stat_is_init = 1;
	setup();
	updatestat(0);
	(void) signal(SIGALRM, updatestat);
	alarm(1);
}

statstime *
rstatproc_stats_3_svc(void *arg, struct svc_req *rqstp)
{
	(void)&arg; (void)&rqstp;
	if (!stat_is_init)
		stat_init();
	sincelastreq = 0;
	return (&stats_all.s3);
}

statsswtch *
rstatproc_stats_2_svc(void *arg, struct svc_req *rqstp)
{
	(void)&arg; (void)&rqstp;
	if (!stat_is_init)
		stat_init();
	sincelastreq = 0;
	return (&stats_all.s2);
}

stats *
rstatproc_stats_1_svc(void *arg, struct svc_req *rqstp)
{
	(void)&arg; (void)&rqstp;
	if (!stat_is_init)
		stat_init();
	sincelastreq = 0;
	return (&stats_all.s1);
}

u_int *
rstatproc_havedisk_3_svc(void *arg, struct svc_req *rqstp)
{
	static u_int have;

	(void)&arg; (void)&rqstp;
	if (!stat_is_init)
		stat_init();
	sincelastreq = 0;
	have = havedisk();
	return (&have);
}

u_int *
rstatproc_havedisk_2_svc(void *arg, struct svc_req *rqstp)
{
	(void)&arg; (void)&rqstp;
	return (rstatproc_havedisk_3_svc(arg, rqstp));
}

u_int *
rstatproc_havedisk_1_svc(void *arg, struct svc_req *rqstp)
{
	(void)&arg; (void)&rqstp;
	return (rstatproc_havedisk_3_svc(arg, rqstp));
}

#if defined(__linux__)

struct _ldisk {
	unsigned int xfer[4];
	unsigned int rio[4];
	unsigned int wio[4];
	unsigned int rblk[4];
	unsigned int wblk[4];
};

static 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, struct _ldisk *d)
{
  static int stat;
#define	BUFFSIZE	1024
  char buff[BUFFSIZE];

  if ((stat=open("/proc/stat", O_RDONLY, 0)) != -1) {
    char* b;
    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, "disk ");
    sscanf(b, "disk %u %u %u %u", d->xfer+0, d->xfer+1, d->xfer+2, d->xfer+3);
    b = strstr(buff, "disk_rio ");
    sscanf(b, "disk_rio %u %u %u %u", d->rio+0, d->rio+1, d->rio+2, d->rio+3);
    b = strstr(buff, "disk_wio ");
    sscanf(b, "disk_rio %u %u %u %u", d->wio+0, d->wio+1, d->wio+2, d->wio+3);
    b = strstr(buff, "disk_rblk ");
    sscanf(b, "disk_rblk %u %u %u %u", d->rblk+0,d->rblk+1,d->rblk+2,d->rblk+3);
    b = strstr(buff, "disk_wblk ");
    sscanf(b, "disk_wblk %u %u %u %u", d->wblk+0,d->wblk+1,d->wblk+2,d->wblk+3);
    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);
  }
}

static int procnetdev_vsn;

static char *
get_name(char *name, char *p)
{
    while (isspace(*p))
	p++;
    while (*p) {
	if (isspace(*p))
	    break;
	if (*p == ':') {	/* could be an alias */
	    char *dot = p, *dotname = name;
	    *name++ = *p++;
	    while (isdigit(*p))
		*name++ = *p++;
	    if (*p != ':') {	/* it wasn't, backup */
		p = dot;
		name = dotname;
	    }
	    if (*p == '\0')
		return NULL;
	    p++;
	    break;
	}
	*name++ = *p++;
    }
    *name++ = '\0';
    return p;
}

static int procnetdev_version(char *buf)
{
    if (strstr(buf, "compressed"))
	return 3;
    if (strstr(buf, "bytes"))
	return 2;
    return 1;
}

static int
get_dev_fields(char *bp, struct interface *ife)
{
    switch (procnetdev_vsn) {
    case 3:
	sscanf(bp,
	"%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
	       &ife->stats.rx_bytes,
	       &ife->stats.rx_packets,
	       &ife->stats.rx_errors,
	       &ife->stats.rx_dropped,
	       &ife->stats.rx_fifo_errors,
	       &ife->stats.rx_frame_errors,
	       &ife->stats.rx_compressed,
	       &ife->stats.rx_multicast,

	       &ife->stats.tx_bytes,
	       &ife->stats.tx_packets,
	       &ife->stats.tx_errors,
	       &ife->stats.tx_dropped,
	       &ife->stats.tx_fifo_errors,
	       &ife->stats.collisions,
	       &ife->stats.tx_carrier_errors,
	       &ife->stats.tx_compressed);
	break;
    case 2:
	sscanf(bp, "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
	       &ife->stats.rx_bytes,
	       &ife->stats.rx_packets,
	       &ife->stats.rx_errors,
	       &ife->stats.rx_dropped,
	       &ife->stats.rx_fifo_errors,
	       &ife->stats.rx_frame_errors,

	       &ife->stats.tx_bytes,
	       &ife->stats.tx_packets,
	       &ife->stats.tx_errors,
	       &ife->stats.tx_dropped,
	       &ife->stats.tx_fifo_errors,
	       &ife->stats.collisions,
	       &ife->stats.tx_carrier_errors);
	ife->stats.rx_multicast = 0;
	break;
    case 1:
	sscanf(bp, "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
	       &ife->stats.rx_packets,
	       &ife->stats.rx_errors,
	       &ife->stats.rx_dropped,
	       &ife->stats.rx_fifo_errors,
	       &ife->stats.rx_frame_errors,

	       &ife->stats.tx_packets,
	       &ife->stats.tx_errors,
	       &ife->stats.tx_dropped,
	       &ife->stats.tx_fifo_errors,
	       &ife->stats.collisions,
	       &ife->stats.tx_carrier_errors);
	ife->stats.rx_bytes = 0;
	ife->stats.tx_bytes = 0;
	ife->stats.rx_multicast = 0;
	break;
    }
    return 0;
}

static int hz;
static struct timeval tm, btm;
static double avrun[3];
static unsigned int cpu_use[2], cpu_nic[2], cpu_sys[2];
static unsigned long cpu_idl[2];
static struct _ldisk disk[2];
static unsigned int pgpgin[2], pgpgout[2], pswpin[2], pswpout[2];
static unsigned int inter[2], ticks[2], ctxt[2];
#define	_DELTA(_x, _i)	( (_x)[(_i)] - (_x)[!(_i)] )

extern int uptime(double *, double *);
extern void loadavg(double *, double *, double *);

static void
convert_stats(void)
{
	static int tog = 0;
	double up;
	int i;

	hz = sysconf(_SC_CLK_TCK);
	gettimeofday(&tm, (struct timezone *) 0);

	i = uptime(&up, NULL);
	btm.tv_sec = tm.tv_sec - i;
	btm.tv_usec = 0;
	up -= i;
	if (up > 0)
		btm.tv_usec = 1000000 * up;

	loadavg(&avrun[0], &avrun[1], &avrun[2]);
	getstat(cpu_use+tog,cpu_nic+tog,cpu_sys+tog,cpu_idl+tog,
		pgpgin+tog,pgpgout+tog,pswpin+tog,pswpout+tog,
		inter+tog,ticks+tog,ctxt+tog, disk+tog);
	tog = !tog;

	dk_ndrive = 0;
	for (i = 0; i < 4; i++) {
		if (disk[tog].xfer[i] == 0)
			continue;
		cur.dk_xfer[dk_ndrive] = disk[tog].xfer[i] - disk[!tog].xfer[i];
		dk_ndrive++;
	}

	cur.cp_time[CP_USER] = _DELTA(cpu_use, tog);
	cur.cp_time[CP_NICE] = _DELTA(cpu_nic, tog);
	cur.cp_time[CP_SYS]  = _DELTA(cpu_sys, tog);
	cur.cp_time[CP_INTR] = 0;
	cur.cp_time[CP_SYS] = (_DELTA(cpu_idl, tog)) % UINT_MAX;

	cnt.v_pgpgin = pgpgin[tog];
	cnt.v_pgpgout = pgpgout[tog];
	cnt.v_pswpin = pswpin[tog];
	cnt.v_pswpout = pswpout[tog];
	cnt.v_intr = inter[tog];
	cnt.v_swtch = ctxt[tog];
}

static void
updatestat(int signo)
{
	int i, save_errno = errno;

	(void)&signo;
#ifdef DEBUG
	syslog(LOG_DEBUG, "entering updatestat");
#endif
	if (sincelastreq >= closedown) {
#ifdef DEBUG
		syslog(LOG_DEBUG, "about to closedown");
#endif
		if (from_inetd) {
			exit(0);
		} else {
			stat_is_init = 0;
			return;
		}
	}
	sincelastreq++;

/* ======================================= */
	convert_stats();

	memset(&stats_all, 0, sizeof(stats_all));

	for (i = 0; i < dk_ndrive && i < DK_NDRIVE; i++)
		stats_all.s1.dk_xfer[i] = cur.dk_xfer[i];

	for (i = 0; i < CPUSTATES; i++)
		stats_all.s1.cp_time[i] += cur.cp_time[cp_xlat[i]];

	stats_all.s2.avenrun[0] = avrun[0] * FSCALE;
	stats_all.s2.avenrun[1] = avrun[1] * FSCALE;
	stats_all.s2.avenrun[2] = avrun[2] * FSCALE;

	stats_all.s2.boottime.tv_sec = btm.tv_sec;
	stats_all.s2.boottime.tv_usec = btm.tv_usec;

#ifdef DEBUG
	syslog(LOG_DEBUG, "%d %d %d %d", stats_all.s1.cp_time[0],
	    stats_all.s1.cp_time[1], stats_all.s1.cp_time[2],
	    stats_all.s1.cp_time[3]);
#endif

	stats_all.s1.v_pgpgin = cnt.v_pgpgin;
	stats_all.s1.v_pgpgout = cnt.v_pgpgout;
	stats_all.s1.v_pswpin = cnt.v_pswpin;
	stats_all.s1.v_pswpout = cnt.v_pswpout;
	stats_all.s1.v_intr = cnt.v_intr;
	gettimeofday(&tm, (struct timezone *) 0);
	stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) +
	    hz*(tm.tv_usec - btm.tv_usec)/1000000;
	stats_all.s2.v_swtch = cnt.v_swtch;

    {	FILE *fh;
	if ((fh = fopen("/proc/net/dev", "r")) != NULL) {
	    char buf[512], *s;
	    struct interface myif, *ife = &myif;

	    fgets(buf, sizeof buf, fh);
	    fgets(buf, sizeof buf, fh);
	    procnetdev_vsn = procnetdev_version(buf);

	    while(fgets(buf, sizeof buf, fh)) {
		s = get_name(ife->name, buf);
		get_dev_fields(s, ife);
		stats_all.s1.if_ipackets += ife->stats.rx_packets;
		stats_all.s1.if_opackets += ife->stats.tx_packets;
		stats_all.s1.if_ierrors += ife->stats.rx_errors;
		stats_all.s1.if_oerrors += ife->stats.tx_errors;
		stats_all.s1.if_collisions += ife->stats.collisions;
	    }

	    fclose(fh);
	}
    }

/* ======================================= */

	gettimeofday((struct timeval *)&stats_all.s3.curtime,
		(struct timezone *) 0);
	alarm(1);
	errno = save_errno;
}

static void
setup(void)
{
	/* Prime the pump */
	convert_stats();
	sleep(1);
}


/*
 * returns true if have a disk
 */
static int
havedisk(void)
{
	return dk_ndrive != 0;
}

#else	/* __linux__ */

static void
updatestat(void)
{
	long off;
	int i, save_errno = errno;
	struct vmmeter cnt;
	struct ifnet ifnet;
	double avrun[3];
	struct timeval tm, btm;
#ifdef BSD
	long *cp_time = cur.cp_time;
#endif

#ifdef DEBUG
	syslog(LOG_DEBUG, "entering updatestat");
#endif
	if (sincelastreq >= closedown) {
#ifdef DEBUG
		syslog(LOG_DEBUG, "about to closedown");
#endif
		if (from_inetd)
			exit(0);
		else {
			stat_is_init = 0;
			return;
		}
	}
	sincelastreq++;

	/*
	 * dkreadstats reads in the "disk_count" as well as the "disklist"
	 * statistics.  It also retrieves "hz" and the "cp_time" array.
	 */
	dkreadstats();
	memset(stats_all.s1.dk_xfer, '\0', sizeof(stats_all.s1.dk_xfer));
	for (i = 0; i < dk_ndrive && i < DK_NDRIVE; i++)
		stats_all.s1.dk_xfer[i] = cur.dk_xfer[i];
	
#ifdef BSD
	for (i = 0; i < CPUSTATES; i++)
		stats_all.s1.cp_time[i] = cp_time[cp_xlat[i]];
#else
	if (kvm_read(kfd, (long)nl[X_HZ].n_value, (char *)&hz, sizeof hz) !=
	    sizeof hz) {
		syslog(LOG_ERR, "can't read hz from kmem");
		exit(1);
	}
 	if (kvm_read(kfd, (long)nl[X_CPTIME].n_value,
	    (char *)stats_all.s1.cp_time, sizeof (stats_all.s1.cp_time))
	    != sizeof (stats_all.s1.cp_time)) {
		syslog(LOG_ERR, "can't read cp_time from kmem");
		exit(1);
	}
#endif
#ifdef BSD
	(void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0]));
#endif
	stats_all.s2.avenrun[0] = avrun[0] * FSCALE;
	stats_all.s2.avenrun[1] = avrun[1] * FSCALE;
	stats_all.s2.avenrun[2] = avrun[2] * FSCALE;
 	if (kvm_read(kfd, (long)nl[X_BOOTTIME].n_value,
	    (char *)&btm, sizeof (stats_all.s2.boottime))
	    != sizeof (stats_all.s2.boottime)) {
		syslog(LOG_ERR, "can't read boottime from kmem");
		exit(1);
	}
	stats_all.s2.boottime.tv_sec = btm.tv_sec;
	stats_all.s2.boottime.tv_usec = btm.tv_usec;


#ifdef DEBUG
	syslog(LOG_DEBUG, "%d %d %d %d", stats_all.s1.cp_time[0],
	    stats_all.s1.cp_time[1], stats_all.s1.cp_time[2],
	    stats_all.s1.cp_time[3]);
#endif

 	if (kvm_read(kfd, (long)nl[X_CNT].n_value, (char *)&cnt, sizeof cnt) !=
	    sizeof cnt) {
		syslog(LOG_ERR, "can't read cnt from kmem");
		exit(1);
	}
	stats_all.s1.v_pgpgin = cnt.v_pgpgin;
	stats_all.s1.v_pgpgout = cnt.v_pgpgout;
	stats_all.s1.v_pswpin = cnt.v_pswpin;
	stats_all.s1.v_pswpout = cnt.v_pswpout;
	stats_all.s1.v_intr = cnt.v_intr;
	gettimeofday(&tm, (struct timezone *) 0);
	stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) +
	    hz*(tm.tv_usec - btm.tv_usec)/1000000;
	stats_all.s2.v_swtch = cnt.v_swtch;

#ifndef BSD
 	if (kvm_read(kfd, (long)nl[X_DKXFER].n_value,
	    (char *)stats_all.s1.dk_xfer, sizeof (stats_all.s1.dk_xfer))
	    != sizeof (stats_all.s1.dk_xfer)) {
		syslog(LOG_ERR, "can't read dk_xfer from kmem");
		exit(1);
	}
#endif

	stats_all.s1.if_ipackets = 0;
	stats_all.s1.if_opackets = 0;
	stats_all.s1.if_ierrors = 0;
	stats_all.s1.if_oerrors = 0;
	stats_all.s1.if_collisions = 0;
	for (off = (long)ifnetq.tqh_first, i = 0; off && i < numintfs; i++) {
		if (kvm_read(kfd, off, (char *)&ifnet, sizeof ifnet) !=
		    sizeof ifnet) {
			syslog(LOG_ERR, "can't read ifnet from kmem");
			exit(1);
		}
		stats_all.s1.if_ipackets += ifnet.if_data.ifi_ipackets;
		stats_all.s1.if_opackets += ifnet.if_data.ifi_opackets;
		stats_all.s1.if_ierrors += ifnet.if_data.ifi_ierrors;
		stats_all.s1.if_oerrors += ifnet.if_data.ifi_oerrors;
		stats_all.s1.if_collisions += ifnet.if_data.ifi_collisions;
		off = (long)ifnet.if_list.tqe_next;
	}
	gettimeofday((struct timeval *)&stats_all.s3.curtime,
		(struct timezone *) 0);
	alarm(1);
	errno = save_errno;
}

static void
setup(void)
{
	struct ifnet ifnet;
	long off;
	char errbuf[_POSIX2_LINE_MAX];

	kfd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
	if (kfd == NULL) {
		syslog(LOG_ERR, "%s", errbuf);
		exit (1);
	}

	if (kvm_nlist(kfd, nl) != 0) {
		syslog(LOG_ERR, "can't get namelist");
		exit (1);
	}

	if (kvm_read(kfd, (long)nl[X_IFNET].n_value, &ifnetq,
	    sizeof ifnetq) != sizeof ifnetq) {
		syslog(LOG_ERR, "can't read ifnet queue head from kmem");
		exit(1);
	}

	numintfs = 0;
	for (off = (long)ifnetq.tqh_first; off;) {
		if (kvm_read(kfd, off, (char *)&ifnet, sizeof ifnet) !=
		    sizeof ifnet) {
			syslog(LOG_ERR, "can't read ifnet from kmem");
			exit(1);
		}
		numintfs++;
		off = (long)ifnet.if_list.tqe_next;
	}
#ifdef BSD
	dkinit(0);
#endif
}

/*
 * returns true if have a disk
 */
static int
havedisk(void)
{
#ifdef BSD
	return dk_ndrive != 0;
#else
	int i, cnt;
	long  xfer[DK_NDRIVE];

	if (kvm_nlist(kfd, nl) != 0) {
		syslog(LOG_ERR, "can't get namelist");
		exit (1);
	}

	if (kvm_read(kfd, (long)nl[X_DKXFER].n_value,
		     (char *)xfer, sizeof xfer) != sizeof xfer) {
		syslog(LOG_ERR, "can't read dk_xfer from kmem");
		exit(1);
	}
	cnt = 0;
	for (i=0; i < DK_NDRIVE; i++)
		cnt += xfer[i];
	return (cnt != 0);
#endif
}
#endif	/* __linux__ */

void
rstat_service(struct svc_req *rqstp, SVCXPRT *transp)
{
	union {
		int fill;
	} argument;
	char *result;
	xdrproc_t xdr_argument, xdr_result;
	typedef char *(*localproc_t) __P((void *, struct svc_req *));
	localproc_t local;

	switch (rqstp->rq_proc) {
	case NULLPROC:
		(void)svc_sendreply(transp, xdr_void, (char *)NULL);
		return;
		break;

	case RSTATPROC_STATS:
		xdr_argument = (xdrproc_t)xdr_void;
		xdr_result = (xdrproc_t)xdr_statstime;
		switch (rqstp->rq_vers) {
		case RSTATVERS_ORIG:
			local = (localproc_t) rstatproc_stats_1_svc;
			break;
		case RSTATVERS_SWTCH:
			local = (localproc_t) rstatproc_stats_2_svc;
			break;
		case RSTATVERS_TIME:
			local = (localproc_t) rstatproc_stats_3_svc;
			break;
		default:
			svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
			return;
			break;
		}
		break;

	case RSTATPROC_HAVEDISK:
		xdr_argument = (xdrproc_t)xdr_void;
		xdr_result = (xdrproc_t)xdr_u_int;
		switch (rqstp->rq_vers) {
		case RSTATVERS_ORIG:
			local = (localproc_t) rstatproc_havedisk_1_svc;
			break;
		case RSTATVERS_SWTCH:
			local = (localproc_t) rstatproc_havedisk_2_svc;
			break;
		case RSTATVERS_TIME:
			local = (localproc_t) rstatproc_havedisk_3_svc;
			break;
		default:
			svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
			return;
			break;
		}
		break;

	default:
		svcerr_noproc(transp);
		return;
		break;
	}
	memset(&argument, 0, sizeof(argument));
	if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
		svcerr_decode(transp);
		return;
	}
	result = (*local)(&argument, rqstp);
	if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
		svcerr_systemerr(transp);
	}
	if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
		syslog(LOG_ERR, "unable to free arguments");
		exit(1);
	}
}
