#include <stdio.h>
#include <stdlib.h>
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/sysinfo.h>
#include <sys/stat.h>
#include <sys/statvfs.h>

/* From old ganglia 2.5.x... */
#include "file.h"
#include "libmetrics.h"
/* End old ganglia 2.5.x headers */

#define OSNAME "Linux"
#define OSNAME_LEN strlen(OSNAME)

/* /proc/net/dev hash table stuff */
typedef struct net_dev_stats net_dev_stats;
struct net_dev_stats {
  char *name;
  double pkts_in;
  double pkts_out;
  double bytes_in;
  double bytes_out;
  net_dev_stats *next;
};
#define NHASH 101
#define MULTIPLIER 31
static net_dev_stats *netstats[NHASH];

/* Never changes */
#ifndef BUFFSIZE
#define BUFFSIZE 8192
#endif
char proc_cpuinfo[BUFFSIZE];
char proc_sys_kernel_osrelease[BUFFSIZE];
char sys_devices_system_cpu[32];
int cpufreq;

typedef struct {
  uint32_t last_read;
  uint32_t thresh;
  char *name;
  char buffer[BUFFSIZE];
} timely_file;

timely_file proc_stat    = { 0, 1, "/proc/stat" };
timely_file proc_loadavg = { 0, 5, "/proc/loadavg" };
timely_file proc_meminfo = { 0, 5, "/proc/meminfo" };
timely_file proc_net_dev = { 0, 1, "/proc/net/dev" };

char *update_file(timely_file *tf)
{
  int now,rval;
  now = time(0);
  if(now - tf->last_read > tf->thresh) {
    rval = slurpfile(tf->name, tf->buffer, BUFFSIZE);
    if(rval == SYNAPSE_FAILURE) {
      err_msg("update_file() got an error from slurpfile() reading %s",
	      tf->name);
      return (char *)SYNAPSE_FAILURE;
    }
    else tf->last_read = now;
  }
  return tf->buffer;
}

/*
** A helper function to determine the number of cpustates in /proc/stat (MKN)
*/
#define NUM_CPUSTATES_24X 4
#define NUM_CPUSTATES_26X 7
static unsigned int num_cpustates;

unsigned int
num_cpustates_func ( void )
{
   char *p;
   unsigned int i=0;

   proc_stat.last_read=0;
   p = update_file(&proc_stat);
   proc_stat.last_read=0;

/*
** Skip initial "cpu" token
*/
   p = skip_token(p);
   p = skip_whitespace(p);
/*
** Loop over file until next "cpu" token is found.
** i=4 : Linux 2.4.x
** i=7 : Linux 2.6.x
*/
   while (strncmp(p,"cpu",3)) {
     p = skip_token(p);
     p = skip_whitespace(p);
     i++;
     }

   return i;
}

/*
** Helper functions to hash /proc/net/dev stats (Kernighan & Pike)
*/
static unsigned int hashval(const char *s)
{
  unsigned int hval;
  unsigned char *p;

  hval = 0;
  for (p = (unsigned char *)s; *p != '\0'; p++)
    hval = MULTIPLIER * hval + *p;
  return hval % NHASH;
}

static net_dev_stats *hash_lookup(char *name, const int enter, 
				  const double pkts_in, 
				  const double pkts_out, 
				  const double bytes_in, 
				  const double bytes_out)
{
  int hval;
  net_dev_stats *stats;

  hval = hashval(name);
  for (stats = netstats[hval]; stats != NULL; stats = stats->next)
  {
    if (strcmp(name, stats->name) == 0)
      return stats;
  }

  if (enter)
  {
    stats = (net_dev_stats *)malloc(sizeof(net_dev_stats));
    if ( stats == NULL )
    {   
      err_msg("unable to allocate memory for /proc/net/dev/stats in hash_lookup()");
      return NULL;
    }
    stats->name = name;
    stats->pkts_in   = pkts_in;
    stats->pkts_out  = pkts_out;
    stats->bytes_in  = bytes_in;
    stats->bytes_out = bytes_out;
    stats->next = netstats[hval];
    netstats[hval] = stats;
  }
  return stats;
}

/*
 * 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 rval;
   char * dummy;

   num_cpustates = num_cpustates_func();

   cpufreq = 1;
   rval.int32 = slurpfile("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", sys_devices_system_cpu, BUFFSIZE);
   if ( rval.int32 == SYNAPSE_FAILURE )
      cpufreq = 0;

   rval.int32 = slurpfile("/proc/cpuinfo", proc_cpuinfo, BUFFSIZE);
   if ( rval.int32 == SYNAPSE_FAILURE )
      {
         err_msg("metric_init() got an error from slurpfile() /proc/cpuinfo");
         return rval;
      }  

   rval.int32 = slurpfile( "/proc/sys/kernel/osrelease", 
                       proc_sys_kernel_osrelease, BUFFSIZE);
   if ( rval.int32 == SYNAPSE_FAILURE )
      {
         err_msg("metric_init() got an error from slurpfile()");
         return rval;
      }  
   
   /* Get rid of pesky \n in osrelease */
   proc_sys_kernel_osrelease[rval.int32-1] = '\0';

   dummy = update_file(&proc_net_dev);
   if ( dummy == (char *)SYNAPSE_FAILURE )
      {
         err_msg("metric_init() got an error from update_file()");
	 rval.int32 = SYNAPSE_FAILURE;
         return rval;
      } 

   rval.int32 = SYNAPSE_SUCCESS;
   return rval;
}

g_val_t
pkts_in_func ( void )
{
   char *p;
   register int i;
   static g_val_t val;
   static int stamp;
   double bytes_in=0, bytes_out=0, pkts_in=0, pkts_out=0, t = 0;

   p = update_file(&proc_net_dev);
   if (proc_net_dev.last_read != stamp)
      {
	 /*  skip past the two-line header ... */
	 p = index (p, '\n')+1;
	 p = index (p, '\n')+1;

	 while (*p != 0x00 )
	    {
	       /*  skip past the interface tag portion of this line */
	       /*  but save the name of the interface (hash key) */
	       char *devname, *src;
	       size_t n = 0;

	       while (p != 0x00 && isblank(*p))
		  p++;
	       src = p;
	       while (p != 0x00 && *p != ':')
		  {
		     n++;
		     p++;
		  }
	       devname = strndup(src, n);

	       p = index(p, ':')+1;
	       if ( ((*(p-2) != 'o') && (*(p-3) != 'l')) && 
		    ((*(p-3) != 'd') && (*(p-4) != 'n') && 
		     (*(p-5) != 'o') && (*(p-6) != 'b')) )
		  { 
		     double temp = 0.;
		     /* Check for data from the last read for this */
		     /* interface.  If nothing exists, add to the table. */
		     net_dev_stats *ns = hash_lookup(devname, 1, 
						     0., 0., 0., 0.);
		     if ( ns == NULL )
		        {
			   val.f = 0.;
			   debug_msg(" **********  BYTES_OUT RETURN:  %f", 
				     val.f);
			   return val;
			}

		     t = strtod( p, &p ) - ns->bytes_in;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     bytes_in += t;
		     temp = strtod( p, &p );
		     t = temp - ns->pkts_in;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     pkts_in += t;
		     for (i = 0; i < 6; i++) strtol(p, &p, 10);
		     /* Fixed 2003 by Dr Michael Wirtz <m.wirtz@tinnit.de>
			and Phil Radden <P.Radden@rl.ac.uk> */
		     t = strtod( p, &p ) - ns->bytes_out;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     bytes_out += t;
		     t = strtod( p, &p ) - ns->pkts_out;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     pkts_out += t;

		     /* Update the hash for this measurement */
		     ns->pkts_in = temp;
		  }
	       p = index (p, '\n') + 1;    // skips a line
	    }

	 val.f = 0.;
	 if ( pkts_in >= 1. )
	    {
	       t = proc_net_dev.last_read - stamp;
	       val.f = pkts_in / t;
	    }

	 stamp = proc_net_dev.last_read;
      }

   debug_msg(" **********  PKTS_IN RETURN:  %f", val.f);
   return val;
}

g_val_t
pkts_out_func ( void )
{
   char *p;
   register int i;
   static g_val_t val;
   static int stamp;
   double bytes_in=0, bytes_out=0, pkts_in=0, pkts_out=0, t = 0;

   p = update_file(&proc_net_dev);
   if (proc_net_dev.last_read != stamp)
      {
	 /*  skip past the two-line header ... */
	 p = index (p, '\n')+1;
	 p = index (p, '\n')+1;

	 while (*p != 0x00 )
	    {
	       /*  skip past the interface tag portion of this line */
	       /*  but save the name of the interface (hash key) */
	       char *devname, *src;
	       size_t n = 0;

	       while (p != 0x00 && isblank(*p))
		  p++;
	       src = p;
	       while (p != 0x00 && *p != ':')
		  {
		     n++;
		     p++;
		  }
	       devname = strndup(src, n);

	       p = index(p, ':')+1;
	       if ( ((*(p-2) != 'o') && (*(p-3) != 'l')) && 
		    ((*(p-3) != 'd') && (*(p-4) != 'n') && 
		     (*(p-5) != 'o') && (*(p-6) != 'b')) )
		  { 
		     double temp = 0.;
		     /* Check for data from the last read for this */
		     /* interface.  If nothing exists, add to the table. */
		     net_dev_stats *ns = hash_lookup(devname, 1, 
						     0., 0., 0., 0.);
		     if ( ns == NULL )
		        {
			   val.f = 0.;
			   debug_msg(" **********  BYTES_OUT RETURN:  %f", 
				     val.f);
			   return val;
			}

		     t = strtod( p, &p ) - ns->bytes_in;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     bytes_in += t;
		     t = strtod( p, &p ) - ns->pkts_in;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     pkts_in += t;
		     for (i = 0; i < 6; i++) strtol(p, &p, 10);
		     /* Fixed 2003 by Dr Michael Wirtz <m.wirtz@tinnit.de>
			and Phil Radden <P.Radden@rl.ac.uk> */
		     t = strtod( p, &p ) - ns->bytes_out;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     bytes_out += t;
		     temp = strtod( p, &p );
		     t = temp - ns->pkts_out;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     pkts_out += t;

		     /* Update the hash for this measurement */
		     ns->pkts_out = temp;
		  }
	       p = index (p, '\n') + 1;    // skips a line
	    }

	 val.f = 0.;
	 if ( pkts_out >= 1. )
	    {
	       t = proc_net_dev.last_read - stamp;
	       val.f = pkts_out / t;
	    }

	 stamp = proc_net_dev.last_read;
      }

   debug_msg(" **********  PKTS_OUT RETURN:  %f", val.f);
   return val;
}

g_val_t
bytes_out_func ( void )
{
   char *p;
   register int i;
   static g_val_t val;
   static int stamp;
   double bytes_in=0, bytes_out=0, pkts_in=0, pkts_out=0, t = 0;

   p = update_file(&proc_net_dev);
   if (proc_net_dev.last_read != stamp)
      {
	 /*  skip past the two-line header ... */
	 p = index (p, '\n')+1;
	 p = index (p, '\n')+1;

	 while (*p != 0x00 )
	    {
	       /*  skip past the interface tag portion of this line */
	       /*  but save the name of the interface (hash key) */
	       char *devname, *src;
	       size_t n = 0;

	       while (p != 0x00 && isblank(*p))
		  p++;
	       src = p;
	       while (p != 0x00 && *p != ':')
		  {
		     n++;
		     p++;
		  }
	       devname = strndup(src, n);

	       p = index(p, ':')+1;
	       if ( ((*(p-2) != 'o') && (*(p-3) != 'l')) && 
		    ((*(p-3) != 'd') && (*(p-4) != 'n') && 
		     (*(p-5) != 'o') && (*(p-6) != 'b')) )
		  { 
		     double temp = 0.;
		     /* Check for data from the last read for this */
		     /* interface.  If nothing exists, add to the table. */
		     net_dev_stats *ns = hash_lookup(devname, 1, 
						     0., 0., 0., 0.);
		     if ( ns == NULL )
		        {
			   val.f = 0.;
			   debug_msg(" **********  BYTES_OUT RETURN:  %f", 
				     val.f);
			   return val;
			}

		     t = strtod( p, &p ) - ns->bytes_in;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     bytes_in += t;
		     t = strtod( p, &p ) - ns->pkts_in;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     pkts_in += t;
		     for (i = 0; i < 6; i++) strtol(p, &p, 10);
		     /* Fixed 2003 by Dr Michael Wirtz <m.wirtz@tinnit.de>
			and Phil Radden <P.Radden@rl.ac.uk> */
		     temp = strtod( p, &p );
		     t = temp - ns->bytes_out;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     bytes_out += t;
		     t = strtod( p, &p ) - ns->pkts_out;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     pkts_out += t;

		     /* Update the hash for this measurement */
		     ns->bytes_out = temp;
		  }
	       p = index (p, '\n') + 1;    // skips a line
	    }

	 val.f = 0.;
	 if ( bytes_out >= 1. )
	    {
	       t = proc_net_dev.last_read - stamp;
	       val.f = bytes_out / t;
	    }

	 stamp = proc_net_dev.last_read;
      }

   debug_msg(" **********  BYTES_OUT RETURN:  %f", val.f);
   return val;
}

g_val_t
bytes_in_func ( void )
{
   char *p;
   register int i;
   static g_val_t val;
   static int stamp;
   double bytes_in=0, bytes_out=0, pkts_in=0, pkts_out=0, t = 0;

   p = update_file(&proc_net_dev);
   if (proc_net_dev.last_read != stamp)
      {
	 /*  skip past the two-line header ... */
	 p = index (p, '\n')+1;
	 p = index (p, '\n')+1;

	 while (*p != 0x00 )
	    {
	       /*  skip past the interface tag portion of this line */
	       /*  but save the name of the interface (hash key) */
	       char *devname, *src;
	       size_t n = 0;

	       while (p != 0x00 && isblank(*p))
		  p++;
	       src = p;
	       while (p != 0x00 && *p != ':')
		  {
		     n++;
		     p++;
		  }
	       devname = strndup(src, n);

	       p = index(p, ':')+1;
	       if ( ((*(p-2) != 'o') && (*(p-3) != 'l')) && 
		    ((*(p-3) != 'd') && (*(p-4) != 'n') && 
		     (*(p-5) != 'o') && (*(p-6) != 'b')) )
		  { 
		     double temp = 0.;
		     /* Check for data from the last read for this */
		     /* interface.  If nothing exists, add to the table. */
		     net_dev_stats *ns = hash_lookup(devname, 1, 
						     0., 0., 0., 0.);
		     if ( ns == NULL )
		        {
			   val.f = 0.;
			   debug_msg(" **********  BYTES_OUT RETURN:  %f", 
				     val.f);
			   return val;
			}

		     temp = strtod( p, &p );
		     t = temp - ns->bytes_in;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     bytes_in += t;
		     t = strtod( p, &p ) - ns->pkts_in;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     pkts_in += t;
		     for (i = 0; i < 6; i++) strtol(p, &p, 10);
		     /* Fixed 2003 by Dr Michael Wirtz <m.wirtz@tinnit.de>
			and Phil Radden <P.Radden@rl.ac.uk> */
		     t = strtod( p, &p ) - ns->bytes_out;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     bytes_out += t;
		     t = strtod( p, &p ) - ns->pkts_out;
		     if ( t < 0. ) { t += ULONG_MAX; }
		     pkts_out += t;

		     /* Update the hash for this measurement */
		     ns->bytes_in = temp;
		  }
	       p = index (p, '\n') + 1;    // skips a line
	    }

	 val.f = 0.;
	 if ( bytes_in >= 1. )
	    {
	       t = proc_net_dev.last_read - stamp;
	       val.f = bytes_in / t;
	    }

	 stamp = proc_net_dev.last_read;
      }

   debug_msg(" **********  BYTES_IN RETURN:  %f", val.f);
   return val;
}

g_val_t
cpu_num_func ( void )
{
   static int cpu_num = 0;
   g_val_t val;

   /* Only need to do this once */
   if (! cpu_num) {
      /* We'll use _SC_NPROCESSORS_ONLN to get operating cpus */
      cpu_num = get_nprocs();
   }
   val.uint16 = cpu_num;

   return val;
}

g_val_t
cpu_speed_func ( void )
{
   char *p;
   static g_val_t val = {0};

   /* we'll use scaling_max_freq before we fallback on proc_cpuinfo */
   if(cpufreq && ! val.uint32)
      {
         p = sys_devices_system_cpu;
         val.uint32 = (uint32_t)(strtol( p, (char **)NULL , 10 ) / 1000 );
      }

/* i386, ia64, x86_64 and hppa all report MHz in the same format */
#if defined (__i386__) || defined(__ia64__) || defined(__hppa__) || defined(__x86_64__)
   if (! val.uint32 )
      {
         p = proc_cpuinfo;  
         p = strstr( p, "cpu MHz" );
         if(p) {
           p = strchr( p, ':' );
           p++;
           p = skip_whitespace(p);
           val.uint32 = (uint32_t)strtol( p, (char **)NULL , 10 );
         } else {
           val.uint32 = 0;
         }
      }
#endif
#if defined (__alpha__)
   if (! val.uint32 ) {
         int num;
         p = proc_cpuinfo;
         p = strstr( p, "cycle frequency [Hz]" );
         if(p) {
           p = strchr( p, ':' );
           p++;
           p = skip_whitespace(p);
           sscanf(p, "%d", &num);
           num = num / 1000000;  /* Convert to Mhz */
           val.uint32 = (uint32_t)num;
         } else {
           val.uint32 = 0;
         }
      }
#endif
#if defined (__powerpc__)
   if (! val.uint32 )
      {
         p = proc_cpuinfo;
         p = strstr( p, "clock" );
         if(p) { 
           p = strchr( p, ':' );
           p++;
           p = skip_whitespace(p);
           val.uint32 = (uint32_t)strtol( p, (char **)NULL , 10 );
        } else {
           val.uint32 = 0;
        }
      }
#endif
   return val;
}

g_val_t
mem_total_func ( void )
{
   char *p;
   g_val_t val;

   p = strstr( update_file(&proc_meminfo), "MemTotal:");
   if(p) {
     p = skip_token(p);
     val.uint32 = strtol( p, (char **)NULL, 10 );
   } else {
     val.uint32 = 0;
   }

   return val;
}

g_val_t
swap_total_func ( void )
{
   char *p;
   g_val_t val;
 
   p = strstr( update_file(&proc_meminfo), "SwapTotal:" );
   if(p) {
     p = skip_token(p);
     val.uint32 = strtol( p, (char **)NULL, 10 );  
   } else {
     val.uint32 = 0;
   }

   return val;
}

g_val_t
boottime_func ( void )
{
   char *p;
   g_val_t val;

   p = update_file(&proc_stat); 

   p = strstr ( p, "btime" );
   if(p) { 
     p = skip_token ( p );
     val.uint32 = strtod ( p, (char **)NULL );
   } else {
     val.uint32 = 0;
   }

   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;
 
#ifdef __i386__
   snprintf(val.str, MAX_G_STRING_SIZE, "x86");
#endif
#ifdef __x86_64__
   snprintf(val.str, MAX_G_STRING_SIZE, "x86_64");
#endif
#ifdef __ia64__
   snprintf(val.str, MAX_G_STRING_SIZE, "ia64");
#endif
#ifdef __sparc__
   snprintf(val.str, MAX_G_STRING_SIZE, "sparc");
#endif
#ifdef __alpha__
   snprintf(val.str, MAX_G_STRING_SIZE, "alpha");
#endif
#ifdef __powerpc__
   snprintf(val.str, MAX_G_STRING_SIZE, "powerpc");
#endif
#ifdef __m68k__
   snprintf(val.str, MAX_G_STRING_SIZE, "m68k");
#endif
#ifdef __mips__
   snprintf(val.str, MAX_G_STRING_SIZE, "mips");
#endif
#ifdef __arm__
   snprintf(val.str, MAX_G_STRING_SIZE, "arm");
#endif
#ifdef __hppa__
   snprintf(val.str, MAX_G_STRING_SIZE, "hppa");
#endif
#ifdef __s390__
   snprintf(val.str, MAX_G_STRING_SIZE, "s390");
#endif

   return val;
}

g_val_t
os_name_func ( void )
{
   g_val_t val;

   snprintf(val.str, MAX_G_STRING_SIZE, "Linux");
   return val;
}

g_val_t
os_release_func ( void )
{
   g_val_t val;

   snprintf(val.str, MAX_G_STRING_SIZE, "%s", proc_sys_kernel_osrelease);
   return val;
}


/*
 * A helper function to return the total number of cpu jiffies
 */
unsigned long
total_jiffies_func ( void )
{
   char *p;
   unsigned long user_jiffies, nice_jiffies, system_jiffies, idle_jiffies,
                 wio_jiffies, irq_jiffies, sirq_jiffies;

   p = update_file(&proc_stat);
   p = skip_token(p);
   p = skip_whitespace(p);
   user_jiffies = strtod( p, &p );
   p = skip_whitespace(p);
   nice_jiffies = strtod( p, &p ); 
   p = skip_whitespace(p);
   system_jiffies = strtod( p , &p ); 
   p = skip_whitespace(p);
   idle_jiffies = strtod( p , &p );

   if(num_cpustates == NUM_CPUSTATES_24X)
     return user_jiffies + nice_jiffies + system_jiffies + idle_jiffies;

   p = skip_whitespace(p);
   wio_jiffies = strtod( p , &p );
   p = skip_whitespace(p);
   irq_jiffies = strtod( p , &p );
   p = skip_whitespace(p);
   sirq_jiffies = strtod( p , &p );
  
   return user_jiffies + nice_jiffies + system_jiffies + idle_jiffies +
          wio_jiffies + irq_jiffies + sirq_jiffies; 
}   


g_val_t
cpu_user_func ( void )
{
   char *p;
   static g_val_t val;
   static int stamp;
   static double last_user_jiffies,  user_jiffies, 
                 last_total_jiffies, total_jiffies, diff;
   
   p = update_file(&proc_stat);
   if(proc_stat.last_read != stamp) {
     stamp = proc_stat.last_read;
     
     p = skip_token(p);
     user_jiffies  = strtod( p , (char **)NULL );
     total_jiffies = total_jiffies_func();
     
     diff = user_jiffies - last_user_jiffies; 
     
     if ( diff )
       val.f = (diff/(total_jiffies - last_total_jiffies))*100;
     else
       val.f = 0.0;
     
     last_user_jiffies  = user_jiffies;
     last_total_jiffies = total_jiffies;
     
   }
   return val;
}

g_val_t
cpu_nice_func ( void )
{
   char *p;
   static g_val_t val;
   static int stamp;
   static double last_nice_jiffies,  nice_jiffies,
                 last_total_jiffies, total_jiffies, diff;
 
   p = update_file(&proc_stat);
   if(proc_stat.last_read != stamp) {
     stamp = proc_stat.last_read;
 
     p = skip_token(p);
     p = skip_token(p);
     nice_jiffies  = strtod( p , (char **)NULL );
     total_jiffies = total_jiffies_func();

     diff = (nice_jiffies  - last_nice_jiffies);
 
     if ( diff )
       val.f = (diff/(total_jiffies - last_total_jiffies))*100;
     else
       val.f = 0.0;
 
     last_nice_jiffies  = nice_jiffies;
     last_total_jiffies = total_jiffies;

   }
   return val;
}

g_val_t 
cpu_system_func ( void )
{
   char *p;
   static g_val_t val;
   static int stamp;
   static double last_system_jiffies,  system_jiffies,
                 last_total_jiffies, total_jiffies, diff;
 
   p = update_file(&proc_stat);
   if(proc_stat.last_read != stamp) {
     stamp = proc_stat.last_read;
     
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     system_jiffies = strtod( p , (char **)NULL );
     if (num_cpustates > NUM_CPUSTATES_24X) {
       p = skip_token(p);
       p = skip_token(p);
       p = skip_token(p);
       system_jiffies += strtod( p , (char **)NULL ); /* "intr" counted in system */
       p = skip_token(p);
       system_jiffies += strtod( p , (char **)NULL ); /* "sintr" counted in system */
       }
     total_jiffies  = total_jiffies_func();

     diff = system_jiffies  - last_system_jiffies;
 
     if ( diff )
       val.f = (diff/(total_jiffies - last_total_jiffies))*100;
     else
       val.f = 0.0;
 
     last_system_jiffies  = system_jiffies;
     last_total_jiffies = total_jiffies;   

   }
   return val;
}

g_val_t 
cpu_idle_func ( void )
{
   char *p;
   static g_val_t val;
   static int stamp;
   static double last_idle_jiffies,  idle_jiffies,
                 last_total_jiffies, total_jiffies, diff;
 
   p = update_file(&proc_stat);
   if(proc_stat.last_read != stamp) {
     stamp = proc_stat.last_read;
     
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     idle_jiffies  = strtod( p , (char **)NULL );
     total_jiffies = total_jiffies_func();
     
     diff = idle_jiffies - last_idle_jiffies;
     
     if ( diff ) 
       val.f = (diff/(total_jiffies - last_total_jiffies))*100;
     else
       val.f = 0.0;
     
     last_idle_jiffies  = idle_jiffies;
     last_total_jiffies = total_jiffies;
     
   }
   
   return val;
}

g_val_t 
cpu_aidle_func ( void )
{
   char *p;
   g_val_t val;
   double idle_jiffies, total_jiffies;
   
   p = update_file(&proc_stat);

   p = skip_token(p);
   p = skip_token(p);
   p = skip_token(p);
   p = skip_token(p);
   idle_jiffies  = strtod( p , (char **)NULL );
   total_jiffies = total_jiffies_func();
   
   val.f = (idle_jiffies/total_jiffies)*100;
   return val;
}

g_val_t 
cpu_wio_func ( void )
{
   char *p;
   static g_val_t val;
   static int stamp;
   static double last_wio_jiffies,  wio_jiffies,
                 last_total_jiffies, total_jiffies, diff;
 
   if (num_cpustates == NUM_CPUSTATES_24X) {
     val.f = 0.;
     return val;
     }

   p = update_file(&proc_stat);
   if(proc_stat.last_read != stamp) {
     stamp = proc_stat.last_read;
     
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     wio_jiffies  = strtod( p , (char **)NULL );
     total_jiffies = total_jiffies_func();
     
     diff = wio_jiffies - last_wio_jiffies;
     
     if ( diff ) 
       val.f = (diff/(total_jiffies - last_total_jiffies))*100;
     else
       val.f = 0.0;
     
     last_wio_jiffies  = wio_jiffies;
     last_total_jiffies = total_jiffies;
     
   }
   
   return val;
}

g_val_t 
cpu_intr_func ( void )
{
   char *p;
   static g_val_t val;
   static int stamp;
   static double last_intr_jiffies,  intr_jiffies,
                 last_total_jiffies, total_jiffies, diff;
 
   if (num_cpustates == NUM_CPUSTATES_24X) {
     val.f = 0.;
     return val;
     }

   p = update_file(&proc_stat);
   if(proc_stat.last_read != stamp) {
     stamp = proc_stat.last_read;
     
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     intr_jiffies  = strtod( p , (char **)NULL );
     total_jiffies = total_jiffies_func();
     
     diff = intr_jiffies - last_intr_jiffies;
     
     if ( diff ) 
       val.f = (diff/(total_jiffies - last_total_jiffies))*100;
     else
       val.f = 0.0;
     
     last_intr_jiffies  = intr_jiffies;
     last_total_jiffies = total_jiffies;
     
   }
   
   return val;
}

g_val_t 
cpu_sintr_func ( void )
{
   char *p;
   static g_val_t val;
   static int stamp;
   static double last_sintr_jiffies,  sintr_jiffies,
                 last_total_jiffies, total_jiffies, diff;
 
   if (num_cpustates == NUM_CPUSTATES_24X) {
     val.f = 0.;
     return val;
     }

   p = update_file(&proc_stat);
   if(proc_stat.last_read != stamp) {
     stamp = proc_stat.last_read;
     
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     p = skip_token(p);
     sintr_jiffies  = strtod( p , (char **)NULL );
     total_jiffies = total_jiffies_func();
     
     diff = sintr_jiffies - last_sintr_jiffies;
     
     if ( diff ) 
       val.f = (diff/(total_jiffies - last_total_jiffies))*100;
     else
       val.f = 0.0;
     
     last_sintr_jiffies  = sintr_jiffies;
     last_total_jiffies = total_jiffies;
     
   }
   
   return val;
}

g_val_t
load_one_func ( void )
{
   g_val_t val;

   val.f = strtod( update_file(&proc_loadavg), (char **)NULL);

   return val;
}

g_val_t
load_five_func ( void )
{
   char *p;
   g_val_t val;

   p = update_file(&proc_loadavg);
   p = skip_token(p);
   val.f = strtod( p, (char **)NULL);
 
   return val;
}

g_val_t
load_fifteen_func ( void )
{
   char *p;
   g_val_t val;

   p = update_file(&proc_loadavg);
 
   p = skip_token(p);
   p = skip_token(p);
   val.f = strtod( p, (char **)NULL);

   return val;
}

g_val_t
proc_run_func( void )
{
   char *p;
   g_val_t val;

   p = update_file(&proc_loadavg);
   p = skip_token(p);
   p = skip_token(p);
   p = skip_token(p);
   val.uint32 = strtol( p, (char **)NULL, 10 );

   val.uint32--;
   /* This shouldn't happen.. but it might */
   if (val.uint32 <0)
      val.uint32 = 0;

   return val;
}

g_val_t
proc_total_func ( void )
{
   char *p;
   g_val_t val;

   p = update_file(&proc_loadavg);
   p = skip_token(p);
   p = skip_token(p);
   p = skip_token(p); 
   p = skip_whitespace(p);
   while ( isdigit(*p) )
      p++;
   p++;  /* skip the slash-/ */ 
   val.uint32 = strtol( p, (char **)NULL, 10 ); 

   return val;
}

g_val_t
mem_free_func ( void )
{
   char *p;
   g_val_t val;

   p = strstr( update_file(&proc_meminfo), "MemFree:" );
   if(p) {
     p = skip_token(p);
     val.uint32 = strtol( p, (char **)NULL, 10 );
   } else {
     val.uint32 = 0;
   }

   return val;
}

g_val_t
mem_shared_func ( void )
{
   char *p;
   g_val_t val;

   /*
   ** Broken since linux-2.5.52 when Memshared was removed !!
   */
   p = strstr( update_file(&proc_meminfo), "MemShared:" );
   if (p) {
      p = skip_token(p);
      val.uint32 = strtol( p, (char **)NULL, 10 );
   } else {
      val.uint32 = 0;
   }

   return val;
}

g_val_t
mem_buffers_func ( void )
{
   char *p;
   g_val_t val;

   p = strstr( update_file(&proc_meminfo), "Buffers:" );
   if(p) {
     p = skip_token(p);
     val.uint32 = strtol( p, (char **)NULL, 10 ); 
   } else {
     val.uint32 = 0;
   }

   return val;
}

g_val_t
mem_cached_func ( void )
{
   char *p;
   g_val_t val;

   p = strstr( update_file(&proc_meminfo), "Cached:");
   if(p) {
     p = skip_token(p);
     val.uint32 = strtol( p, (char **)NULL, 10 );
   } else {
     val.uint32 = 0;
   }

   return val;
}

g_val_t
swap_free_func ( void )
{
   char *p;
   g_val_t val;

   p = strstr( update_file(&proc_meminfo), "SwapFree:" );
   if(p) {
     p = skip_token(p);
     val.uint32 = strtol( p, (char **)NULL, 10 ); 
   } else {
     val.uint32 = 0;
   }

   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;
}

/* ===================================================== */
/* Disk Usage. Using fsusage.c from the GNU fileutils-4.1.5 package. */
#include <inttypes.h>

/* Linux Specific, but we are in the Linux machine file. */
#define MOUNTS "/proc/mounts"

struct nlist {
   struct nlist *next;
   char *name;
};

#define DFHASHSIZE 101
static struct nlist *DFhashvector[DFHASHSIZE];

/* --------------------------------------------------------------------------- */
unsigned int DFhash(const char *s)
{
   unsigned int hashval;
   for (hashval=0; *s != '\0'; s++)
      hashval = *s + 31 * hashval;
   return hashval % DFHASHSIZE;
}

/* --------------------------------------------------------------------------- */
/* From K&R C book, pp. 144-145 */
struct nlist * seen_before(const char *name)
{
   struct nlist *found=0, *np;
   unsigned int hashval;

   /* lookup */
   hashval=DFhash(name);
   for (np=DFhashvector[hashval]; np; np=np->next) {
      if (!strcmp(name,np->name)) {
         found=np;
         break;
      }
   }
   if (!found) {    /* not found */
      np = (struct nlist *) malloc(sizeof(*np));
      if (!np || !(np->name = (char *) strdup(name)))
         return NULL;
      np->next = DFhashvector[hashval];
      DFhashvector[hashval] = np;
      return NULL;
   }
   else /* found name */
      return found;
}

/* --------------------------------------------------------------------------- */
void DFcleanup()
{
   struct nlist *np, *next;
   int i;
   for (i=0; i<DFHASHSIZE; i++) {
      /* Non-standard for loop. Note the last clause happens at the end of the loop. */
      for (np = DFhashvector[i]; np; np=next) {
         next=np->next;
         free(np->name);
         free(np);
      }
      DFhashvector[i] = 0;
   }
}

/* --------------------------------------------------------------------------- */
int remote_mount(const char *device, const char *type)
{
   /* From ME_REMOTE macro in mountlist.h:
   A file system is `remote' if its Fs_name contains a `:'
   or if (it is of type smbfs and its Fs_name starts with `//'). */
   return ((strchr(device,':') != 0)
      || (!strcmp(type, "smbfs") && device[0]=='/' && device[1]=='/')
      || (!strncmp(type, "nfs", 3)) || (!strcmp(type, "autofs"))
      || (!strcmp(type,"gfs")) || (!strcmp(type,"none")) );
}

/* --------------------------------------------------------------------------- */
float device_space(char *mount, char *device, double *total_size, double *total_free)
{
   struct statvfs svfs;
   uint32_t blocksize;
   uint32_t free;
   uint32_t size;
   /* The percent used: used/total * 100 */
   float pct=0.0;

   /* Avoid multiply-mounted disks - not done in df. */
   if (seen_before(device)) return pct;

   if (statvfs(mount, &svfs)) {
      /* Ignore funky devices... */
      return pct;
   }

   free = svfs.f_bavail;
   size  = svfs.f_blocks;
   blocksize = svfs.f_bsize;
   /* Keep running sum of total used, free local disk space. */
   *total_size += size * (double) blocksize;
   *total_free += free * (double) blocksize;
   /* The percentage of space used on this partition. */
   pct = size ? ((size - free) / (float) size) * 100 : 0.0;
   return pct;
}

/* --------------------------------------------------------------------------- */
float find_disk_space(double *total_size, double *total_free)
{
   FILE *mounts;
   char procline[256];
   char mount[128], device[128], type[32], mode[128];
   /* We report in GB = 1e9 bytes. */
   double reported_units = 1e9;
   /* Track the most full disk partition, report with a percentage. */
   float thispct, max=0.0;
   int rc;

   /* Read all currently mounted filesystems. */
   mounts=fopen(MOUNTS,"r");
   if (!mounts) {
      debug_msg("Df Error: could not open mounts file %s. Are we on the right OS?\n", MOUNTS);
      return max;
   }
   while ( fgets(procline, sizeof(procline), mounts) ) {
      rc=sscanf(procline, "%s %s %s %s ", device, mount, type, mode);
      if (!rc) continue;
      if (!strncmp(mode, "ro", 2)) continue;
      if (remote_mount(device, type)) continue;
      if (strncmp(device, "/dev/", 5) != 0 &&
          strncmp(device, "/dev2/", 6) != 0) continue;
      thispct = device_space(mount, device, total_size, total_free);
      debug_msg("Counting device %s (%.2f %%)", device, thispct);
      if (!max || max<thispct)
         max = thispct;
   }
   fclose(mounts);

   *total_size = *total_size / reported_units;
   *total_free = *total_free / reported_units;
   debug_msg("For all disks: %.3f GB total, %.3f GB free for users.", *total_size, *total_free);

   DFcleanup();
   return max;
}

/* --------------------------------------------------------------------------- */
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;
}

/* ---------------------------------- */
/*
 * Stub for doing stand-alone testing
 *
 * Compile with:
 *
 *
gcc -g -O2 -D_REENTRANT -Wall -I.. -I../lib -I../lib/dnet -DHAVE_CONFIG_H \
   -DSTAND_ALONE_TEST  -o gmo.tst machine.c ../lib/.libs/libganglia.a \
   ../lib/libgetopthelper.a -ldl -lresolv -lnsl -lpthread
 *
 */
#ifdef STAND_ALONE_TEST
int main()
{
g_val_t tval;

metric_init();
printf("num_cpustates = %d\n",num_cpustates);

tval = proc_total_func();
printf("Tot proc: %d\n",tval.uint32);

cpu_user_func();
cpu_nice_func();
cpu_system_func();
cpu_idle_func();
cpu_wio_func();
cpu_intr_func();
cpu_sintr_func();


sleep(5);

tval = cpu_user_func();
printf("User pct.: %f\n",tval.f);
tval = cpu_nice_func();
printf("Nice pct.: %f\n",tval.f);
tval = cpu_system_func();
printf("Sys  pct.: %f\n",tval.f);
tval = cpu_idle_func();
printf("Idle pct.: %f\n",tval.f);
tval = cpu_wio_func();
printf("Wio  pct.: %f\n",tval.f);
tval = cpu_intr_func();
printf("Intr pct.: %f\n",tval.f);
tval = cpu_sintr_func();
printf("Sntr pct.: %f\n",tval.f);

return 0;
}
#endif



syntax highlighted by Code2HTML, v. 0.9.1