$NetBSD: patch-ab,v 1.4 2005/05/02 18:15:57 jschauma Exp $

--- /dev/null	2005-04-12 21:39:02.000000000 -0400
+++ srclib/libmetrics/netbsd/metrics.c	2005-04-12 21:34:24.000000000 -0400
@@ -0,0 +1,1291 @@
+/*
+ *  First stab at support for metrics in NetBSD
+ *  by Jan Schaumann <jschauma@netbsd.org>
+ *  Thu Feb  3 22:33:36 EST 2005
+ *
+ */
+
+#include <kvm.h>
+
+#include <sys/param.h> 
+#include <sys/mount.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/user.h>
+#include <sys/resource.h>
+#include <sys/sched.h>
+#include <sys/stat.h>
+#include <sys/swap.h>
+#include <uvm/uvm_param.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <unistd.h>
+#include <err.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <paths.h>
+
+#include "interface.h"
+#include "libmetrics.h"
+
+#ifdef HAVE_SYS_STATVFS_H
+#define statfs statvfs
+#endif
+
+#define MIB_SWAPINFO_SIZE 3
+
+#ifndef MIN_NET_POLL_INTERVAL
+#define MIN_NET_POLL_INTERVAL 0.5
+#endif
+
+#ifndef MIN_CPU_POLL_INTERVAL
+#define MIN_CPU_POLL_INTERVAL 0.5
+#endif
+
+#ifndef UINT64_MAX
+#define UINT64_MAX	ULLONG_MAX
+#endif
+
+#define timertod(tvp) \
+    ((double)(tvp)->tv_sec + (double)(tvp)->tv_usec/(1000*1000))
+
+struct traffic {
+	uint64_t in_bytes;
+	uint64_t out_bytes;
+	uint64_t in_pkts;
+	uint64_t out_pkts;
+};
+
+static void get_netbw(double *, double *, double *, double *);
+static uint64_t counterdiff(uint64_t, uint64_t, uint64_t, uint64_t);
+
+static char	*makenetvfslist(void);
+static size_t	regetmntinfo(struct statfs **, long, const char **);
+static int	checkvfsname(const char *, const char **);
+static const char **makevfslist(char *);
+static float	find_disk_space(double *, double *);
+
+// XXX
+//static int	vm_state(int);
+
+static int 	use_vm_swap_info = 0;
+static int 	mibswap[MIB_SWAPINFO_SIZE];
+static size_t 	mibswap_size;
+static kvm_t 	*kd = NULL;
+static int 	pagesize;
+static int	skipvfs;
+
+/* Function prototypes */
+static long percentages(int cnt, u_int64_t *out, register u_int64_t *new,
+                          register u_int64_t *old, u_int64_t *diffs);
+ 
+/*
+ * This function is called only once by the gmond.  Use to 
+ * initialize data structures, etc or just return SYNAPSE_SUCCESS;
+ */
+g_val_t
+metric_init(void)
+{
+   g_val_t val;
+   kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "metric_init()");
+   pagesize = sysconf(_SC_PAGESIZE);
+
+   /* Initialize some counters */
+   get_netbw(NULL, NULL, NULL, NULL);
+   cpu_state(-1);
+   val.int32 = SYNAPSE_SUCCESS;
+   return val;
+}
+
+g_val_t
+cpu_num_func ( void )
+{
+   g_val_t val;
+   int ncpu;
+   size_t len = sizeof (int);
+   int mib[2];
+
+   mib[0] = CTL_HW;
+   mib[1] = HW_NCPU;
+   if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1 || !len)
+        ncpu = 1;
+
+   val.uint16 = ncpu;
+   return val;
+}
+
+g_val_t
+cpu_speed_func ( void )
+{
+   g_val_t val;
+   size_t len;
+   long cpu_speed;
+
+   cpu_speed = 0;
+
+#if (__NetBSD_Version__ > 299000000)
+   if (sysctlbyname("machdep.est.frequency.target", &cpu_speed, &len, NULL, 0) == -1)
+	val.uint16 = 0;
+#endif
+
+   val.uint16 = cpu_speed /= 1000000;
+   return val;
+}
+
+g_val_t
+mem_total_func ( void )
+{
+   g_val_t val;
+   size_t len;
+   int total;
+   int mib[2];
+
+   mib[0] = CTL_HW;
+   mib[1] = HW_PHYSMEM;
+   len = sizeof (total);
+
+   sysctl(mib, 2, &total, &len, NULL, 0);
+   total /= 1024;
+   val.uint32 = total;
+   return val;
+}
+
+g_val_t
+swap_total_func ( void )
+{
+	g_val_t val;
+
+	int mib[2];
+	struct uvmexp_sysctl uvmexp;
+	size_t len;
+	int totswap;
+
+	val.uint32 = 0;
+	totswap = 0;
+
+	mib[0] = CTL_VM;
+	mib[1] = VM_UVMEXP2;
+
+	len = sizeof(uvmexp);
+
+	if((sysctl(mib, 2, &uvmexp, &len, NULL, 0) < 0)
+			|| !len)
+		totswap = 0;
+	else
+		totswap = uvmexp.swpages;
+
+	val.uint32 = totswap * (pagesize / 1024);
+
+	return val;
+}
+
+g_val_t
+boottime_func ( void )
+{
+   g_val_t val;
+   struct timeval  boottime;
+   int mib[2];
+   size_t size;
+
+   mib[0] = CTL_KERN;
+   mib[1] = KERN_BOOTTIME;
+   size = sizeof(boottime);
+   if (sysctl(mib, 2, &boottime, &size, NULL, 0) == -1)
+       val.uint32 = 0;
+
+   val.uint32 = boottime.tv_sec;
+
+   return val;
+}
+
+g_val_t
+sys_clock_func ( void )
+{
+   g_val_t val;
+
+   val.uint32 = time(NULL);
+   return val;
+}
+
+g_val_t
+machine_type_func ( void )
+{
+   g_val_t val;
+   char machine_type[MAX_G_STRING_SIZE];
+   size_t len = MAX_G_STRING_SIZE;
+   int mib[2];
+
+   mib[0] = CTL_HW;
+   mib[1] = HW_MACHINE;
+
+   if (sysctl(mib, 2, &machine_type, &len, NULL, 0) == -1 || !len)
+        strncpy( val.str, "x86", MAX_G_STRING_SIZE );
+
+   strncpy( val.str, machine_type, MAX_G_STRING_SIZE );
+   return val;
+}
+
+g_val_t
+os_name_func ( void )
+{
+   g_val_t val;
+   char osname[MAX_G_STRING_SIZE];
+   size_t len = MAX_G_STRING_SIZE;
+   int mib[2];
+
+   mib[0] = CTL_KERN;
+   mib[1] = KERN_OSTYPE;
+
+   if (sysctl(mib, 2, &osname, &len, NULL, 0) == -1 || !len)
+        strncpy( val.str, "NetBSD", MAX_G_STRING_SIZE );
+
+   strncpy( val.str, osname, MAX_G_STRING_SIZE );
+ 
+   return val;
+}        
+
+g_val_t
+os_release_func ( void )
+{
+   g_val_t val;
+   int mib[2];
+   size_t len;
+   char *prefix, buf[1024];
+
+   prefix = "";
+
+   mib[0] = CTL_KERN;
+   mib[1] = KERN_OSRELEASE;
+   len = sizeof(buf);
+   if (sysctl(mib, 2, &buf, &len, NULL, 0) == -1)
+        strncpy( val.str, "Unknown", MAX_G_STRING_SIZE );
+
+   strncpy( val.str, buf, MAX_G_STRING_SIZE );
+
+   return val;
+}
+
+int
+cpu_state(int which)
+{
+	u_int64_t cp_time[CPUSTATES];
+	u_int64_t cp_diff[CPUSTATES];
+	static u_int64_t cp_old[CPUSTATES];
+	static u_int64_t cpu_states[CPUSTATES];
+	static struct timeval this_time, last_time;
+	struct timeval time_diff;
+	size_t len = sizeof(cp_time);
+	int i;
+
+	if (which == -1) {
+		bzero(cp_old, sizeof(cp_old));
+		bzero(&last_time, sizeof(last_time));
+		return 0.0;
+	}
+
+	gettimeofday(&this_time, NULL);
+	timersub(&this_time, &last_time, &time_diff);
+	if (timertod(&time_diff) < MIN_CPU_POLL_INTERVAL) {
+		goto output;
+	}
+	last_time = this_time;
+
+	/* puts kern.cp_time array into cp_time */
+	if (sysctlbyname("kern.cp_time", &cp_time, &len, NULL, 0) == -1) {
+		warn("kern.cp_time");
+		return 0.0;
+	}
+	/* Use percentages function lifted from top(1) to figure percentages */
+	percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
+
+output:
+	return cpu_states[which];
+
+}
+
+g_val_t
+cpu_user_func ( void )
+{
+   g_val_t val;
+   int res;
+
+   static u_int64_t cp_time[CPUSTATES];
+   static u_int64_t cp_old[CPUSTATES];
+   static u_int64_t cp_diff[CPUSTATES];
+   static u_int64_t cpu_states[CPUSTATES];
+   static long tot;
+   size_t len = sizeof(cp_time);
+   int mib[2];
+
+   mib[0] = CTL_KERN;
+   mib[1] = KERN_CP_TIME;
+
+   /* Copy the last cp_time into cp_old */
+   memcpy(&cp_old, &cp_time, CPUSTATES*sizeof(long));
+   /* puts kern.cp_time array into cp_time */
+   if (sysctl(mib, 2, &cp_time, &len, NULL, 0) == -1 || !len)
+      res = 0.0;
+   else {
+	   /* Use percentages function lifted from top(1) to figure percentages */
+	   tot = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
+	   res = cpu_states[CP_USER];
+   }
+   val.f = (float)res/10;
+   return val;
+}
+
+g_val_t
+cpu_nice_func ( void )
+{
+   g_val_t val;
+   int res;
+
+   static u_int64_t cp_time[CPUSTATES];
+   static u_int64_t cp_old[CPUSTATES];
+   static u_int64_t cp_diff[CPUSTATES];
+   static u_int64_t cpu_states[CPUSTATES];
+   static long tot;
+   size_t len = sizeof(cp_time);
+   int mib[2];
+
+   mib[0] = CTL_KERN;
+   mib[1] = KERN_CP_TIME;
+
+   /* Copy the last cp_time into cp_old */
+   memcpy(&cp_old, &cp_time, CPUSTATES*sizeof(long));
+   /* puts kern.cp_time array into cp_time */
+   if (sysctl(mib, 2, &cp_time, &len, NULL, 0) == -1 || !len)
+      res = 0.0;
+   else {
+	   /* Use percentages function lifted from top(1) to figure percentages */
+	   tot = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
+	   res = cpu_states[CP_NICE];
+   }
+   val.f = (float)res/10;
+   return val;
+}
+
+g_val_t 
+cpu_system_func ( void )
+{
+   g_val_t val;
+   int res;
+
+   static u_int64_t cp_time[CPUSTATES];
+   static u_int64_t cp_old[CPUSTATES];
+   static u_int64_t cp_diff[CPUSTATES];
+   static u_int64_t cpu_states[CPUSTATES];
+   static long tot;
+   size_t len = sizeof(cp_time);
+   int mib[2];
+
+   mib[0] = CTL_KERN;
+   mib[1] = KERN_CP_TIME;
+
+   /* Copy the last cp_time into cp_old */
+   memcpy(&cp_old, &cp_time, CPUSTATES*sizeof(long));
+   /* puts kern.cp_time array into cp_time */
+   if (sysctl(mib, 2, &cp_time, &len, NULL, 0) == -1 || !len)
+      res = 0.0;
+   else {
+	   /* Use percentages function lifted from top(1) to figure percentages */
+	   tot = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
+	   res = cpu_states[CP_SYS];
+   }
+   val.f = (float)res/10;
+   return val;
+}
+
+g_val_t 
+cpu_idle_func ( void )
+{
+   g_val_t val;
+   int res;
+
+   static u_int64_t cp_time[CPUSTATES];
+   static u_int64_t cp_old[CPUSTATES];
+   static u_int64_t cp_diff[CPUSTATES];
+   static u_int64_t cpu_states[CPUSTATES];
+   static long tot;
+   size_t len = sizeof(cp_time);
+   int mib[2];
+
+   mib[0] = CTL_KERN;
+   mib[1] = KERN_CP_TIME;
+
+   /* Copy the last cp_time into cp_old */
+   memcpy(&cp_old, &cp_time, CPUSTATES*sizeof(long));
+   /* puts kern.cp_time array into cp_time */
+   if (sysctl(mib, 2, &cp_time, &len, NULL, 0) == -1 || !len)
+      res = 0.0;
+   else {
+	   /* Use percentages function lifted from top(1) to figure percentages */
+	   tot = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
+	   res = cpu_states[CP_IDLE];
+   }
+   val.f = (float)res/10;
+   return val;
+}
+
+/*
+** FIXME
+*/
+g_val_t 
+cpu_wio_func ( void )
+{
+   g_val_t val;
+   val.f = 0.0;
+   return val;
+}
+
+g_val_t 
+cpu_aidle_func ( void )
+{
+   g_val_t val;
+   
+   val.f = 0.0;
+   return val;
+}
+
+g_val_t 
+cpu_intr_func ( void )
+{
+   g_val_t val;
+   int res;
+
+   static u_int64_t cp_time[CPUSTATES];
+   static u_int64_t cp_old[CPUSTATES];
+   static u_int64_t cp_diff[CPUSTATES];
+   static u_int64_t cpu_states[CPUSTATES];
+   static long tot;
+   size_t len = sizeof(cp_time);
+   int mib[2];
+
+   mib[0] = CTL_KERN;
+   mib[1] = KERN_CP_TIME;
+
+   /* Copy the last cp_time into cp_old */
+   memcpy(&cp_old, &cp_time, CPUSTATES*sizeof(long));
+   /* puts kern.cp_time array into cp_time */
+   if (sysctl(mib, 2, &cp_time, &len, NULL, 0) == -1 || !len)
+      res = 0.0;
+   else {
+	   /* Use percentages function lifted from top(1) to figure percentages */
+	   tot = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
+	   res = cpu_states[CP_INTR];
+   }
+
+   val.f = (float)res/10;
+   return val;
+}
+
+/*
+** FIXME
+*/
+g_val_t 
+cpu_sintr_func ( void )
+{
+   g_val_t val;
+   val.f = 0.0;
+   return val;
+}
+
+g_val_t
+load_one_func ( void )
+{
+   g_val_t val;
+   double load[3];
+
+   getloadavg(load, 3);
+   val.f = load[0];
+   return val;
+}
+
+g_val_t
+load_five_func ( void )
+{
+   g_val_t val;
+   double load[3];
+
+   getloadavg(load, 3);
+ 
+   val.f = load[1];
+   return val;
+}
+
+g_val_t
+load_fifteen_func ( void )
+{
+   g_val_t val;
+   double load[3];
+
+   getloadavg(load, 3);
+   val.f = load[2];
+   return val;
+}
+
+g_val_t
+proc_total_func ( void )
+{
+   g_val_t val;
+   int mib[3];
+   size_t len;
+
+   mib[0] = CTL_KERN;
+   mib[1] = KERN_PROC;
+   mib[2] = KERN_PROC_ALL;
+
+   sysctl(mib, 3, NULL, &len, NULL, 0);
+
+   val.uint32 = (len / sizeof (struct kinfo_proc)); 
+
+   return val;
+}
+
+
+/* 
+ * Don't know how to do this yet..
+ */
+g_val_t
+proc_run_func( void )
+{
+   struct kinfo_proc *kp;
+   int i;
+   int state;
+   int nentries;
+   int what = KERN_PROC_ALL;
+   g_val_t val;
+
+   val.uint32 = 0;
+
+   if (kd == NULL)
+      goto output;
+#ifdef KERN_PROC_NOTHREADS
+   what |= KERN_PROC_NOTHREADS
+#endif
+   if ((kp = kvm_getprocs(kd, what, 0, &nentries)) == 0 || nentries < 0)
+      goto output;
+
+   for (i = 0; i < nentries; kp++, i++) {
+#ifdef KINFO_PROC_SIZE
+      state = kp->ki_stat;
+#else
+      state = kp->kp_proc.p_stat;
+#endif
+      switch(state) {
+#if (__NetBSD_Version__ >= 200000000)
+	 case SACTIVE:
+#else
+	 case SRUN:
+	 case SONPROC:
+#endif
+	 case SIDL:
+		val.uint32++;
+		break;
+      }
+   }
+
+   if (val.uint32 > 0)
+	val.uint32--;
+
+output:
+   return val;
+}
+
+g_val_t
+mem_free_func ( void )
+{
+	g_val_t val;
+	size_t len; 
+	struct vmtotal vminfo;
+	int mib[2];
+
+	len = sizeof(vminfo);
+	mib[0] = CTL_VM;
+	mib[1] = VM_METER;
+	if (sysctl(mib, 2, &vminfo, &len, NULL, 0) < 0)
+		val.uint32 = 0;
+	else
+		val.uint32 = vminfo.t_free * (pagesize / 1024);
+	return val;
+}
+
+g_val_t
+mem_shared_func ( void )
+{
+	g_val_t val;
+	size_t len; 
+	struct vmtotal vminfo;
+	int mib[2];
+
+	len = sizeof(vminfo);
+	mib[0] = CTL_VM;
+	mib[1] = VM_METER;
+	if (sysctl(mib, 2, &vminfo, &len, NULL, 0) < 0)
+		val.uint32 = 0;
+	else
+		val.uint32 = (vminfo.t_vmshr + vminfo.t_rmshr) * (pagesize / 1024);
+	return val;
+}
+
+g_val_t
+mem_buffers_func ( void )
+{
+   g_val_t val;
+   size_t len;
+   int buffers;
+   int mib[2];
+
+   mib[0] = CTL_VM;
+   mib[1] = VM_NKMEMPAGES;
+
+   len = sizeof (buffers);
+   if((sysctl(mib, 2, &buffers, &len, NULL, 0) == -1) || !len)
+		buffers = 0; 
+   buffers /= 1024;
+
+   val.uint32 = buffers;
+   return val;
+}
+
+g_val_t
+mem_cached_func ( void )
+{
+	g_val_t val;
+	size_t len;
+	int cache;
+	struct uvmexp_sysctl uvmexp;
+	int mib[2];
+
+	mib[0] = CTL_VM;
+	mib[1] = VM_UVMEXP2;
+
+	len = sizeof(uvmexp);
+
+	if((sysctl(mib, 2, &uvmexp, &len, NULL, 0) < 0)
+			|| !len)
+		cache = 0;
+	else
+		cache = uvmexp.execpages + uvmexp.filepages;
+
+	val.uint32 = cache * (pagesize / 1024);
+	return val;
+}
+
+g_val_t
+swap_free_func ( void )
+{
+	g_val_t val;
+
+	int mib[2];
+	struct uvmexp_sysctl uvmexp;
+	size_t len;
+	int totswapf;
+
+	val.uint32 = 0;
+	totswapf = 0;
+
+	mib[0] = CTL_VM;
+	mib[1] = VM_UVMEXP2;
+
+	len = sizeof(uvmexp);
+
+	if((sysctl(mib, 2, &uvmexp, &len, NULL, 0) < 0)
+			|| !len)
+		totswapf = 0;
+	else
+		totswapf = uvmexp.swpages - uvmexp.swpginuse;
+
+	val.uint32 = totswapf * (pagesize / 1024);
+
+	return val;
+}
+
+g_val_t
+mtu_func ( void )
+{
+   /* We want to find the minimum MTU (Max packet size) over all UP interfaces.
+*/
+   g_val_t val;
+   val.uint32 = get_min_mtu();
+   /* A val of 0 means there are no UP interfaces. Shouldn't happen. */
+   return val;
+}
+
+/*
+ * Function to get cpu percentages.
+ * Might be changed ever so slightly, but is still mostly:
+ * AUTHOR:  Christos Zoulas <christos@ee.cornell.edu>
+ *          Steven Wallace  <swallace@freebsd.org>
+ *          Wolfram Schneider <wosch@FreeBSD.org>
+ *
+ * $FreeBSD: src/usr.bin/top/machine.c,v 1.29.2.2 2001/07/31 20:27:05 tmm Exp $
+ */
+
+static long percentages(int cnt, u_int64_t *out, register u_int64_t *new, 
+                        register u_int64_t *old, u_int64_t *diffs)  {
+
+    register int i;
+    register u_int64_t change;
+    register u_int64_t total_change;
+    register u_int64_t *dp;
+    long half_total;
+
+    /* initialization */
+    total_change = 0;
+    dp = diffs;
+
+    /* calculate changes for each state and the overall change */
+    for (i = 0; i < cnt; i++) {
+        if ((change = *new - *old) < 0) {
+            /* this only happens when the counter wraps */
+            change = (int)
+                ((unsigned long)*new-(unsigned long)*old);
+        }
+        total_change += (*dp++ = change);
+        *old++ = *new++;
+    }
+    /* avoid divide by zero potential */
+    if (total_change == 0) { total_change = 1; }
+
+    /* calculate percentages based on overall change, rounding up */
+    half_total = total_change / 2l;
+
+    /* Do not divide by 0. Causes Floating point exception */
+    if(total_change) {
+        for (i = 0; i < cnt; i++) {
+          *out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
+        }
+    }
+
+    /* return the total in case the caller wants to use it */
+    return(total_change);
+}
+
+g_val_t
+pkts_in_func ( void )
+{
+   double in_pkts;
+   g_val_t val;
+
+   get_netbw(NULL, NULL, &in_pkts, NULL);
+
+   val.f = (float)in_pkts;
+   return val;
+}
+
+g_val_t
+pkts_out_func ( void )
+{
+   double out_pkts;
+   g_val_t val;
+
+   get_netbw(NULL, NULL, NULL, &out_pkts);
+
+   val.f = (float)out_pkts;
+   return val;
+}
+
+g_val_t
+bytes_out_func ( void )
+{
+   double out_bytes;
+   g_val_t val;
+
+   get_netbw(NULL, &out_bytes, NULL, NULL);
+
+   val.f = (float)out_bytes;
+   return val;
+}
+
+g_val_t
+bytes_in_func ( void )
+{
+   double in_bytes;
+   g_val_t val;
+
+   get_netbw(&in_bytes, NULL, NULL, NULL);
+
+   val.f = (float)in_bytes;
+   return val;
+}
+
+/*
+ * Disk space reporting functions from Linux code.  find_disk_space()
+ * body derived from NetBSD df and mount code.
+ */
+
+g_val_t
+disk_free_func( void )
+{
+   double total_free=0.0;
+   double total_size=0.0;
+   g_val_t val;
+
+   find_disk_space(&total_size, &total_free);
+
+   val.d = total_free;
+   return val;
+}
+
+g_val_t
+disk_total_func( void )
+{
+   double total_free=0.0;
+   double total_size=0.0;
+   g_val_t val;
+
+   find_disk_space(&total_size, &total_free);
+
+   val.d = total_size;
+   return val;
+}
+
+g_val_t
+part_max_used_func( void )
+{
+   double total_free=0.0;
+   double total_size=0.0;
+   float most_full;
+   g_val_t val;
+
+   most_full = find_disk_space(&total_size, &total_free);
+
+   val.f = most_full;
+   return val;
+}
+
+
+/*
+ * Copyright (c) 1980, 1983, 1990, 1993, 1994, 1995
+ *	The Regents of the University of California.  All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by the University of
+ *      California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * NOTE: The copyright of UC Berkeley's Berkeley Software Distribution
+ * ("BSD") source has been updated.  The copyright addendum may be found
+ * at ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change.
+ */
+
+
+
+static float
+find_disk_space(double *total, double *tot_avail)
+{
+	struct statfs *mntbuf;
+	const char *fstype;
+	const char **vfslist;
+	char *netvfslist;
+	size_t i, mntsize;
+	size_t used, availblks;
+	const double reported_units = 1e9;
+	double toru;
+	float pct;
+	float most_full = 0.0;
+
+	*total = 0.0;
+	*tot_avail = 0.0;
+
+	fstype = "ufs";
+
+	netvfslist = makenetvfslist();
+	vfslist = makevfslist(netvfslist);
+	free(netvfslist);
+
+	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
+	mntsize = regetmntinfo(&mntbuf, mntsize, vfslist);
+	for (i = 0; i < mntsize; i++) {
+#ifdef HAVE_SYS_STATVFS_H
+		if ((mntbuf[i].f_flag & MNT_IGNORE) == 0) {
+#else
+		if ((mntbuf[i].f_flags & MNT_IGNORE) == 0) {
+#endif
+			used = mntbuf[i].f_blocks - mntbuf[i].f_bfree;
+			availblks = mntbuf[i].f_bavail + used;
+			pct = (availblks == 0 ? 100.0 :
+			    (double)used / (double)availblks * 100.0);
+			if (pct > most_full)
+				most_full = pct;
+
+#ifdef HAVE_SYS_STATVFS_H
+			toru = reported_units/mntbuf[i].f_frsize;
+#else
+			toru = reported_units/mntbuf[i].f_bsize;
+#endif
+			*total += mntbuf[i].f_blocks / toru;
+			*tot_avail += mntbuf[i].f_bavail / toru;
+		}
+	}
+
+	return most_full;
+}
+
+/*
+ * Make a pass over the file system info in ``mntbuf'' filtering out
+ * file system types not in vfslist and possibly re-stating to get
+ * current (not cached) info.  Returns the new count of valid statfs bufs.
+ */
+static size_t
+regetmntinfo(struct statfs **mntbufp, long mntsize, const char **vfslist)
+{
+	int i, j;
+	struct statfs *mntbuf;
+
+	if (vfslist == NULL)
+		return (getmntinfo(mntbufp, MNT_WAIT));
+
+	mntbuf = *mntbufp;
+	j = 0;
+	for (j = 0, i = 0; i < mntsize; i++) {
+		if (checkvfsname(mntbuf[i].f_fstypename, vfslist))
+			continue;
+		(void)statfs(mntbuf[i].f_mntonname, &mntbuf[j]);
+		j++;
+	}
+	return (j);
+}
+
+static int
+checkvfsname(const char *vfsname, const char **vfslist)
+{
+	if (vfslist == NULL)
+		return (0);
+	while (*vfslist != NULL) {
+		if (strcmp(vfsname, *vfslist) == 0)
+			return (skipvfs);
+		++vfslist;
+	}
+	return (!skipvfs);
+}
+
+static const char **
+makevfslist(char *fslist)
+{
+	const char **av;
+	int i;
+	char *nextcp;
+	if (fslist == NULL)
+		return (NULL);
+	if (fslist[0] == 'n' && fslist[1] == 'o') {
+		fslist += 2;
+		skipvfs = 1;
+	}
+	for (i = 0, nextcp = fslist; *nextcp; nextcp++)
+		if (*nextcp == ',')
+			i++;
+	if ((av = malloc((size_t)(i + 2) * sizeof(char *))) == NULL) {
+		warnx("malloc failed");
+		return (NULL);
+	}
+	nextcp = fslist;
+	i = 0;
+	i = 0;
+	av[i++] = nextcp;
+	while ((nextcp = strchr(nextcp, ',')) != NULL) {
+		*nextcp++ = '\0';
+		av[i++] = nextcp;
+	}
+	av[i++] = NULL;
+	return (av);
+}
+
+static char *
+makenetvfslist(void)
+{
+	char *str = NULL, *strptr, **listptr = NULL;
+	size_t slen;
+	int cnt, i;
+
+	int mib[3], maxvfsconf;
+	size_t miblen;
+	struct ovfsconf *ptr;
+
+	mib[0] = CTL_VFS; mib[1] = VFS_GENERIC; mib[2] = VFS_MAXTYPENUM;
+	miblen=sizeof(maxvfsconf);
+	if (sysctl(mib, (unsigned int)(sizeof(mib) / sizeof(mib[0])),
+				&maxvfsconf, &miblen, NULL, 0)) {
+		warnx("sysctl failed");
+		goto done;
+	}
+
+	if ((listptr = malloc(sizeof(char*) * maxvfsconf)) == NULL) {
+		warnx("malloc failed");
+		goto done;
+	}
+
+	cnt = 0;
+	/* XXX: use sysctl ? */
+	/* while ((ptr = getvfsent()) != NULL && cnt < maxvfsconf) {
+
+		listptr[cnt] = strdup(ptr->vfc_name);
+		if (listptr[cnt] == NULL) {
+			warnx("malloc failed");
+			goto done;
+		}
+		cnt++;
+	} */
+	if (cnt == 0)
+		goto done;
+	/*
+	 * Count up the string lengths, we need a extra byte to hold
+	 * the between entries ',' or the NUL at the end.
+	 */
+	for (i = 0; i < cnt; i++)
+		slen = strlen(listptr[i]) + 1;
+	/* Add 2 for initial "no". */
+	slen += 2;
+
+	if ((str = malloc(slen)) == NULL) {
+		warnx("malloc failed");
+		goto done;
+	}
+
+	str[0] = 'n';
+	str[1] = 'o';
+	for (i = 0, strptr = str + 2; i < cnt; i++, strptr++) {
+		strcpy(strptr, listptr[i]);
+		strptr += strlen(listptr[i]);
+		*strptr = ',';
+	}
+	*strptr = '\0';
+
+done:
+	if (listptr != NULL) {
+		for(i = 0; i < cnt && listptr[i] != NULL; i++)
+			free(listptr[i]);
+		free(listptr);
+	}
+	return (str);
+
+}
+
+static void
+get_netbw(double *in_bytes, double *out_bytes,
+    double *in_pkts, double *out_pkts)
+{
+#ifdef NETBW_DEBUG
+	char		name[IFNAMSIZ];
+#endif
+	struct		if_msghdr *ifm, *nextifm;
+	struct		sockaddr_dl *sdl;
+	char		*buf, *lim, *next;
+	size_t		needed;
+	int		mib[6];
+	int		i;
+	int		index;
+	static double	ibytes, obytes, ipkts, opkts;
+	struct timeval	this_time;
+	struct timeval	time_diff;
+	struct traffic	traffic;
+	static struct timeval last_time = {0,0};
+	static int	indexes = 0;
+	static int	*seen = NULL;
+	static struct traffic *lastcount = NULL;
+	static double	o_ibytes, o_obytes, o_ipkts, o_opkts;
+
+	ibytes = obytes = ipkts = opkts = 0.0;
+
+	mib[0] = CTL_NET;
+	mib[1] = PF_ROUTE;
+	mib[2] = 0;
+	mib[3] = 0;			/* address family */
+	mib[4] = NET_RT_IFLIST;
+	mib[5] = 0;		/* interface index */
+
+	gettimeofday(&this_time, NULL);
+	timersub(&this_time, &last_time, &time_diff);
+	if (timertod(&time_diff) < MIN_NET_POLL_INTERVAL) {
+		goto output;
+	}
+
+
+	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+		errx(1, "iflist-sysctl-estimate");
+	if ((buf = malloc(needed)) == NULL)
+		errx(1, "malloc");
+	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+		errx(1, "actual retrieval of interface table");
+	lim = buf + needed;
+
+	next = buf;
+	while (next < lim) {
+
+		ifm = (struct if_msghdr *)next;
+		
+		if (ifm->ifm_type == RTM_IFINFO) {
+			sdl = (struct sockaddr_dl *)(ifm + 1);
+		} else {
+			fprintf(stderr, "out of sync parsing NET_RT_IFLIST\n");
+			fprintf(stderr, "expected %d, got %d\n", RTM_IFINFO,
+				ifm->ifm_type);
+			fprintf(stderr, "msglen = %d\n", ifm->ifm_msglen);
+			fprintf(stderr, "buf:%p, next:%p, lim:%p\n", buf, next,
+				lim);
+			exit (1);
+		}
+
+		next += ifm->ifm_msglen;
+		while (next < lim) {
+			nextifm = (struct if_msghdr *)next;
+
+			if (nextifm->ifm_type != RTM_NEWADDR)
+				break;
+
+			next += nextifm->ifm_msglen;
+		}
+
+		if ((ifm->ifm_flags & IFF_LOOPBACK) || 
+		    !(ifm->ifm_flags & IFF_UP))
+			continue;
+
+		index = ifm->ifm_index;
+
+		/* If we don't have a previous value yet, make a slot. */
+		if (index >= indexes) {
+			seen = realloc(seen, sizeof(*seen)*(index+1));
+			lastcount = realloc(lastcount,
+			    sizeof(*lastcount)*(index+1));
+
+			/* Initalize the new slots */
+			for (i = indexes; i <= index; i++) {
+				seen[i] = 0;
+			}
+			indexes = index+1;
+		}
+
+		/*
+		 * If this is the first time we've seen this interface,
+		 * set the last values to the current ones.  That causes
+		 * us to see no bandwidth on the interface the first
+		 * time, but that's OK.
+		 */
+		if (!seen[index]) {
+			seen[index] = 1;
+			lastcount[index].in_bytes = ifm->ifm_data.ifi_ibytes;
+			lastcount[index].out_bytes = ifm->ifm_data.ifi_obytes;
+			lastcount[index].in_pkts = ifm->ifm_data.ifi_ipackets;
+			lastcount[index].out_pkts = ifm->ifm_data.ifi_opackets;
+		}
+
+		traffic.in_bytes = counterdiff(lastcount[index].in_bytes,
+		    ifm->ifm_data.ifi_ibytes, ULONG_MAX, 0);
+		traffic.out_bytes = counterdiff(lastcount[index].out_bytes,
+		    ifm->ifm_data.ifi_obytes, ULONG_MAX, 0);
+		traffic.in_pkts = counterdiff(lastcount[index].in_pkts,
+		    ifm->ifm_data.ifi_ipackets, ULONG_MAX, 0);
+		traffic.out_pkts = counterdiff(lastcount[index].out_pkts,
+		    ifm->ifm_data.ifi_opackets, ULONG_MAX, 0);
+
+		lastcount[index].in_bytes = ifm->ifm_data.ifi_ibytes;
+		lastcount[index].out_bytes = ifm->ifm_data.ifi_obytes;
+		lastcount[index].in_pkts = ifm->ifm_data.ifi_ipackets;
+		lastcount[index].out_pkts = ifm->ifm_data.ifi_opackets;
+
+#ifdef NETBW_DEBUG
+		if_indextoname(index, name);
+		printf("%s: \n", name);
+		printf("\topackets=%llu ipackets=%llu\n",
+		    traffic.out_pkts, traffic.in_pkts);
+		printf("\tobytes=%llu ibytes=%llu\n",
+		    traffic.out_bytes, traffic.in_bytes);
+#endif
+
+		if (timerisset(&last_time)) {
+			ibytes += (double)traffic.in_bytes / timertod(&time_diff);
+			obytes += (double)traffic.out_bytes / timertod(&time_diff);
+			ipkts += (double)traffic.in_pkts / timertod(&time_diff);
+			opkts += (double)traffic.out_pkts / timertod(&time_diff);
+		}
+	}
+	free(buf);
+
+	/* Save the values from this time */
+	last_time = this_time;
+	o_ibytes = ibytes;
+	o_obytes = obytes;
+	o_ipkts = ipkts;
+	o_opkts = opkts;
+
+output:
+	if (in_bytes != NULL)
+		*in_bytes = o_ibytes;
+	if (out_bytes != NULL)
+		*out_bytes = o_obytes;
+	if (in_pkts != NULL)
+		*in_pkts = o_ipkts;
+	if (out_pkts != NULL)
+		*out_pkts = o_opkts;
+}
+
+static uint64_t
+counterdiff(uint64_t oldval, uint64_t newval, uint64_t maxval, uint64_t maxdiff)
+{
+	uint64_t diff;
+
+	if (maxdiff == 0)
+		maxdiff = maxval;
+
+	/* Paranoia */
+	if (oldval > maxval || newval > maxval)
+		return 0;
+
+	/*
+	 * Tackle the easy case.  Don't worry about maxdiff here because
+	 * we're SOL if it happens (i.e. assuming a reset just makes
+	 * matters worse).
+	 */
+	if (oldval <= newval)
+		return (newval - oldval);
+
+	/*
+         * Now the tricky part.  If we assume counters never get reset,
+         * this is easy.  Unfortunaly, they do get reset on some
+         * systems, so we need to try and deal with that.  Our huristic
+         * is that if out difference is greater then maxdiff and newval
+         * is less or equal to maxdiff, then we've probably been reset
+         * rather then actually wrapping.  Obviously, you need to be
+         * careful to poll often enough that you won't exceed maxdiff or
+         * you will get undersized numbers when you do wrap.
+	 */
+	diff = maxval - oldval + newval;
+	if (diff > maxdiff && newval <= maxdiff)
+		return newval;
+
+	return diff;
+}
