*** ./gmond/modules/conf.d/ibmpower.conf.ORIG	Fri May 25 17:10:12 2012
--- ./gmond/modules/conf.d/ibmpower.conf	Fri May 25 17:16:20 2012
***************
*** 0 ****
--- 1,130 ----
+ 
+ modules {
+   module {
+     name = "ibmpower_module"
+     path = "modibmpower.so"
+   }
+ }
+ 
+ collection_group {
+   collect_once = yes
+   time_threshold = 1200
+   metric {
+     name = "kernel64bit"
+     title = "Kernel 64 bit?"
+   }
+   metric {
+     name = "lpar"
+     title = "LPAR Mode?"
+   }
+   metric {
+     name = "splpar"
+     title = "Shared Processor LPAR?"
+   }
+ }
+ 
+ collection_group {
+   collect_every = 180
+   time_threshold = 1200
+   metric {
+     name = "cpu_in_machine"
+     title = "Cores in Machine"
+   }
+   metric {
+     name = "fwversion"
+     title = "Firmware Version"
+   }
+   metric {
+     name = "lpar_name"
+     title = "LPAR Name"
+   }
+   metric {
+     name = "lpar_num"
+     title = "LPAR Number"
+   }
+   metric {
+     name = "model_name"
+     title = "Machine Name"
+   }
+   metric {
+     name = "oslevel"
+     title = "Output of 'oslevel -s'"
+   }
+   metric {
+     name = "serial_num"
+     title = "System Serial Number"
+   }
+ }
+ 
+ collection_group {
+   collect_every = 15
+   time_threshold = 180
+   metric {
+     name = "capped"
+     title = "Capped Mode?"
+   }
+   metric {
+     name = "cpu_pool_id"
+     title = "Shared processor pool ID of this LPAR"
+   }
+   metric {
+     name = "cpu_entitlement"
+     title = "CPU Entitlement"
+     value_threshold = 0.01
+   }
+   metric {
+     name = "cpu_in_lpar"
+     title = "Number of Virtual CPUs in LPAR"
+     value_threshold = 1
+   }
+   metric {
+     name = "cpu_in_pool"
+     title = "Number of Cores in Pool"
+     value_threshold = 1
+   }
+   metric {
+     name = "cpu_in_syspool"
+     title = "Number of Cores in System Pool"
+     value_threshold = 1
+   }
+   metric {
+     name = "disk_iops"
+     title = "Total number I/O operations per second"
+     value_threshold = 1.0
+   }
+   metric {
+     name = "disk_read"
+     title = "Total Disk Read I/O per second"
+     value_threshold = 1.0
+   }
+   metric {
+     name = "disk_write"
+     title = "Total Disk Write I/O per second"
+     value_threshold = 1.0
+   }
+   metric {
+     name = "smt"
+     title = "SMT enabled?"
+   }
+   metric {
+     name = "weight"
+     title = "LPAR Weight"
+     value_threshold = 1
+   }
+ }
+ 
+ collection_group {
+   collect_every = 15
+   time_threshold = 60
+   metric {
+     name = "cpu_pool_idle"
+     title = "CPU Pool Idle"
+     value_threshold = 0.0001
+   }
+   metric {
+     name = "cpu_used"
+     title = "Physical Cores Used"
+     value_threshold = 0.0001
+   }
+ }
+ 
*** ./gmond/modules/ibmpower/Makefile.am.ORIG	Thu Sep  2 14:54:41 2010
--- ./gmond/modules/ibmpower/Makefile.am	Thu Sep  2 14:53:54 2010
***************
*** 0 ****
--- 1,16 ----
+ AM_CFLAGS  = -I$(top_builddir)/include -I$(top_builddir)/lib -I$(top_builddir)/libmetrics
+ 
+ if STATIC_BUILD
+ noinst_LTLIBRARIES    = libmodibmpower.la
+ libmodibmpower_la_SOURCES = mod_ibmpower.c 
+ else
+ pkglib_LTLIBRARIES    = modibmpower.la
+ modibmpower_la_SOURCES = mod_ibmpower.c 
+ modibmpower_la_LDFLAGS = -module -avoid-version
+ modibmpower_la_LIBADD = $(top_builddir)/libmetrics/libmetrics.la
+ 
+ EXTRA_DIST = ../conf.d/ibmpower.conf
+ endif
+ 
+ INCLUDES = @APR_INCLUDES@
+ 
*** ./gmond/modules/ibmpower/mod_ibmpower-aix.c.ORIG	Mon Mar  5 22:36:10 2012
--- ./gmond/modules/ibmpower/mod_ibmpower-aix.c	Mon Mar  5 22:00:37 2012
***************
*** 0 ****
--- 1,1118 ----
+ /******************************************************************************
+  *
+  *  This module implements IBM POWER5- and POWER6-specific extensions like:
+  *    - SPLPAR
+  *    - SMT
+  *    - CPU Entitlement
+  *    - Capped/Uncapped
+  *    - etc.
+  *
+  *  The libperfstat API is used and it can deal with a 32-bit and a 64-bit
+  *  kernel and does not require root authority.
+  *
+  *  The code has been tested with AIX 5.1, AIX 5.2, AIX 5.3, AIX 6.1 and AIX 7.1
+  *  on different systems.
+  *
+  *  Written by Michael Perzl (michael@perzl.org)
+  *
+  *  Version 1.4, Feb 09, 2012
+  *
+  *  based on Ganglia V3.0.7 AIX Ganglia libmetrics code written by:
+  *         Michael Perzl (michael@perzl.org)
+  *     and Nigel Griffiths (nigelargriffiths@hotmail.com)
+  *
+  *  Version 1.4:  Feb 09, 2012
+  *                - added new metric cpu_pool_id
+  *                  (--> cpu_pool_id_func() )
+  *
+  *  Version 1.3:  Apr 27, 2010
+  *                - added sanity check for cpu_pool_idle_func()
+  *                - added new metric fwversion
+  *                  (--> fwversion_func() )
+  *
+  *  Version 1.2:  Feb 10, 2010
+  *                - added IO ops/sec metric
+  *                  (--> disk_iops_func() )
+  *                - changed metric type from GANGLIA_VALUE_FLOAT to
+  *                  GANGLIA_VALUE_DOUBLE and changed unit to bytes/sec
+  *                  for disk_read_func() and disk_write_func()
+  *                - added model_name metric
+  *                  (--> model_name_func() )
+  *
+  *  Version 1.1:  Jan 21, 2010
+  *                - improved cpu_used() function
+  *                - fixed defuncts caused by open pipes
+  *                  (--> popen() without pclose() )
+  *                - added checks for possible libperfstat counter resets in
+  *                  * cpu_pool_idle_func()
+  *                  * cpu_used_func()
+  *                  * disk_read_func()
+  *                  * disk_write_func()
+  *
+  *  Version 1.0:  Dec 11, 2008
+  *                - initial version
+  *
+  ******************************************************************************/
+ 
+ /*
+  * The ganglia metric "C" interface, required for building DSO modules.
+  */
+ 
+ #include <gm_metric.h>
+ 
+ 
+ #include <stdlib.h>
+ #include <strings.h>
+ #include <time.h>
+ 
+ #include <ctype.h>
+ #include <utmp.h>
+ #include <stdio.h>
+ #include <sys/types.h>
+ #include <procinfo.h>
+ #include <signal.h>
+ #include <odmi.h>
+ #include <cf.h>
+ #include <sys/utsname.h>
+ 
+ #if !defined(_AIX43)
+ #include <sys/dr.h>
+ #endif
+ #include <sys/systemcfg.h>
+ 
+ #include <libperfstat.h>
+ 
+ #include "libmetrics.h"
+ 
+ 
+ static int isVIOserver;
+ 
+ static time_t boottime;
+ 
+ 
+ g_val_t
+ capped_func( void )
+ {
+    g_val_t val;
+ #if defined(_AIX53) || defined(_AIX61) || defined(_AIX71)
+    perfstat_partition_total_t p;
+ 
+ 
+    if (perfstat_partition_total( NULL, &p, sizeof( perfstat_partition_total_t ), 1 ) == -1)
+       strcpy( val.str, "libperfstat returned an error" );
+    else
+       if ( __LPAR() && p.type.b.shared_enabled )
+          strcpy ( val.str, p.type.b.capped ? "yes" : "no" );
+       else
+          strcpy( val.str, "No SPLPAR-capable system" );
+ #else
+    strcpy( val.str, "No SPLPAR-capable system" );
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ cpu_entitlement_func( void )
+ {
+    g_val_t val;
+ #if defined(_AIX53) || defined(_AIX61) || defined(_AIX71)
+    perfstat_partition_total_t p;
+ 
+ 
+    if (perfstat_partition_total( NULL, &p, sizeof( perfstat_partition_total_t ), 1 ) == -1)
+       val.f = 0.0;
+    else
+       if ( p.type.b.shared_enabled )
+       {
+          val.f = p.entitled_proc_capacity / 100.0;
+       }
+       else /* dedicated LPAR/standalone system so fake entitled as number of online CPUs */
+          val.f = p.online_cpus;
+ #else
+    perfstat_cpu_total_t c;
+ 
+ 
+    if (perfstat_cpu_total( NULL, &c, sizeof( perfstat_cpu_total_t ), 1 ) == -1)
+       val.f = 0.;
+    else
+       val.f = c.ncpus;
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ cpu_in_lpar_func( void )
+ {
+    g_val_t val;
+ #if defined(_AIX53) || defined(_AIX61) || defined(_AIX71)
+    perfstat_partition_total_t p;
+ 
+ 
+    if (perfstat_partition_total( NULL, &p, sizeof( perfstat_partition_total_t ), 1 ) == -1)
+       val.int32 = -1;
+    else
+       val.int32 = p.online_cpus;
+ #else
+    perfstat_cpu_total_t c;
+ 
+ 
+    if (perfstat_cpu_total( NULL, &c, sizeof( perfstat_cpu_total_t ), 1 ) == -1)
+       val.int32 = -1;
+    else
+       val.int32 = c.ncpus_cfg;
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ cpu_in_machine_func( void )
+ {
+    g_val_t val;
+ #if defined(_AIX53) || defined(_AIX61) || defined(_AIX71)
+    perfstat_partition_total_t p;
+ 
+ 
+    if (perfstat_partition_total( NULL, &p, sizeof( perfstat_partition_total_t ), 1 ) == -1)
+       val.int32 = -1;
+    else
+       val.int32 = p.online_phys_cpus_sys;
+ #else
+    perfstat_cpu_total_t c;
+ 
+ 
+    if (perfstat_cpu_total( NULL, &c, sizeof( perfstat_cpu_total_t ), 1 ) == -1)
+       val.int32 = -1;
+    else
+       val.int32 = c.ncpus;
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ cpu_in_pool_func( void )
+ {
+    g_val_t val;
+ #if defined(_AIX53) || defined(_AIX61) || defined(_AIX71)
+    perfstat_partition_total_t p;
+ 
+ 
+    if (perfstat_partition_total( NULL, &p, sizeof( perfstat_partition_total_t ), 1 ) == -1)
+       val.int32 = -1;
+    else
+       val.int32 = p.phys_cpus_pool;
+ #else
+    perfstat_cpu_total_t c;
+ 
+ 
+    if (perfstat_cpu_total( NULL, &c, sizeof( perfstat_cpu_total_t ), 1 ) == -1)
+       val.int32 = -1;
+    else
+       val.int32 = c.ncpus;
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ cpu_in_syspool_func( void )
+ {
+    g_val_t val;
+ #if defined(_AIX53) || defined(_AIX61) || defined(_AIX71)
+    perfstat_partition_total_t p;
+ 
+ 
+    if (perfstat_partition_total( NULL, &p, sizeof( perfstat_partition_total_t ), 1 ) == -1)
+       val.int32 = -1;
+    else
+ #if defined(POWER6_POOLS)
+    {
+       val.int32 = p.shcpus_in_sys;
+ 
+       if ((val.int32 == 0) && (p.phys_cpus_pool > 0))
+          val.int32 = p.phys_cpus_pool;
+    }
+ #else
+       val.int32 = p.phys_cpus_pool;
+ #endif
+ #else
+    perfstat_cpu_total_t c;
+ 
+ 
+    if (perfstat_cpu_total( NULL, &c, sizeof( perfstat_cpu_total_t ), 1 ) == -1)
+       val.int32 = -1;
+    else
+       val.int32 = c.ncpus;
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ cpu_pool_id_func( void )
+ {
+    g_val_t val;
+ #if defined(_AIX53) || defined(_AIX61) || defined(_AIX71)
+    perfstat_partition_total_t p;
+ 
+ 
+    if ( __LPAR() )
+    {
+       if (perfstat_partition_total( NULL, &p, sizeof( perfstat_partition_total_t ), 1) == -1)
+          val.int32 = -1;
+       else
+          val.int32 = p.pool_id;
+    }
+    else
+       val.int32 = -1;
+ #else
+       val.int32 = -1;
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ #define MAX_CPU_POOL_IDLE (256.0)
+ 
+ g_val_t
+ cpu_pool_idle_func( void )
+ {
+    g_val_t val;
+ #if defined(_AIX53) || defined(_AIX61) || defined(_AIX71)
+    perfstat_partition_total_t p;
+    lpar_info_format2_t f2;
+    static uint64_t saved_pool_idle_time = 0LL;
+    longlong_t diff;
+    static double last_time = 0.0;
+    static float last_val = 0.0;
+    double now, delta_t;
+    struct timeval timeValue;
+    struct timezone timeZone;
+ 
+ 
+    gettimeofday( &timeValue, &timeZone );
+ 
+    now = (double) (timeValue.tv_sec - boottime) + (timeValue.tv_usec / 1000000.0);
+  
+    lpar_get_info( LPAR_INFO_FORMAT2, &f2, sizeof( lpar_info_format2_t ) );
+ 
+    if (perfstat_partition_total( NULL, &p, sizeof( perfstat_partition_total_t ), 1 ) == -1)
+       val.f = 0.0;
+    else
+    {
+       delta_t = now - last_time;
+ 
+       if ( p.type.b.shared_enabled )
+       {
+          if ( (delta_t > 0.0) && (f2.lpar_flags & LPAR_INFO2_AUTH_PIC) )
+          {
+             diff = f2.pool_idle_time - saved_pool_idle_time;
+ 
+             if (diff >= 0LL)
+                val.f = (double) diff / delta_t / 1000.0 / 1000.0 / 1000.0;
+             else
+                val.f = last_val;
+          }
+          else
+             val.f = 0.0;
+ 
+          saved_pool_idle_time = f2.pool_idle_time;
+       }
+       else
+          val.f = 0.0;
+    }
+ 
+ /* prevent against huge value when suddenly performance data collection */
+ /* is enabled or disabled for this LPAR */
+    if (val.f > MAX_CPU_POOL_IDLE)
+       val.f = 0.0;
+ 
+    last_time = now;
+    last_val = val.f;
+ #else
+    val.f = 0.0;
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ cpu_used_func( void )
+ {
+    g_val_t val;
+ #if defined(_AIX53) || defined(_AIX61) || defined(_AIX71)
+    perfstat_partition_total_t p;
+    lpar_info_format2_t f2;
+    static uint64_t saved_dispatch_time = 0LL;
+    longlong_t diff;
+    static double last_time = 0.0;
+    static float last_val = 0.0;
+    double now, delta_t;
+    struct timeval timeValue;
+    struct timezone timeZone;
+ 
+ 
+    gettimeofday( &timeValue, &timeZone );
+ 
+    now = (double) (timeValue.tv_sec - boottime) + (timeValue.tv_usec / 1000000.0);
+ 
+    if (lpar_get_info( LPAR_INFO_FORMAT2, &f2, sizeof( lpar_info_format2_t ) ) == -1)
+       val.f = 0.0;
+    else
+       if (perfstat_partition_total( NULL, &p, sizeof( perfstat_partition_total_t ), 1 ) == -1)
+          val.f = 0.0;
+       else
+       {
+          delta_t = now - last_time;
+ 
+          if ( p.type.b.shared_enabled )
+          {
+             if ( delta_t > 0.0 )
+             {
+                diff = f2.tot_dispatch_time - saved_dispatch_time;
+ 
+                if (diff >= 0LL)
+                   val.f = (double) diff / delta_t / 1000.0 / 1000.0 / 1000.0;
+                else
+                   val.f = last_val;
+             }
+             else
+                val.f = 0.0;
+ 
+             saved_dispatch_time = f2.tot_dispatch_time;
+          }
+          else /* dedicated LPAR/standalone system so calculate cpu_used with cpu_idle_func() */
+          {
+             val = cpu_idle_func();
+             val.f = (float) p.online_cpus * (100.0 - val.f) / 100.0;
+          }
+       }
+ 
+    last_time = now;
+    last_val = val.f;
+ #else
+    perfstat_cpu_total_t c;
+ 
+ 
+    if (perfstat_cpu_total( NULL, &c, sizeof( perfstat_cpu_total_t ), 1 ) == -1)
+       val.f = 0.0;
+    else
+    {
+       val = cpu_idle_func();
+       val.f = (float) c.ncpus_cfg * (100.0 - val.f) / 100.0;
+    }
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ disk_iops_func( void )
+ {
+    g_val_t val;
+    static perfstat_disk_total_t d1, d2;
+    static double last_time = 0.0;
+    static double last_val = 0.0;
+    longlong_t diff = 0LL;
+    double now, delta_t;
+    struct timeval timeValue;
+    struct timezone timeZone;
+ 
+ 
+    gettimeofday( &timeValue, &timeZone );
+ 
+    now = (double) (timeValue.tv_sec - boottime) + (timeValue.tv_usec / 1000000.0);
+ 
+    if (perfstat_disk_total( NULL, &d2, sizeof( perfstat_disk_total_t ), 1 ) == -1)
+       val.d = 0.0;
+    else
+    {
+       delta_t = now - last_time;
+ 
+       if ( delta_t > 0.0 )
+       {
+          diff = d2.xfers - d1.xfers;
+ 
+          if (diff >= 0LL)
+             val.d = diff / delta_t;
+          else
+             val.d = last_val;
+       }
+       else
+          val.d = 0.0;
+ 
+       d1 = d2;
+    }
+ 
+    last_time = now;
+    last_val = val.d;
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ disk_read_func( void )
+ {
+    g_val_t val;
+    static perfstat_disk_total_t d1, d2;
+    static double last_time = 0.0;
+    static double last_val = 0.0;
+    longlong_t diff = 0LL;
+    double now, delta_t;
+    struct timeval timeValue;
+    struct timezone timeZone;
+ 
+ 
+    gettimeofday( &timeValue, &timeZone );
+ 
+    now = (double) (timeValue.tv_sec - boottime) + (timeValue.tv_usec / 1000000.0);
+    if (perfstat_disk_total( NULL, &d2, sizeof( perfstat_disk_total_t ), 1 ) == -1)
+       val.d = 0.0;
+    else
+    {
+       delta_t = now - last_time;
+ 
+       if ( delta_t > 0.0 )
+       {
+          diff = d2.rblks - d1.rblks;
+ 
+          if (diff >= 0LL)
+ /* the result is returned in number of 512 byte blocks */
+             val.d = (diff * 512.0) / delta_t;
+          else
+             val.d = last_val;
+       }
+       else
+          val.d = 0.0;
+ 
+       d1 = d2;
+    }
+ 
+    last_time = now;
+    last_val = val.d;
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ disk_write_func( void )
+ {
+    g_val_t val;
+    static perfstat_disk_total_t d1, d2;
+    static double last_time = 0.0;
+    static double last_val = 0.0;
+    longlong_t diff = 0LL;
+    double now, delta_t;
+    struct timeval timeValue;
+    struct timezone timeZone;
+ 
+ 
+    gettimeofday( &timeValue, &timeZone );
+ 
+    now = (double) (timeValue.tv_sec - boottime) + (timeValue.tv_usec / 1000000.0);
+ 
+    if (perfstat_disk_total( NULL, &d2, sizeof( perfstat_disk_total_t ), 1 ) == -1)
+       val.d = 0.0;
+    else
+    {
+       delta_t = now - last_time;
+ 
+       if ( delta_t > 0.0 )
+       {
+          diff = d2.wblks - d1.wblks;
+ 
+          if (diff >= 0LL)
+ /* the result is returned in number of 512 byte blocks */
+             val.d = (diff * 512.0) / delta_t;
+          else
+             val.d = last_val;
+       }
+       else
+          val.d = 0.0;
+ 
+       d1 = d2;
+    }
+ 
+    last_time = now;
+    last_val = val.d;
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ fwversion_func( void )
+ {
+    FILE    *f;
+    g_val_t  val;
+ 
+ 
+    f = popen( "/usr/sbin/lsattr -El sys0 -a fwversion | /usr/bin/awk '{ print $2 }' 2>/dev/null", "r" );
+ 
+    if (f == NULL)
+    {
+       strcpy( val.str, "popen 'lsattr -El sys0' failed" );
+    }
+    else
+    {
+       if ( fgets( val.str, MAX_G_STRING_SIZE, f ) != NULL )
+       {
+          val.str[MAX_G_STRING_SIZE - 1] = '\0';
+          val.str[strlen( val.str ) - 1] = '\0';  /* truncate \n */
+       }
+       else
+          strcpy( val.str, "Can't run AIX cmd 'lsattr'" );
+ 
+       pclose( f );
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ kernel64bit_func( void )
+ {
+    g_val_t val;
+ 
+    strcpy ( val.str, __KERNEL_64() ? "yes" : "no" );
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ lpar_func( void )
+ {
+    g_val_t val;
+ 
+ #if defined(_AIX43)
+    strcpy ( val.str, "no" );
+ #else
+    strcpy ( val.str, __LPAR() ? "yes" : "no" );
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ lpar_name_func( void )
+ {
+    g_val_t val;
+ #if defined(_AIX53) || defined(_AIX61) || defined(_AIX71)
+    perfstat_partition_total_t p;
+ 
+ 
+    if ( __LPAR() )
+    {
+       if (perfstat_partition_total( NULL, &p, sizeof( perfstat_partition_total_t ), 1) == -1)
+          strcpy( val.str, "libperfstat returned an error" );
+       else
+          strcpy( val.str, p.name );
+    }
+    else
+       strcpy( val.str, "No LPAR system" );
+ #else
+    FILE *f;
+    char  buf[64], *p;
+ 
+ #if defined(_AIX43)
+    if ( 0 )
+ #else
+    if ( __LPAR() )
+ #endif
+    {
+       f = popen( "/usr/bin/uname -L", "r" );
+ 
+       if (f == NULL)
+          strcpy( val.str, "popen of cmd 'uname -L' failed" );
+       else
+       {
+          if ( fgets( buf, 64, f ) != NULL)
+          {
+             p = &buf[0];
+             while (*p != ' ') p++;
+             p++;
+ 
+             strncpy( val.str, p, MAX_G_STRING_SIZE );
+             val.str[MAX_G_STRING_SIZE - 1] = '\0';
+             val.str[strlen( val.str ) - 1] = '\0';  /* truncate \n */
+          }
+          else
+             strcpy( val.str, "Can't run 'uname -L'" );
+ 
+          pclose( f );
+       }
+    }
+    else
+       strcpy( val.str, "No LPAR system" );
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ lpar_num_func( void )
+ {
+    g_val_t val;
+ #if defined(_AIX53) || defined(_AIX61) || defined(_AIX71)
+    perfstat_partition_total_t p;
+ 
+ 
+    if (perfstat_partition_total( NULL, &p, sizeof( perfstat_partition_total_t ), 1) == -1)
+       val.int32 = -1;
+    else
+       val.int32 = p.lpar_id;
+ #else
+    FILE *f;
+    char s[MAX_G_STRING_SIZE];
+ 
+ #if defined(_AIX43)
+    if ( 0 )
+ #else
+    if ( __LPAR() )
+ #endif
+    {
+       f = popen( "/usr/bin/uname -L 2>/dev/null", "r" );
+       if (f == NULL)
+          val.int32 = -1;
+       else
+       {
+          if ( fgets( s, MAX_G_STRING_SIZE, f ) != NULL)
+             val.int32 = atoi( s );
+          else
+             val.int32 = -1;
+ 
+          pclose( f );
+       }
+    }
+    else
+       val.int32 = -1;
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ model_name_func( void )
+ {
+    FILE    *f;
+    g_val_t  val;
+ 
+ 
+    f = popen( "/usr/bin/uname -M 2>/dev/null", "r" );
+ 
+    if (f == NULL)
+    {
+       strcpy( val.str, "popen of cmd 'uname -M' failed" );
+    }
+    else
+    {
+       if ( fgets( val.str, MAX_G_STRING_SIZE, f ) != NULL )
+       {
+          val.str[MAX_G_STRING_SIZE - 1] = '\0';
+          val.str[strlen( val.str ) - 1] = '\0';  /* truncate \n */
+       }
+       else
+          strcpy( val.str, "Can't run AIX cmd 'uname'" );
+ 
+       pclose( f );
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ static g_val_t
+ oslevel_func_CALLED_ONCE( void )
+ {
+    FILE    *f;
+    g_val_t  val;
+ 
+ 
+    if (isVIOserver)
+    {
+       f = popen( "/usr/ios/cli/ioscli ioslevel 2>/dev/null", "r" );
+ 
+       if (f == NULL)
+       {
+          strcpy( val.str, "popen of cmd 'ioscli' failed" );
+       }
+       else
+       {
+          if ( fgets( val.str, MAX_G_STRING_SIZE, f ) != NULL )
+          {
+             val.str[MAX_G_STRING_SIZE - 1] = '\0';
+             val.str[strlen( val.str ) - 1] = '\0';  /* truncate \n */
+          }
+          else
+             strcpy( val.str, "Can't run AIX cmd 'ioscli'" );
+ 
+          pclose( f );
+       }
+    }
+    else
+    {
+       f = popen( "/usr/bin/oslevel -s 2>/dev/null", "r" );
+ 
+       if (f == NULL)
+       {
+          strcpy( val.str, "popen of cmd 'oslevel -s' failed" );
+       }
+       else
+       {
+          if ( fgets( val.str, MAX_G_STRING_SIZE, f ) != NULL )
+          {
+             val.str[MAX_G_STRING_SIZE - 1] = '\0';
+             val.str[strlen( val.str ) - 1] = '\0';  /* truncate \n */
+          }
+          else
+             strcpy( val.str, "Can't run AIX cmd 'oslevel'" );
+ 
+          if (! strncmp( val.str, "Usage: oslevel", 14 ))
+          {
+             pclose( f );
+ 
+             f = popen( "/usr/bin/oslevel -r 2>/dev/null", "r" );
+       
+             if ( fgets( val.str, MAX_G_STRING_SIZE, f ) != NULL )
+             {
+                val.str[MAX_G_STRING_SIZE - 1] = '\0';
+                val.str[strlen( val.str ) - 1] = '\0';  /* truncate \n */
+             }
+             else
+                strcpy( val.str, "Can't run AIX cmd 'oslevel'" );
+          }
+ 
+          pclose( f );
+       }
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ oslevel_func( void )
+ {
+    static g_val_t val;
+    static int firstTime = 1;
+ 
+ 
+    if (firstTime)
+    {
+       val = oslevel_func_CALLED_ONCE();
+       firstTime = 0;
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ serial_num_func( void )
+ {
+    FILE    *f;
+    g_val_t  val;
+ 
+ 
+    f = popen( "/usr/bin/uname -u 2>/dev/null", "r" );
+ 
+    if (f == NULL)
+    {
+       strcpy( val.str, "popen of cmd 'uname -u' failed" );
+    }
+    else
+    {
+       if ( fgets( val.str, MAX_G_STRING_SIZE, f ) != NULL )
+       {
+          val.str[MAX_G_STRING_SIZE - 1] = '\0';
+          val.str[strlen( val.str ) - 1] = '\0';  /* truncate \n */
+       }
+       else
+       {
+          strcpy( val.str, "Can't run AIX cmd 'uname -u'" );
+       }
+ 
+       pclose( f );
+    }
+ 
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ smt_func( void )
+ {
+    g_val_t val;
+ #if defined(_AIX53) || defined(_AIX61)
+    perfstat_partition_total_t p;
+ 
+ 
+    if (perfstat_partition_total( NULL, &p, sizeof( perfstat_partition_total_t), 1 ) == -1)
+       strcpy( val.str, "libperfstat returned an error" );
+    else
+       strcpy( val.str, p.type.b.smt_enabled ? "yes" : "no" );
+ #else
+    strcpy( val.str, "No SMT-capable system" );
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ splpar_func( void )
+ {
+    g_val_t val;
+ #if defined(_AIX53) || defined(_AIX61)
+    perfstat_partition_total_t p;
+ 
+ 
+    if (perfstat_partition_total( NULL, &p, sizeof( perfstat_partition_total_t), 1) == -1)
+       strcpy( val.str, "libperfstat returned an error" );
+    else
+       strcpy( val.str, p.type.b.shared_enabled ? "yes" : "no" );
+ #else
+    strcpy( val.str, "No SPLPAR-capable system" );
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ weight_func( void )
+ {
+    g_val_t val;
+ #if defined(_AIX53) || defined(_AIX61)
+    perfstat_partition_total_t p;
+ 
+ 
+    if (perfstat_partition_total( NULL, &p, sizeof( perfstat_partition_total_t), 1 ) == -1)
+       val.int32 = -1;
+    else
+       if ( p.type.b.shared_enabled )
+          val.int32 = p.var_proc_capacity_weight;
+       else
+          val.int32 = -1;
+ #else
+    val.int32 = -1;
+ #endif
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ static time_t
+ boottime_func_CALLED_ONCE( void )
+ {
+    time_t boottime;
+    struct utmp buf;
+    FILE *utmp;
+ 
+ 
+    utmp = fopen( UTMP_FILE, "r" );
+ 
+    if (utmp == NULL)
+    {
+       /* Can't open utmp, use current time as boottime */
+       boottime = time( NULL );
+    }
+    else
+    {
+       while (fread( (char *) &buf, sizeof( buf ), 1, utmp ) == 1)
+       {
+          if (buf.ut_type == BOOT_TIME)
+          {
+             boottime = buf.ut_time;
+             break;
+         }
+       }
+ 
+       fclose( utmp );
+    }
+ 
+    return( boottime );
+ }
+ 
+ 
+ 
+ /*
+  * Declare ourselves so the configuration routines can find and know us.
+  * We'll fill it in at the end of the module.
+  */
+ extern mmodule ibmpower_module;
+ 
+ 
+ static int ibmpower_metric_init ( apr_pool_t *p )
+ {
+    int i;
+    FILE *f;
+    g_val_t val;
+ 
+ 
+    for (i = 0;  ibmpower_module.metrics_info[i].name != NULL;  i++)
+    {
+       /* Initialize the metadata storage for each of the metrics and then
+        *  store one or more key/value pairs.  The define MGROUPS defines
+        *  the key for the grouping attribute. */
+       MMETRIC_INIT_METADATA( &(ibmpower_module.metrics_info[i]), p );
+       MMETRIC_ADD_METADATA( &(ibmpower_module.metrics_info[i]), MGROUP, "ibmpower" );
+    }
+ 
+ 
+ /* find out if we are running on a VIO server */
+ 
+    f = fopen( "/usr/ios/cli/ioscli", "r" );
+ 
+    if (f)
+    {
+       isVIOserver = 1;
+       fclose( f );
+    }
+    else
+       isVIOserver = 0;
+ 
+ 
+ /* initialize the routines which require a time interval */
+ 
+    boottime = boottime_func_CALLED_ONCE();
+    val = cpu_pool_idle_func();
+    val = cpu_used_func();
+    val = disk_iops_func();
+    val = disk_read_func();
+    val = disk_write_func();
+ 
+    return( 0 );
+ }
+ 
+ 
+ 
+ static void ibmpower_metric_cleanup ( void )
+ {
+ }
+ 
+ 
+ 
+ static g_val_t ibmpower_metric_handler ( int metric_index )
+ {
+    g_val_t val;
+ 
+ /* The metric_index corresponds to the order in which
+    the metrics appear in the metric_info array
+ */
+    switch (metric_index)
+    {
+       case 0:  return( capped_func() );
+       case 1:  return( cpu_entitlement_func() );
+       case 2:  return( cpu_in_lpar_func() );
+       case 3:  return( cpu_in_machine_func() );
+       case 4:  return( cpu_in_pool_func() );
+       case 5:  return( cpu_in_syspool_func() );
+       case 6:  return( cpu_pool_id_func() );
+       case 7:  return( cpu_pool_idle_func() );
+       case 8:  return( cpu_used_func() );
+       case 9:  return( disk_iops_func() );
+       case 10: return( disk_read_func() );
+       case 11: return( disk_write_func() );
+       case 12: return( fwversion_func() );
+       case 13: return( kernel64bit_func() );
+       case 14: return( lpar_func() );
+       case 15: return( lpar_name_func() );
+       case 16: return( lpar_num_func() );
+       case 17: return( model_name_func() );
+       case 18: return( oslevel_func() );
+       case 19: return( serial_num_func() );
+       case 20: return( smt_func() );
+       case 21: return( splpar_func() );
+       case 22: return( weight_func() );
+       default: val.uint32 = 0; /* default fallback */
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ static Ganglia_25metric ibmpower_metric_info[] = 
+ {
+    {0, "capped",           180, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Is this SPLPAR running in capped mode?"},
+    {0, "cpu_entitlement",  180, GANGLIA_VALUE_FLOAT,        "CPUs", "both", "%.2f", UDP_HEADER_SIZE+8,  "Capacity entitlement in units of physical cores"},
+    {0, "cpu_in_lpar",      180, GANGLIA_VALUE_UNSIGNED_INT, "CPUs", "both", "%d",   UDP_HEADER_SIZE+8,  "Number of CPUs the OS sees in the system"},
+    {0, "cpu_in_machine",  1200, GANGLIA_VALUE_UNSIGNED_INT, "CPUs", "both", "%d",   UDP_HEADER_SIZE+8,  "Total number of physical cores in the whole system"},
+    {0, "cpu_in_pool",      180, GANGLIA_VALUE_UNSIGNED_INT, "CPUs", "both", "%d",   UDP_HEADER_SIZE+8,  "Number of physical cores in the shared processor pool"},
+    {0, "cpu_in_syspool",   180, GANGLIA_VALUE_UNSIGNED_INT, "CPUs", "both", "%d",   UDP_HEADER_SIZE+8,  "Number of physical cores in the global shared processor pool"},
+    {0, "cpu_pool_id",      180, GANGLIA_VALUE_UNSIGNED_INT, "",     "both", "%d",   UDP_HEADER_SIZE+8,  "Shared processor pool ID of this LPAR"},
+    {0, "cpu_pool_idle",     15, GANGLIA_VALUE_FLOAT,        "CPUs", "both", "%.4f", UDP_HEADER_SIZE+8,  "Number of idle cores in the shared processor pool"},
+    {0, "cpu_used",          15, GANGLIA_VALUE_FLOAT,        "CPUs", "both", "%.4f", UDP_HEADER_SIZE+8,  "Number of physical cores used"},
+    {0, "disk_iops",        180, GANGLIA_VALUE_DOUBLE,     "IO/sec", "both", "%.3f", UDP_HEADER_SIZE+16, "Total number of I/O operations per second"},
+    {0, "disk_read",        180, GANGLIA_VALUE_DOUBLE,  "bytes/sec", "both", "%.2f", UDP_HEADER_SIZE+16, "Total number of bytes read I/O of the system"},
+    {0, "disk_write",       180, GANGLIA_VALUE_DOUBLE,  "bytes/sec", "both", "%.2f", UDP_HEADER_SIZE+16, "Total number of bytes write I/O of the system"},
+    {0, "fwversion",       1200, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Firmware Version"},
+    {0, "kernel64bit",     1200, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Is the kernel running in 64-bit mode?"},
+    {0, "lpar",            1200, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Is the system an LPAR or not?"},
+    {0, "lpar_name",        180, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Name of the LPAR as defined on the HMC"},
+    {0, "lpar_num",        1200, GANGLIA_VALUE_UNSIGNED_INT, "",     "both", "%d",   UDP_HEADER_SIZE+8,  "Partition ID of the LPAR as defined on the HMC"},
+    {0, "model_name",      1200, GANGLIA_VALUE_STRING,       "",     "both", "%d",   UDP_HEADER_SIZE+32, "Machine Model Name"},
+    {0, "oslevel",          180, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Exact AIX version string"},
+    {0, "serial_num",      1200, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Serial number of the hardware system"},
+    {0, "smt",              180, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Is SMT enabled or not?"},
+    {0, "splpar",          1200, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Is this a shared processor LPAR or not?"},
+    {0, "weight",           180, GANGLIA_VALUE_UNSIGNED_INT, "",     "both", "%d",   UDP_HEADER_SIZE+8,  "Capacity weight of the LPAR"},
+    {0, NULL}
+ };
+ 
+ 
+ 
+ mmodule ibmpower_module =
+ {
+    STD_MMODULE_STUFF,
+    ibmpower_metric_init,
+    ibmpower_metric_cleanup,
+    ibmpower_metric_info,
+    ibmpower_metric_handler,
+ };
+ 
*** ./gmond/modules/ibmpower/mod_ibmpower-linux.c.ORIG	Mon Mar  5 21:51:18 2012
--- ./gmond/modules/ibmpower/mod_ibmpower-linux.c	Mon Mar  5 22:05:46 2012
***************
*** 0 ****
--- 1,1514 ----
+ /******************************************************************************
+  *
+  *  This module implements IBM POWER5- and POWER6-specific extensions like:
+  *    - SPLPAR
+  *    - SMT
+  *    - CPU Entitlement
+  *    - Capped/Uncapped
+  *    - etc.
+  *
+  *  The code has been tested with on the following systems (all PowerPC-based):
+  *    - SLES 9, SLES 10 and SLES 11
+  *    - RHEL 4 AS and RHEL 5 AS
+  *    - openSUSE 10.0, 10.1, 10.2, 10.3, 11.0 and 11.1
+  *    - Fedora Core 4, 5, 6, 7, 8. 9, 10, 11 and 12
+  *
+  *  Written by Michael Perzl (michael@perzl.org)
+  *
+  *  Version 0.4, Feb 09, 2012
+  *
+  *  As long as I have not figured out how to obtain the number of cores
+  *  contained in the global shared processor pool this will not be called
+  *  version 1.x.
+  *
+  *  Version 0.4:  Feb 09, 2012
+  *                - added new metric cpu_pool_id
+  *                  (--> cpu_pool_id_func() )
+  *
+  *  Version 0.3:  Apr 27, 2010
+  *                - added sanity check for cpu_pool_idle_func()
+  *                - added new metric fwversion
+  *                  (--> fwversion_func() )
+  *                - fixed cpu_used_func() for systems which have
+  *                  /proc/ppc64/lparcfg and the purr stanza does exist but
+  *                  returns garbage because the CPU does not have a PURR
+  *                  register, e.g., true for PowerPC970 (--> JS20, JS21)
+  *
+  *  Version 0.2:  Feb 10, 2010
+  *                - improved cpu_used() function
+  *                - added IO ops/sec metric
+  *                  (--> disk_iops_func() )
+  *                - changed metric type from GANGLIA_VALUE_FLOAT to
+  *                  GANGLIA_VALUE_DOUBLE and changed unit to bytes/sec
+  *                  for disk_read_func() and disk_write_func()
+  *                - added model_name metric
+  *                  (--> model_name_func() )
+  *
+  *  Version 0.1:  Dec 11, 2008
+  *                - initial release
+  *
+  ******************************************************************************/
+ 
+ /*
+  * The ganglia metric "C" interface, required for building DSO modules.
+  */
+ 
+ #include <gm_metric.h>
+ 
+ 
+ #include <stdio.h>
+ #include <stdint.h>
+ #include <string.h>
+ #include <time.h>
+ 
+ #include "gm_file.h"
+ #include "libmetrics.h"
+ 
+ 
+ #ifndef BUFFSIZE
+ #define BUFFSIZE 8192
+ #endif
+ 
+ 
+ typedef struct
+ {
+    uint32_t last_read;
+    uint32_t thresh;
+    char *name;
+    char buffer[BUFFSIZE];
+ } my_timely_file;
+ 
+ 
+ static my_timely_file proc_cpuinfo = { 0, 1, "/proc/cpuinfo" };
+ static my_timely_file proc_diskstats = { 0, 1, "/proc/diskstats" };
+ static my_timely_file proc_stat = { 0, 1, "/proc/stat" };
+ static my_timely_file proc_ppc64_lparcfg = { 0, 1, "/proc/ppc64/lparcfg" };
+ 
+ static time_t boottime = 0;
+ 
+ static int purrUsable = FALSE;
+ 
+ 
+ 
+ static char *
+ my_update_file( my_timely_file *tf )
+ {
+    int now, rval;
+ 
+ 
+    now = time( NULL );
+    if (now - tf->last_read > tf->thresh)
+    {
+       rval = slurpfile( tf->name, tf->buffer, BUFFSIZE );
+       if (rval == SYNAPSE_FAILURE)
+       {
+          err_msg( "my_update_file() got an error from slurpfile() reading %s",
+ 	          tf->name );
+          return( (char *) SYNAPSE_FAILURE );
+       }
+       else
+          tf->last_read = now;
+    }
+ 
+    return( tf->buffer );
+ }
+ 
+ 
+ 
+ static time_t
+ boottime_func_CALLED_ONCE( void )
+ {
+    char   *p;
+    time_t  boottime;
+ 
+ 
+    p = my_update_file( &proc_stat ); 
+ 
+    p = strstr( p, "btime" );
+    if (p)
+    { 
+       p = skip_token( p );
+       boottime = strtod( p, (char **) NULL );
+    }
+    else
+       boottime = 0;
+ 
+    return( boottime );
+ }
+ 
+ 
+ 
+ g_val_t
+ model_name_func( void );
+ 
+ static void
+ CheckPURRusability( void )
+ {
+    g_val_t val;
+ 
+ 
+    purrUsable = TRUE;
+ 
+    val = model_name_func();
+ 
+    if ((! strncmp( val.str, "IBM,8842-21X", 12 )) ||
+        (! strncmp( val.str, "IBM,8842-41X", 12 )) ||
+        (! strncmp( val.str, "IBM,8844-31",  11 )) ||
+        (! strncmp( val.str, "IBM,8844-41",  11 )) ||
+        (! strncmp( val.str, "IBM,8844-51",  11 )))
+       purrUsable = FALSE;
+ }
+ 
+ 
+ 
+ g_val_t
+ capped_func( void )
+ {
+    g_val_t val;
+    char *p;
+    int i;
+ 
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg), "capped=" );
+ 
+    if (p)
+       i = strtol( p+7, (char **) NULL, 10 );
+    else
+       i = -1;
+ 
+    strcpy( val.str, i == -1 ? "No SPLPAR-capable system" : (i == 1 ? "yes" : "no" ) ); 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ cpu_entitlement_func( void )
+ {
+    g_val_t  val;
+    char    *p;
+    int      cpus;
+ 
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg), "partition_entitled_capacity=" );
+ 
+    if (p)
+       val.f = (float) strtol( p+28, (char **) NULL, 10 ) / 100.0;
+    else
+    {
+ /* find out the number of CPUs in the system/LPAR */
+ 
+       p = my_update_file( &proc_stat );
+ 
+ /* Skip initial "cpu" token and find first real cpu "cpu0" */
+       p = strstr( p+3, "cpu");
+ 
+       cpus = 1;
+       while ((p = strstr( p+3, "cpu" )))
+          cpus++;
+ 
+       val.f = cpus;
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ cpu_in_lpar_func( void )
+ {
+    g_val_t  val;
+    char    *p;
+    int      cpus;
+ 
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "partition_active_processors=" );
+ 
+    if (p)
+       val.int32 = strtol( p+28, (char **) NULL, 10 );
+    else
+    {
+ /* find out the number of CPUs in the system/LPAR */
+ 
+       p = my_update_file( &proc_stat );
+ 
+ /* Skip initial "cpu" token and find first real cpu "cpu0" */
+       p = strstr( p+3, "cpu");
+ 
+       cpus = 1;
+       while ((p = strstr( p+3, "cpu" )))
+          cpus++;
+ 
+       val.int32 = cpus;
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ cpu_in_machine_func( void )
+ {
+    g_val_t  val;
+    char    *p;
+    int      cpus;
+ 
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "system_potential_processors=" );
+ 
+    if (p)
+       val.int32 = strtol( p+28, (char **) NULL, 10 );
+    else
+    {
+ /* find out the number of CPUs in the system/LPAR */
+ 
+       p = my_update_file( &proc_stat );
+ 
+ /* Skip initial "cpu" token and find first real cpu "cpu0" */
+       p = strstr( p+3, "cpu");
+ 
+       cpus = 1;
+       while ((p = strstr( p+3, "cpu" )))
+          cpus++;
+ 
+       val.int32 = cpus;
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ cpu_in_pool_func( void )
+ {
+    g_val_t  val;
+    char    *p;
+    int      cpus;
+ 
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "pool_num_procs=" );
+ 
+    if (p)
+       val.int32 = strtol( p+15, (char **) NULL, 10 );
+    else
+    {
+ /* find out the number of CPUs in the system/LPAR */
+ 
+       p = my_update_file( &proc_stat );
+ 
+ /* Skip initial "cpu" token and find first real cpu "cpu0" */
+       p = strstr( p+3, "cpu");
+ 
+       cpus = 1;
+       while ((p = strstr( p+3, "cpu" )))
+          cpus++;
+ 
+       val.int32 = cpus;
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ cpu_in_syspool_func( void )
+ {
+    g_val_t  val;
+    char    *p;
+    int      cpus;
+ 
+ 
+ /* this is still not implemented for multiple shared processor pools */
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "pool_num_procs=" );
+ 
+    if (p)
+       val.int32 = strtol( p+15, (char **) NULL, 10 );
+    else
+    {
+ /* find out the number of CPUs in the system/LPAR */
+ 
+       p = my_update_file( &proc_stat );
+ 
+ /* Skip initial "cpu" token and find first real cpu "cpu0" */
+       p = strstr( p+3, "cpu");
+ 
+       cpus = 1;
+       while ((p = strstr( p+3, "cpu" )))
+          cpus++;
+ 
+       val.int32 = cpus;
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ cpu_pool_id_func( void )
+ {
+    g_val_t val;
+    int pool_id;
+    char *p;
+ 
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "pool=" );
+ 
+    if (p)
+       pool_id = strtol( p+5, (char **) NULL, 10 );
+    else
+       pool_id = -1;
+ 
+    val.int32 = pool_id;
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ #define MAX_CPU_POOL_IDLE (256.0)
+ 
+ g_val_t
+ cpu_pool_idle_func( void )
+ {
+    g_val_t val;
+    static long long pool_idle_saved = 0LL;
+    long long pool_idle, pool_idle_diff, timebase;
+    static double last_time = 0.0;
+    static float last_val  = 0.0;
+    double now, delta_t;
+    struct timeval timeValue;
+    struct timezone timeZone;
+    char *p;
+ 
+ 
+    gettimeofday( &timeValue, &timeZone );
+ 
+    now = (double) (timeValue.tv_sec - boottime) + (timeValue.tv_usec / 1000000.0);
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "pool_idle_time=" );
+ 
+    if (p)
+    {
+       delta_t = now - last_time;
+ 
+       pool_idle = strtoll( p+15, (char **) NULL, 10 );
+ 
+       p = strstr( my_update_file( &proc_cpuinfo ), "timebase" );
+ 
+       if ((delta_t > 0.0) && (p))
+       {
+          p = strchr( p, ':' );
+          p = skip_whitespace( p+1 );
+ 
+          timebase = strtoll( p, (char **) NULL, 10 );
+ 
+          pool_idle_diff = pool_idle - pool_idle_saved;
+ 
+          if ((timebase > 0LL) && (pool_idle_diff >= 0LL))
+             val.f = (double) (pool_idle_diff) / (double) timebase / delta_t;
+          else
+             val.f = last_val;
+       }
+       else
+          val.f = 0.0;
+ 
+       pool_idle_saved = pool_idle;
+    }
+    else
+       val.f = 0.0;
+ 
+ /* prevent against huge value when suddenly performance data collection */
+ /* is enabled or disabled for this LPAR */
+    if (val.f > MAX_CPU_POOL_IDLE)
+       val.f = 0.0;
+ 
+    last_time = now;
+    last_val = val.f;
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ cpu_used_func( void )
+ {
+    g_val_t val;
+    static long long purr_saved = 0LL;
+    long long purr, purr_diff, timebase;
+    static double last_time = 0.0;
+    static double last_system_check_time = 0.0;
+    static float last_val = 0.0;
+    double now, delta_t;
+    struct timeval timeValue;
+    struct timezone timeZone;
+    char *p;
+    int cpus;
+ 
+ 
+    gettimeofday( &timeValue, &timeZone );
+ 
+    now = (double) (timeValue.tv_sec - boottime) + (timeValue.tv_usec / 1000000.0);
+ 
+ /* check every 180 seconds if we are still on the same system --> LPAR Mobility */
+    if (now - last_system_check_time >= 180.0)
+    {
+       CheckPURRusability();
+       last_system_check_time = now;
+    }
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "purr=" );
+ 
+    if (p && purrUsable)
+    {
+       delta_t = now - last_time;
+ 
+       purr = strtoll( p+5, (char **) NULL, 10 );
+ 
+       p = strstr( my_update_file( &proc_cpuinfo ), "timebase" );
+ 
+       if ((delta_t > 0.0) && (p))
+       {
+          p = strchr( p, ':' );
+          p = skip_whitespace( p+1 );
+ 
+          timebase = strtoll( p, (char **) NULL, 10 );
+ 
+          purr_diff = purr - purr_saved;
+ 
+          if ((timebase > 0LL) && (purr_diff >= 0LL))
+             val.f = (double) (purr_diff) / (double) timebase / delta_t;
+          else
+             val.f = last_val;
+       }
+       else
+          val.f = 0.0;
+ 
+       purr_saved = purr;
+    }
+    else /* dedicated LPAR/standalone system so calculate cpu_used with cpu_idle_func() */
+    {
+ /* find out number of CPUs in the system/LPAR via /proc/ppc64/lparcfg */
+ /* --> partition_active_processors should always exist */
+       p = strstr( my_update_file( &proc_ppc64_lparcfg ), "partition_active_processors=" );
+ 
+       if (p)
+       {
+          cpus = strtol( p+28, (char **) NULL, 10 );
+ 
+          val = cpu_idle_func();
+          val.f = (float) cpus * (100.0 - val.f) / 100.0;
+       }
+       else
+          val.f = 0.0;
+    }
+ 
+ /* sanity check to prevent against accidental huge value */
+    if (val.f >= 256.0)
+       val.f = 0.0;
+ 
+    last_time = now;
+    last_val = val.f;
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ struct dsk_stat {
+         char          dk_name[32];
+         int           dk_major;
+         int           dk_minor;
+         long          dk_noinfo;
+         unsigned long dk_reads;
+         unsigned long dk_rmerge;
+         unsigned long dk_rmsec;
+         unsigned long dk_rkb;
+         unsigned long dk_writes;
+         unsigned long dk_wmerge;
+         unsigned long dk_wmsec;
+         unsigned long dk_wkb;
+         unsigned long dk_xfers;
+         unsigned long dk_bsize;
+         unsigned long dk_time;
+         unsigned long dk_inflight;
+         unsigned long dk_11;
+         unsigned long dk_partition;
+         unsigned long dk_blocks; /* in /proc/partitions only */
+         unsigned long dk_use;
+         unsigned long dk_aveq;
+ };
+ 
+ 
+ 
+ static void
+ get_diskstats_iops( double *iops )
+ {
+    char *p, *q;
+    char buf[1024];
+    int  ret;
+    long long total_iops, diff;
+    static long long saved_iops = 0LL;
+    struct dsk_stat dk;
+    static double last_time = 0.0;
+    static double last_val = 0.0;
+    double now, delta_t;
+    struct timeval timeValue;
+    struct timezone timeZone;
+ 
+ 
+    gettimeofday( &timeValue, &timeZone );
+ 
+    now = (double) (timeValue.tv_sec - boottime) + (timeValue.tv_usec / 1000000.0);
+ 
+ 
+    p = my_update_file( &proc_diskstats );
+ 
+    if (p)
+    {
+       total_iops = 0LL;
+ 
+       while ((q = strchr( p, '\n' )))
+       {
+          /* zero the data ready for reading */
+          dk.dk_reads = dk.dk_writes = 0;
+ 
+          strncpy( buf, p, q-p );
+          buf[q-p] = '\0';
+ 
+          ret = sscanf( buf, "%d %d %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+                        &dk.dk_major,
+                        &dk.dk_minor,
+                        &dk.dk_name[0],
+                        &dk.dk_reads,
+                        &dk.dk_rmerge,
+                        &dk.dk_rkb,
+                        &dk.dk_rmsec,
+                        &dk.dk_writes,
+                        &dk.dk_wmerge,
+                        &dk.dk_wkb,
+                        &dk.dk_wmsec,
+                        &dk.dk_inflight,
+                        &dk.dk_time,
+                        &dk.dk_11 );
+ 
+          p = q+1;
+ 
+          if (ret == 7)  /* skip partitions of a disk */
+            continue;
+ 
+          if (strncmp(dk.dk_name, "dm-", 3) == 0)
+            continue;
+ 
+          if (strncmp(dk.dk_name, "md", 2) == 0)
+            continue;
+ 
+ #ifdef MPERZL_DEBUG
+ fprintf(stderr, "dk_name = %5s, dk_reads = %10ld, dk_writes = %10ld\n", dk.dk_name, dk.dk_reads, dk.dk_writes);
+ #endif
+ 
+          total_iops += dk.dk_reads + dk.dk_writes;
+       }
+ 
+ #ifdef MPERZL_DEBUG
+ fprintf(stderr, "total_iops = %" PRIi64 "\n", total_iops);
+ fprintf(stderr, "saved_iops = %" PRIi64 "\n", saved_iops);
+ #endif
+ 
+       delta_t = now - last_time;
+ 
+       if (delta_t > 0)
+       {
+          diff = total_iops - saved_iops;
+ 
+          if (diff > 0LL)
+             *iops = diff / delta_t;
+          else
+             *iops = 0.0;
+       }
+       else
+          *iops = 0.0;
+ 
+       saved_iops = total_iops;
+    }
+    else
+    {
+       *iops = 0.0;
+    }
+ 
+    last_time = now;
+    last_val = *iops;
+ }
+ 
+ 
+ 
+ static void
+ get_diskstats_read( double *read )
+ {
+    char *p, *q;
+    char buf[1024];
+    int  ret;
+    long long total_read, diff;
+    static long long saved_read = 0LL;
+    struct dsk_stat dk;
+    static double last_time = 0.0;
+    static double last_val = 0.0;
+    double now, delta_t;
+    struct timeval timeValue;
+    struct timezone timeZone;
+ 
+ 
+    gettimeofday( &timeValue, &timeZone );
+ 
+    now = (double) (timeValue.tv_sec - boottime) + (timeValue.tv_usec / 1000000.0);
+ 
+ 
+    p = my_update_file( &proc_diskstats );
+ 
+    if (p)
+    {
+       total_read  = 0;
+ 
+       while ((q = strchr( p, '\n' )))
+       {
+          /* zero the data ready for reading */
+          dk.dk_rkb = 0;
+ 
+          strncpy( buf, p, q-p );
+          buf[q-p] = '\0';
+ 
+          ret = sscanf( buf, "%d %d %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+                        &dk.dk_major,
+                        &dk.dk_minor,
+                        &dk.dk_name[0],
+                        &dk.dk_reads,
+                        &dk.dk_rmerge,
+                        &dk.dk_rkb,
+                        &dk.dk_rmsec,
+                        &dk.dk_writes,
+                        &dk.dk_wmerge,
+                        &dk.dk_wkb,
+                        &dk.dk_wmsec,
+                        &dk.dk_inflight,
+                        &dk.dk_time,
+                        &dk.dk_11 );
+ 
+          p = q+1;
+ 
+          if (ret == 7)  /* skip partitions of a disk */
+             continue;
+ 
+          if (strncmp(dk.dk_name, "md", 2) == 0)
+             continue;
+ 
+          if (strncmp(dk.dk_name, "dm-", 3) == 0)
+             continue;
+ 
+ #ifdef MPERZL_DEBUG
+ printf("dk_rkb = %ld   dk_wkb = %ld\n", dk.dk_rkb, dk.dk_wkb);
+ #endif
+ 
+          dk.dk_rkb /= 2; /* sectors = 512 bytes */
+ 
+          total_read  += dk.dk_rkb;
+       }
+ 
+ #ifdef MPERZL_DEBUG
+ printf("total_read  = %" PRIi64 "\n", total_read);
+ printf("saved_read  = %" PRIi64 "\n", saved_read);
+ #endif
+ 
+       delta_t = now - last_time;
+ 
+       if (delta_t > 0)
+       {
+          diff = total_read - saved_read;
+ 
+          if (diff > 0LL)
+             *read = diff / delta_t;
+          else
+             *read = 0.0;
+       }
+       else
+          *read = 0.0;
+ 
+       saved_read  = total_read;
+    }
+    else
+    {
+       *read  = 0.0;
+    }
+ 
+    last_time = now;
+    last_val = *read;
+ }
+ 
+ 
+ 
+ static void
+ get_diskstats_write( double *write )
+ {
+    char *p, *q;
+    char buf[1024];
+    int  ret;
+    long long total_write, diff;
+    static long long saved_write = 0LL;
+    struct dsk_stat dk;
+    static double last_time = 0.0;
+    static double last_val = 0.0;
+    double now, delta_t;
+    struct timeval timeValue;
+    struct timezone timeZone;
+ 
+ 
+    gettimeofday( &timeValue, &timeZone );
+ 
+    now = (double) (timeValue.tv_sec - boottime) + (timeValue.tv_usec / 1000000.0);
+ 
+ 
+    p = my_update_file( &proc_diskstats );
+ 
+    if (p)
+    {
+       total_write = 0;
+ 
+       while ((q = strchr( p, '\n' )))
+       {
+          /* zero the data ready for reading */
+          dk.dk_wkb = 0;
+ 
+          strncpy( buf, p, q-p );
+          buf[q-p] = '\0';
+ 
+          ret = sscanf( buf, "%d %d %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+                        &dk.dk_major,
+                        &dk.dk_minor,
+                        &dk.dk_name[0],
+                        &dk.dk_reads,
+                        &dk.dk_rmerge,
+                        &dk.dk_rkb,
+                        &dk.dk_rmsec,
+                        &dk.dk_writes,
+                        &dk.dk_wmerge,
+                        &dk.dk_wkb,
+                        &dk.dk_wmsec,
+                        &dk.dk_inflight,
+                        &dk.dk_time,
+                        &dk.dk_11 );
+ 
+          p = q+1;
+ 
+          if (ret == 7)  /* skip partitions of a disk */
+             continue;
+ 
+          if (strncmp(dk.dk_name, "md", 2) == 0)
+             continue;
+ 
+          if (strncmp(dk.dk_name, "dm-", 3) == 0)
+             continue;
+ 
+ #ifdef MPERZL_DEBUG
+ printf("dk_rkb = %ld   dk_wkb = %ld\n", dk.dk_rkb, dk.dk_wkb);
+ #endif
+ 
+          dk.dk_wkb /= 2; /* sectors = 512 bytes */
+ 
+          total_write += dk.dk_wkb;
+       }
+ 
+ #ifdef MPERZL_DEBUG
+ printf("total_write = %" PRIi64 "\n", total_write);
+ printf("saved_write = %" PRIi64 "\n", saved_write);
+ #endif
+ 
+       delta_t = now - last_time;
+ 
+       if (delta_t > 0)
+       {
+          diff = total_write - saved_write;
+ 
+          if (diff > 0LL)
+             *write = diff / delta_t;
+          else
+             *write = 0.0;
+       }
+       else
+          *write = 0.0;
+ 
+       saved_write = total_write;
+    }
+    else
+    {
+       *write = 0.0;
+    }
+ 
+    last_time = now;
+    last_val = *write;
+ }
+ 
+ 
+ 
+ g_val_t
+ disk_iops_func( void )
+ {
+    g_val_t val;
+    double disk_iops;
+ 
+  
+    get_diskstats_iops( &disk_iops );
+ 
+    val.d = disk_iops;
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ disk_read_func( void )
+ {
+    g_val_t val;
+    double disk_read;
+ 
+  
+    get_diskstats_read( &disk_read );
+ 
+    val.d = disk_read * 1024.0;
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ disk_write_func( void )
+ {
+    g_val_t val;
+    double disk_write;
+ 
+  
+    get_diskstats_write( &disk_write );
+ 
+    val.d = disk_write * 1024.0;
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ fwversion_func( void )
+ {
+    FILE    *f;
+    g_val_t  val;
+ 
+ 
+    strcpy( val.str, "Firmware version not detected!" );
+ 
+    f = fopen( "/proc/device-tree/openprom/ibm,fw-vernum_encoded", "r" );
+ 
+    if (f)
+    {
+       if (fread( val.str, 1, MAX_G_STRING_SIZE, f ) > 0)
+       {
+          val.str[MAX_G_STRING_SIZE - 1] = '\0';
+          val.str[strlen( val.str ) - 1] = '\0';  /* truncate \n */
+       }
+ 
+       fclose( f );
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ static int LinuxVersion = 0;    /* 1 = /etc/SuSE-release */
+                                 /* 2 = /etc/redhat-release */
+                                 /* 3 = /etc/debian_version */
+                                 /* 4 = unknown, try /etc/\*ease */
+ 
+ 
+ 
+ /* find 64bit kernel or not */
+ g_val_t
+ kernel64bit_func( void )
+ {
+    g_val_t  val;
+    FILE    *f;
+    char     buf[128];
+    int      kernel64bit=0, i;
+ 
+ 
+    if ((LinuxVersion == 1) || (LinuxVersion == 2))
+       f = popen( "uname -i 2>/dev/null", "r" );
+    else
+       if (LinuxVersion == 3)  /* Debian has no "uname -i" */
+          f = popen( "uname -m 2>/dev/null", "r" );
+       else
+          f = popen( "uname -r 2>/dev/null", "r" );
+ 
+    if (f == NULL)
+       strcpy( val.str, "popen() of 'uname -[i,m,r]' failed" );
+    else
+    {
+       if (fread( buf, 1, 128, f ) > 0)
+       {
+          buf[127] = '\0';
+ 
+          for (i = 0;  buf[i] != '\0';  i++)
+          {
+             if (buf[i] == '6' && buf[i+1] == '4')
+             {
+                kernel64bit++;
+                break;
+             }
+          }
+ 
+          strcpy( val.str, kernel64bit ? "yes" : "no" );
+       }
+       else
+          strcpy( val.str, "popen() of 'uname -[i,m,r]' failed" );
+ 
+       pclose( f );
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ lpar_func( void )
+ {
+    g_val_t val;
+    char *p;
+    int capped, shared_processor_mode, partition_id;
+    long DisWheRotPer;
+    long long purr;
+ 
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "shared_processor_mode=" );
+    if (p)
+       shared_processor_mode = strtol( p+22, (char **) NULL, 10 );
+    else
+       shared_processor_mode = -1;
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "capped=" );
+    if (p)
+       capped = strtol( p+7, (char **) NULL, 10 );
+    else
+       capped = -1;
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "partition_id=" );
+    if (p)
+       partition_id = strtol( p+13, (char **) NULL, 10 );
+    else
+       partition_id = -1;
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "DisWheRotPer=" );
+    if (p)
+       DisWheRotPer = strtol( p+13, (char **) NULL, 10 );
+    else
+       DisWheRotPer = -1;
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "purr=" );
+    if (p)
+       purr = strtoll( p+5, (char **) NULL, 10 );
+    else
+       purr = -1;
+ 
+ 
+    if (shared_processor_mode > 0 ||
+        capped >= 0 ||
+        partition_id > 0 ||
+        DisWheRotPer > 0 ||
+        purr > 0)
+       strcpy( val.str, "yes" );
+    else
+       strcpy( val.str, "no" );
+ 
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ lpar_name_func( void )
+ {
+    g_val_t val;
+    FILE *f;
+    char buf[128];
+ 
+ 
+    f = fopen( "/proc/device-tree/ibm,partition-name", "r" );
+ 
+    if (f == NULL)
+    {
+       strcpy( val.str, "No LPAR system" );
+    }
+    else
+    {
+       if (fgets( buf, 128, f ) )
+       {
+          if (strlen( buf ) > MAX_G_STRING_SIZE - 1)
+             buf[MAX_G_STRING_SIZE - 1] = '\0';
+ 
+          strcpy( val.str, buf );
+       }
+       else
+          strcpy( val.str, "Can't find out LPAR name!" );
+ 
+       fclose( f );
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ lpar_num_func( void )
+ {
+    g_val_t val;
+    char *p;
+ 
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "partition_id=" );
+ 
+    if (p)
+       val.int32 = strtol( p+13, (char **) NULL, 10 );
+    else
+       val.int32 = 0;
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ model_name_func( void )
+ {
+    g_val_t val;
+    char *p;
+    int len;
+ 
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "system_type=" );
+ 
+    if (p)
+    {
+       len = strchr( p+12, '\n' ) - (p+12);
+       if (len > MAX_G_STRING_SIZE - 1)
+          len = MAX_G_STRING_SIZE - 1;
+       strncpy( val.str, p+12, len );
+       val.str[len] = '\0';
+    }
+    else
+       strcpy( val.str, "Can't find out model name" );
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ /* find OS version just once */
+ static g_val_t
+ oslevel_func_CALLED_ONCE( void )
+ {
+    g_val_t  val;
+    FILE    *f;
+    char     buf[256], *p, *q;
+    int      i;
+ 
+ 
+    f = fopen( "/etc/SuSE-release", "r" );
+    if (f) LinuxVersion=1;
+    if (! LinuxVersion)
+    {
+       f = fopen( "/etc/redhat-release", "r" );
+       if (f) LinuxVersion=2;
+    }
+    if (! LinuxVersion)
+    {
+       f = fopen( "/etc/debian_version", "r" );
+       if (f) LinuxVersion=3;
+    }
+    if (! LinuxVersion)
+    {
+       f = popen( "cat /etc/*ease 2>/dev/null", "r" );
+       if (f) LinuxVersion=4;
+    }
+    if (f == NULL)
+       strcpy( val.str, "No known Linux release found" );
+    else
+    {
+       if (fread( buf, 1, 256, f) > 0)
+       {
+          if (LinuxVersion == 1)
+          {
+             if ((! strncmp( buf, "SUSE LINUX Enterprise Server", 28 )) ||
+                 (! strncmp( buf, "SUSE Linux Enterprise Server", 28 )))
+             {
+                strcpy( val.str, "SLES " );
+ 
+                p = strchr( buf, '\n' );
+                if (p) p = strchr( p+1, '=' );
+                if (p) p = skip_whitespace( p+1 );
+                if (p) q = strchr( p, '\n' );
+ 
+                if (p && q) strncat( val.str, p, q-p );
+                strcat( val.str, " SP " );
+ 
+                if (q) p = strchr( q+1, '=' );
+                if (p) p = skip_whitespace( p+1 );
+                if (p) q = strchr( p, '\n' );
+ 
+                if (p && q) strncat( val.str, p, q-p );
+             }
+             else
+             {
+                p = strchr( buf, '\n' );
+ 
+                i = p-buf;
+                if ((i < 0) || (i >= MAX_G_STRING_SIZE))
+                   i = MAX_G_STRING_SIZE - 1;
+ 
+                strncpy( val.str, buf, i );
+                val.str[i] = '\0';
+             }
+          }
+          else if (LinuxVersion == 2)
+          {
+             if (! strncmp( buf, "Red Hat Enterprise Linux AS release", 35 ))
+             {
+                strcpy( val.str, "RHEL AS " );
+ 
+                p = skip_whitespace( buf+35 );
+                if (p) q = strchr( p, ' ' );
+ 
+                if (p && q) strncat( val.str, p, q-p );
+ 
+                if (q) p = strstr( q+1, "Update " );
+ 
+                if (p)
+                {
+                   strcat( val.str, " Update " );
+ 
+                   p = skip_whitespace( p+7 );
+                   if (p) q = strchr( p, ')' );
+ 
+                   if (p && q) strncat( val.str, p, q-p );
+                }
+             }
+             else
+             if (! strncmp( buf, "Red Hat Enterprise Linux Server release", 39 ))
+             {
+                strcpy( val.str, "RHEL AS " );
+ 
+                p = skip_whitespace( buf+39 );
+                if (p) q = strchr( p, ' ' );
+ 
+                if (p && q) strncat( val.str, p, q-p );
+ 
+                if (q) p = strstr( q+1, "Update " );
+ 
+                if (p)
+                {
+                   strcat( val.str, " Update " );
+ 
+                   p = skip_whitespace( p+7 );
+                   if (p) q = strchr( p, ')' );
+ 
+                   if (p && q) strncat( val.str, p, q-p );
+                }
+             }
+             else
+             {
+                p = strchr( buf, '\n' );
+ 
+                i = p-buf;
+                if ((i < 0) || (i >= MAX_G_STRING_SIZE))
+                   i = MAX_G_STRING_SIZE - 1;
+ 
+                strncpy( val.str, buf, i );
+                val.str[i] = '\0';
+             }
+          }
+          else if ((LinuxVersion == 3) || (LinuxVersion == 4))
+          {
+             buf[MAX_G_STRING_SIZE - 1] = '\0';
+             for (i = 0;  buf[i] != '\0';  i++)
+                if ((buf[i] == '"') || (buf[i] < ' ') || (buf[i] > 126))
+                   buf[i] = ' ';
+ 
+             strncpy( val.str, buf, MAX_G_STRING_SIZE );
+             val.str[MAX_G_STRING_SIZE - 1] = '\0';
+          }
+       }
+       else
+          strcpy( val.str, "No known Linux release found" );
+ 
+       fclose( f );
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ oslevel_func( void )
+ {
+    static g_val_t val;
+    static int firstTime = 1;
+ 
+ 
+    if (firstTime)
+    {
+       val = oslevel_func_CALLED_ONCE();
+       firstTime = 0;
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ serial_num_func( void )
+ {
+    g_val_t val;
+    char *p;
+    int len;
+ 
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg), "serial_number=" );
+ 
+    if (p)
+    {
+       len = strchr( p+14, '\n' ) - (p+14);
+       if (len > MAX_G_STRING_SIZE - 1)
+          len = MAX_G_STRING_SIZE - 1;
+       strncpy( val.str, p+14, len );
+       val.str[len] = '\0';
+    }
+    else
+       strcpy( val.str, "Can't find out serial number" );
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ smt_func( void )
+ {
+    g_val_t val;
+    char *p;
+    int i;
+ 
+ 
+    p = my_update_file( &proc_stat );
+ 
+ /* Skip initial "cpu" token and find first real cpu "cpu0" */
+    p = strstr( p+3, "cpu");
+ 
+    i = 1;
+    while ((p = strstr( p+3, "cpu" )))
+       i++;
+       
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "partition_active_processors=" );
+ 
+    if (p)
+       strcpy( val.str, i > strtol( p+28, (char **) NULL, 10 ) ? "yes" : "no" );
+    else
+       strcpy( val.str, "No SMT-capable system" );
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ splpar_func( void )
+ {
+    g_val_t val;
+    char *p;
+ 
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "shared_processor_mode=" );
+ 
+    if (p)
+       strcpy( val.str, strtol( p+22, (char **) NULL, 10 ) == 1 ? "yes" : "no" );
+    else
+       strcpy( val.str, "No SPLPAR-capable system" );
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ g_val_t
+ weight_func( void )
+ {
+    g_val_t val;
+    char *p;
+ 
+ 
+    p = strstr( my_update_file( &proc_ppc64_lparcfg ), "unallocated_capacity_weight=" );
+ 
+    if (p)
+    {
+       p = strstr( p+29, "capacity_weight=" );
+ 
+       if (p)
+          val.int32 = strtol( p+16, (char **) NULL, 10 );
+       else
+          val.int32 = -1;
+    }
+    else
+       val.int32 = -1;
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ /*
+  * Declare ourselves so the configuration routines can find and know us.
+  * We'll fill it in at the end of the module.
+  */
+ extern mmodule ibmpower_module;
+ 
+ 
+ static int
+ ibmpower_metric_init ( apr_pool_t *p )
+ {
+    int i;
+    g_val_t val;
+ 
+ 
+    for (i = 0;  ibmpower_module.metrics_info[i].name != NULL;  i++)
+    {
+       /* Initialize the metadata storage for each of the metrics and then
+        *  store one or more key/value pairs.  The define MGROUPS defines
+        *  the key for the grouping attribute. */
+       MMETRIC_INIT_METADATA( &(ibmpower_module.metrics_info[i]), p );
+       MMETRIC_ADD_METADATA( &(ibmpower_module.metrics_info[i]), MGROUP, "ibmpower" );
+    }
+ 
+ 
+ /* initialize the routines which require a time interval */
+ 
+    boottime = boottime_func_CALLED_ONCE();
+ 
+    CheckPURRusability();
+ 
+    val = cpu_pool_idle_func();
+    val = cpu_used_func();
+    val = disk_iops_func();
+    val = disk_read_func();
+    val = disk_write_func();
+ 
+ 
+ /* return SUCCESS */
+ 
+    return( 0 );
+ }
+ 
+ 
+ 
+ static void
+ ibmpower_metric_cleanup ( void )
+ {
+ }
+ 
+ 
+ 
+ static g_val_t
+ ibmpower_metric_handler ( int metric_index )
+ {
+    g_val_t val;
+ 
+ /* The metric_index corresponds to the order in which
+    the metrics appear in the metric_info array
+ */
+    switch (metric_index)
+    {
+       case 0:  return( capped_func() );
+       case 1:  return( cpu_entitlement_func() );
+       case 2:  return( cpu_in_lpar_func() );
+       case 3:  return( cpu_in_machine_func() );
+       case 4:  return( cpu_in_pool_func() );
+       case 5:  return( cpu_in_syspool_func() );
+       case 6:  return( cpu_pool_id_func() );
+       case 7:  return( cpu_pool_idle_func() );
+       case 8:  return( cpu_used_func() );
+       case 9:  return( disk_iops_func() );
+       case 10: return( disk_read_func() );
+       case 11: return( disk_write_func() );
+       case 12: return( fwversion_func() );
+       case 13: return( kernel64bit_func() );
+       case 14: return( lpar_func() );
+       case 15: return( lpar_name_func() );
+       case 16: return( lpar_num_func() );
+       case 17: return( model_name_func() );
+       case 18: return( oslevel_func() );
+       case 19: return( serial_num_func() );
+       case 20: return( smt_func() );
+       case 21: return( splpar_func() );
+       case 22: return( weight_func() );
+       default: val.uint32 = 0; /* default fallback */
+    }
+ 
+    return( val );
+ }
+ 
+ 
+ 
+ static Ganglia_25metric ibmpower_metric_info[] = 
+ {
+    {0, "capped",           180, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Is this SPLPAR running in capped mode?"},
+    {0, "cpu_entitlement",  180, GANGLIA_VALUE_FLOAT,        "CPUs", "both", "%.2f", UDP_HEADER_SIZE+8,  "Capacity entitlement in units of physical cores"},
+    {0, "cpu_in_lpar",      180, GANGLIA_VALUE_UNSIGNED_INT, "CPUs", "both", "%d",   UDP_HEADER_SIZE+8,  "Number of CPUs the OS sees in the system"},
+    {0, "cpu_in_machine",  1200, GANGLIA_VALUE_UNSIGNED_INT, "CPUs", "both", "%d",   UDP_HEADER_SIZE+8,  "Total number of physical cores in the whole system"},
+    {0, "cpu_in_pool",      180, GANGLIA_VALUE_UNSIGNED_INT, "CPUs", "both", "%d",   UDP_HEADER_SIZE+8,  "Number of physical cores in the shared processor pool"},
+    {0, "cpu_in_syspool",   180, GANGLIA_VALUE_UNSIGNED_INT, "CPUs", "both", "%d",   UDP_HEADER_SIZE+8,  "Number of physical cores in the global shared processor pool"},
+    {0, "cpu_pool_id",      180, GANGLIA_VALUE_UNSIGNED_INT, "",     "both", "%d",   UDP_HEADER_SIZE+8,  "Shared processor pool ID of this LPAR"},
+    {0, "cpu_pool_idle",     15, GANGLIA_VALUE_FLOAT,        "CPUs", "both", "%.4f", UDP_HEADER_SIZE+8,  "Number of idle cores in the shared processor pool"},
+    {0, "cpu_used",          15, GANGLIA_VALUE_FLOAT,        "CPUs", "both", "%.4f", UDP_HEADER_SIZE+8,  "Number of physical cores used"},
+    {0, "disk_iops",        180, GANGLIA_VALUE_DOUBLE,     "IO/sec", "both", "%.3f", UDP_HEADER_SIZE+16, "Total number of I/O operations per second"},
+    {0, "disk_read",        180, GANGLIA_VALUE_DOUBLE,  "bytes/sec", "both", "%.2f", UDP_HEADER_SIZE+16, "Total number of bytes read I/O of the system"},
+    {0, "disk_write",       180, GANGLIA_VALUE_DOUBLE,  "bytes/sec", "both", "%.2f", UDP_HEADER_SIZE+16, "Total number of bytes write I/O of the system"},
+    {0, "fwversion",       1200, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Firmware Version"},
+    {0, "kernel64bit",     1200, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Is the kernel running in 64-bit mode?"},
+    {0, "lpar",            1200, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Is the system an LPAR or not?"},
+    {0, "lpar_name",        180, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Name of the LPAR as defined on the HMC"},
+    {0, "lpar_num",        1200, GANGLIA_VALUE_UNSIGNED_INT, "",     "both", "%d",   UDP_HEADER_SIZE+8,  "Partition ID of the LPAR as defined on the HMC"},
+    {0, "model_name",      1200, GANGLIA_VALUE_STRING,       "",     "both", "%d",   UDP_HEADER_SIZE+32, "Machine Model Name"},
+    {0, "oslevel",          180, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Exact AIX version string"},
+    {0, "serial_num",      1200, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Serial number of the hardware system"},
+    {0, "smt",              180, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Is SMT enabled or not?"},
+    {0, "splpar",          1200, GANGLIA_VALUE_STRING,       "",     "both", "%s",   UDP_HEADER_SIZE+32, "Is this a shared processor LPAR or not?"},
+    {0, "weight",           180, GANGLIA_VALUE_UNSIGNED_INT, "",     "both", "%d",   UDP_HEADER_SIZE+8,  "Capacity weight of the LPAR"},
+    {0, NULL}
+ };
+ 
+ 
+ 
+ mmodule ibmpower_module =
+ {
+    STD_MMODULE_STUFF,
+    ibmpower_metric_init,
+    ibmpower_metric_cleanup,
+    ibmpower_metric_info,
+    ibmpower_metric_handler,
+ };
+ 
*** ./gmond/modules/Makefile.am.ORIG	Thu Sep  2 14:55:16 2010
--- ./gmond/modules/Makefile.am	Thu Sep  2 14:55:30 2010
***************
*** 6,16 ****
  STATUS_SUBDIR = status
  endif
  
! DIST_SUBDIRS = example cpu disk memory network system status python
  if STATIC_BUILD
! SUBDIRS = cpu disk memory network system $(STATUS_SUBDIR) $(PYTHON_SUBDIR)
  else
! SUBDIRS = example cpu disk memory network system $(STATUS_SUBDIR) $(PYTHON_SUBDIR)
  
  install: install-recursive
  	@rm -rf $(DESTDIR)$(pkglibdir)/*.a
--- 6,16 ----
  STATUS_SUBDIR = status
  endif
  
! DIST_SUBDIRS = ibmpower example cpu disk memory network system status python
  if STATIC_BUILD
! SUBDIRS = ibmpower cpu disk memory network system $(STATUS_SUBDIR) $(PYTHON_SUBDIR)
  else
! SUBDIRS = ibmpower example cpu disk memory network system $(STATUS_SUBDIR) $(PYTHON_SUBDIR)
  
  install: install-recursive
  	@rm -rf $(DESTDIR)$(pkglibdir)/*.a
*** ./gmond/Makefile.am.ORIG	Tue Apr  5 01:10:15 2011
--- ./gmond/Makefile.am	Mon Aug  8 23:12:56 2011
***************
*** 14,19 ****
--- 14,20 ----
           modules/memory/mod_mem.lo modules/network/mod_net.lo \
           modules/system/mod_proc.lo modules/system/mod_sys.lo \
           modules/cpu/mod_multicpu.lo \
+          modules/ibmpower/mod_ibmpower.lo \
           $(MOD_GSTATUS) @STATIC_USER_MODULES@
  GLDFLAGS = -static @EXPORT_SYMBOLS@
  if BUILD_PYTHON
*** ./configure.in.ORIG	Thu Sep  2 14:56:05 2010
--- ./configure.in	Thu Sep  2 14:57:11 2010
***************
*** 606,611 ****
--- 606,612 ----
  		      moduledir="$libdir/ganglia"
  		   fi
  		fi
+ 		ln -sf mod_ibmpower-linux.c gmond/modules/ibmpower/mod_ibmpower.c
  		;;
  *ia64-*hpux*)	CFLAGS="$CFLAGS -D_PSTAT64 -D_HPUX_SOURCE" 
  		LIBS="-lpthread $LIBS"
***************
*** 626,631 ****
--- 627,633 ----
  		AC_DEFINE(AIX, 1, AIX)
  		LIBS="-lm $LIBS"
  		EXPORT_SYMBOLS_DYNAMIC="-Wl,-bexpfull"
+ 		ln -sf mod_ibmpower-aix.c gmond/modules/ibmpower/mod_ibmpower.c
  		;;
  *hpux*)		CFLAGS="$CFLAGS -D_HPUX_SOURCE" 
  		LIBS="-lpthread $LIBS"
***************
*** 751,756 ****
--- 753,759 ----
            gstat/Makefile 
            gmond/modules/Makefile
            gmond/modules/example/Makefile
+           gmond/modules/ibmpower/Makefile
            gmond/modules/cpu/Makefile
            gmond/modules/disk/Makefile
            gmond/modules/memory/Makefile
