/* lmSensors.c
 *
 * Sections of this code were derived from the published API's of 
 * some Sun products.  Hence, portions of the code may be copyright
 * Sun Microsystems.
 *
 * Additional code provided by Mike Fisher and Thomas E. Lackley
 *
 * This component allows net-snmp to report sensor information.
 *
 * In order to use it, the ./configure invocation must include...
 *
 * --with-mib-modules="ucd-snmp/lmSensors"
 *
 * It uses one of three different methodologies.  Some platforms make
 * use of an lm_sensors driver to access the information on the
 * health monitoring hardware, such as the LM75 and LM78 chips.
 *
 * For further information see http://secure.netroedge.com/~lm78/
 *
 * The Solaris platform uses the other two methodologies.  Earlier
 * platforms such as the Enterprise 450 use kstat to report sensor
 * information.  Later platforms, such as the V880 use the picld
 * daemon to control system resources and report sensor information.
 * Picld is supported only on Solaris 2.8 and later.
 *
 * Both these methodologies are implemented in a "read only" manner.
 * You cannot use this code to change anything eg. fan speeds.
 *
 * The lmSensors component delivers the information documented in the
 * LM-SENSORS-MIB.  The information is divided up as follows:
 *
 * -temperatures (in thousandsths of a Celsius degree)
 * -fans (rpm's)
 * -voltages (in milliVolts)
 * -other (switches, LEDs and  i2c's (things that use the i2c bus))
 * NOTE: This version does not support gpio's.  Still on the learning curve.
 *
 * Because the MIB only allows output of the datatype Gauge32 this
 * limits the amount of meaningful information that can be delivered
 * from "other" sensors.  Hence, the code does a certain amount of
 * translating.  See the source for individual sensor types.
 *
 * If an "other" sensor delivers a value 99, it means that it
 * is delivering a "status" that the code does not account for.
 * If you discover one of these, please pass it on and I'll
 * put it in.
 *
 * It was recently discovered that the sensors code had not be following
 * the MIB for some sensors.  The MIB required reporting some items
 * in mV and mC.  These changes have been noted in the source.
 *
 * To see debugging messages, run the daemon as follows:
 * 
 * /usr/local/sbin/snmpd -f -L -Ducd-snmp/lmSensors
 * (change path to wherever you installed it)
 *
 * or using gdb:
 *
 * gdb snmpd
 * run -f -L -Ducd-snmp/lmSensors
 *
 * The component can record up to 256 instances of each type.
 *
 * The following should always be included first before anything else 
 */

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>

/*
 * minimal include directives 
 */

#include "util_funcs.h"
#include <time.h>

/*
 * Load required drivers and libraries.
 */

#ifdef solaris2
    #include <kstat.h>
    #ifdef HAVE_PICL_H 
        #include <picl.h> /* accesses the picld daemon */
    #endif 
/* the following should be sufficient for any Sun-based sensors */
    #include </usr/platform/sun4u/include/sys/envctrl.h>
#else
    #include <sensors/sensors.h>
    #define CONFIG_FILE_NAME "/etc/sensors.conf"
#endif

#include "lmSensors.h"

#define TEMP_TYPE    (0)
#define FAN_TYPE     (1)
#define VOLT_TYPE    (2)
#define MISC_TYPE    (3)
#define N_TYPES      (4)

#ifdef solaris2
    #define MAX_NAME     (256)
    #define MAX_SENSORS  (256) /* there's a lot of sensors on a v880 */
#else
    #define MAX_NAME     (64)
    #define DEFAULT_SENSORS  (256)
#endif


/*
 * lmSensors_variables_oid:
 *   this is the top level oid that we want to register under.  This
 *   is essentially a prefix, with the suffix appearing in the
 *   variable below.
 */


oid             lmSensors_variables_oid[] =
    { 1, 3, 6, 1, 4, 1, 2021, 13, 16 };

/*
 * variable4 lmSensors_variables:
 *   this variable defines function callbacks and type return information 
 *   for the lmSensors mib section 
 */

struct variable4 lmSensors_variables[] = {
    /*
     * magic number        , variable type , ro/rw , callback fn  , L, oidsuffix 
     */
#define   LMTEMPSENSORSINDEX    3
    {LMTEMPSENSORSINDEX, ASN_INTEGER, RONLY, var_lmSensorsTable, 3,
     {2, 1, 1}},
#define   LMTEMPSENSORSDEVICE   4
    {LMTEMPSENSORSDEVICE, ASN_OCTET_STR, RONLY, var_lmSensorsTable, 3,
     {2, 1, 2}},
#define   LMTEMPSENSORSVALUE    5
    {LMTEMPSENSORSVALUE, ASN_GAUGE, RONLY, var_lmSensorsTable, 3,
     {2, 1, 3}},
#define   LMFANSENSORSINDEX     8
    {LMFANSENSORSINDEX, ASN_INTEGER, RONLY, var_lmSensorsTable, 3,
     {3, 1, 1}},
#define   LMFANSENSORSDEVICE    9
    {LMFANSENSORSDEVICE, ASN_OCTET_STR, RONLY, var_lmSensorsTable, 3,
     {3, 1, 2}},
#define   LMFANSENSORSVALUE     10
    {LMFANSENSORSVALUE, ASN_GAUGE, RONLY, var_lmSensorsTable, 3,
     {3, 1, 3}},
#define   LMVOLTSENSORSINDEX    13
    {LMVOLTSENSORSINDEX, ASN_INTEGER, RONLY, var_lmSensorsTable, 3,
     {4, 1, 1}},
#define   LMVOLTSENSORSDEVICE   14
    {LMVOLTSENSORSDEVICE, ASN_OCTET_STR, RONLY, var_lmSensorsTable, 3,
     {4, 1, 2}},
#define   LMVOLTSENSORSVALUE    15
    {LMVOLTSENSORSVALUE, ASN_GAUGE, RONLY, var_lmSensorsTable, 3,
     {4, 1, 3}},
#define   LMMISCSENSORSINDEX    18
    {LMMISCSENSORSINDEX, ASN_INTEGER, RONLY, var_lmSensorsTable, 3,
     {5, 1, 1}},
#define   LMMISCSENSORSDEVICE   19
    {LMMISCSENSORSDEVICE, ASN_OCTET_STR, RONLY, var_lmSensorsTable, 3,
     {5, 1, 2}},
#define   LMMISCSENSORSVALUE    20
    {LMMISCSENSORSVALUE, ASN_GAUGE, RONLY, var_lmSensorsTable, 3,
     {5, 1, 3}},
};

typedef struct {
#ifdef solaris2
    #ifdef HAVE_PICL_H
        char            name[PICL_PROPNAMELEN_MAX]; /*required for picld*/
        int             value;
    #else
       char            name[MAX_NAME];
       int             value;
    #endif
#else
    char            name[MAX_NAME];
    int             value;
#endif
} _sensor;

typedef struct {
    int             n;
#ifdef solaris2
    _sensor         sensor[MAX_SENSORS];
#else
    _sensor*        sensor;
    size_t          current_len;
#endif
} _sensor_array;

static _sensor_array sensor_array[N_TYPES];
static time_t  timestamp;

static int      sensor_init(void);
static int      sensor_load(void);
static int     _sensor_load(time_t t);
#ifndef solaris2
static void     free_sensor_arrays(void);
#endif

/*
 * init_lmSensors():
 *   Initialization routine.  This is called when the agent starts up.
 *   At a minimum, registration of your variables should take place here.
 */
void
init_lmSensors(void)
{
   sensor_init(); 

    /*
     * register ourselves with the agent to handle our mib tree 
     */
    REGISTER_MIB("lmSensors", lmSensors_variables, variable4,
                 lmSensors_variables_oid);
}

/*
 * shutdown_lmSensors():
 * A shutdown/cleanup routine.  This is called when the agent shutsdown.
 */
void
shutdown_lmSensors(void)
{
#ifndef solaris2
    DEBUGMSG(("ucd-snmp/lmSensors", "=> shutdown_lmSensors\n"));
    free_sensor_arrays();
    DEBUGMSG(("ucd-snmp/lmSensors", "<= shutdown_lmSensors\n"));
#endif
} /* shutdown_lmSensors */

/*
 * var_lmSensorsTable():
 *   Handle this table separately from the scalar value case.
 *   The workings of this are basically the same as for var_lmSensors above.
 */
unsigned char  *
var_lmSensorsTable(struct variable *vp,
                   oid * name,
                   size_t * length,
                   int exact,
                   size_t * var_len, WriteMethod ** write_method)
{
    static long     long_ret;
    static unsigned char string[SPRINT_MAX_LEN];

    int             s_index;
    int             s_type = -1;
    int             n_sensors;
    unsigned char* ret = NULL;

    _sensor         s;

    if (sensor_load())
    {
        ret = NULL;
        goto leaving;
    }

    switch (vp->magic) {
    case LMTEMPSENSORSINDEX:
    case LMTEMPSENSORSDEVICE:
    case LMTEMPSENSORSVALUE:
        s_type = TEMP_TYPE;
        n_sensors = sensor_array[s_type].n;
        break;

    case LMFANSENSORSINDEX:
    case LMFANSENSORSDEVICE:
    case LMFANSENSORSVALUE:
        s_type = FAN_TYPE;
        n_sensors = sensor_array[s_type].n;
        break;

    case LMVOLTSENSORSINDEX:
    case LMVOLTSENSORSDEVICE:
    case LMVOLTSENSORSVALUE:
        s_type = VOLT_TYPE;
        n_sensors = sensor_array[s_type].n;
        break;

    case LMMISCSENSORSINDEX:
    case LMMISCSENSORSDEVICE:
    case LMMISCSENSORSVALUE:
        s_type = MISC_TYPE;
        n_sensors = sensor_array[s_type].n;
        break;

    default:
        s_type = -1;
        n_sensors = 0;
    }

    if (header_simple_table(vp, name, length, exact,
                            var_len, write_method,
                            n_sensors) == MATCH_FAILED)
    {
        ret = NULL;
        goto leaving;
    }

    if (s_type < 0)
    {
        ret = NULL;
        goto leaving;
    }

    s_index = name[*length - 1] - 1;
    s = sensor_array[s_type].sensor[s_index];

    switch (vp->magic) {
    case LMTEMPSENSORSINDEX:
    case LMFANSENSORSINDEX:
    case LMVOLTSENSORSINDEX:
    case LMMISCSENSORSINDEX:
        long_ret = s_index;
        ret = (unsigned char *) &long_ret;
        goto leaving;

    case LMTEMPSENSORSDEVICE:
    case LMFANSENSORSDEVICE:
    case LMVOLTSENSORSDEVICE:
    case LMMISCSENSORSDEVICE:
        strncpy(string, s.name, SPRINT_MAX_LEN - 1);
        *var_len = strlen(string);
        ret = (unsigned char *) string;
        goto leaving;

    case LMTEMPSENSORSVALUE:
    case LMFANSENSORSVALUE:
    case LMVOLTSENSORSVALUE:
    case LMMISCSENSORSVALUE:
        long_ret = s.value;
        ret = (unsigned char *) &long_ret;
        goto leaving;

    default:
        ERROR_MSG("Unable to handle table request");
    }

leaving:
    return ret;
}

static int
sensor_init(void)
{
    int             res;
#ifndef solaris2
    char            filename[] = CONFIG_FILE_NAME;
    time_t          t = time(NULL);
    FILE            *fp = fopen(filename, "r");
    int             i = 0;
  
    DEBUGMSG(("ucd-snmp/lmSensors", "=> sensor_init\n"));

    for (i = 0; i < N_TYPES; i++)
    {
        sensor_array[i].n = 0;
        sensor_array[i].current_len = 0;
        sensor_array[i].sensor = NULL;
    }

    if (!fp)
    {
        res = 1;
        goto leaving;
    }

    if (sensors_init(fp))
    {
        res = 2;
        goto leaving;
    }

    _sensor_load(t); /* I'll let the linux people decide whether they want to load right away */
#endif /* not solaris2 */

leaving:
    DEBUGMSG(("ucd-snmp/lmSensors", "<= sensor_init\n"));
    return res;
}

static int
sensor_load(void)
{
    int rc = 0;
    time_t	   t = time(NULL);

    if (t > timestamp + 7) /* this may require some tuning - currently 7 seconds*/
    {
#ifndef solaris2
        free_sensor_arrays();
#endif
        rc = _sensor_load(t);
    }

    return rc;
}

/* This next code block includes all kstat and picld code for the Solaris platform.
 * If you're not compiling on a Solaris that supports picld, it won't be included.
 */

#ifdef solaris2
/* *******  picld sensor procedures * */
#ifdef HAVE_PICL_H

/* the following are generic modules for reading sensor information
   the scale variable handles miniVolts */

static int
read_num_sensor(picl_nodehdl_t childh, char *prop ,int scale, int *value)
 {
  picl_nodehdl_t  sensorh;
  picl_propinfo_t sensor_info;
  picl_errno_t    error_code;
  int             valid = 1;

  union valu {
    char buf[PICL_PROPSIZE_MAX];
    uint32_t us4;
    uint16_t us2;
    int32_t is4;
    int16_t is2;
    float f;
  } val;

  error_code = (picl_get_propinfo_by_name(childh, prop,
                                         &sensor_info, &sensorh));

  if (error_code != PICL_SUCCESS) {
     DEBUGMSGTL(("ucd-snmp/lmSensors",
                "sensor info lookup failed in read_num_sensor - error code->%d\n", error_code));
     return(error_code);
  }

  error_code = picl_get_propval(sensorh, &val.buf, sensor_info.size);

  if (error_code != PICL_SUCCESS) {
    DEBUGMSGTL(("ucd-snmp/lmSensors",
               "sensor value lookup failed in read_num_sensor - error code->%d\n", error_code));
    return(error_code);
  }
    
  /* Can't make assumptions about the type or size of the value we get... */

  if  (sensor_info.type == PICL_PTYPE_FLOAT) {
    *value = (int)(val.f*scale);
  } else if (sensor_info.type == PICL_PTYPE_UNSIGNED_INT) {
    if (sensor_info.size == 2) {
      *value = (int)(val.us2 * scale);
    } else if (sensor_info.size == 4) {
      *value = (int)(val.us4 * scale);
    } else
      valid = 0;
  } else if (sensor_info.type == PICL_PTYPE_INT) {
    if (sensor_info.size == 2) {
      *value = (int)(val.is2 * scale);
    } else if (sensor_info.size == 4) {
      *value = (int)(val.is4 * scale);
    } else 
      valid = 0;
  } else
    valid = 0;

  if (valid == 0) {
    DEBUGMSGTL(("ucd-snmp/lmSensors",
               "Don't know how to handle data type %d with length %d\n", 
               sensor_info.type, sensor_info.size));
    error_code = PICL_FAILURE;
  } else 
    DEBUGMSGTL(("ucd-snmp/lmSensors", "read_num_sensor value is %d\n", *value));

  return(error_code);
} /* end of read_num_sensor() */

static int
read_enum_sensor(picl_nodehdl_t childh, char **options, u_int *value)
{
  picl_nodehdl_t  sensorh;
  picl_propinfo_t sensor_info;
  picl_errno_t    error_code;
  char            state[PICL_PROPSIZE_MAX];
  int             i;

  error_code = (picl_get_propinfo_by_name(childh, "State",
                                         &sensor_info, &sensorh));

  if (error_code != PICL_SUCCESS) {
     DEBUGMSGTL(("ucd-snmp/lmSensors",
                "sensor info lookup failed in read_enum_sensor - error code->%d\n", error_code));
     return(error_code);
  }

  error_code = picl_get_propval(sensorh, state, sensor_info.size);

  if (error_code != PICL_SUCCESS) {
    DEBUGMSGTL(("ucd-snmp/lmSensors",
               "sensor value lookup failed in read_enum_sensor - error code->%d\n", error_code));
    return(error_code);
  }

  /* Start with error value, then try to fill in something better.
     Use case-insensitive match to find the right value since platforms
     may return either case. 
  */

  *value = 99;

  for (i = 0; options[i] != NULL; i++){
    if (strncasecmp(state, options[i], strlen(options[i])) == 0){
      *value = i;
      break;
    } 
  }

  DEBUGMSGTL(("ucd-snmp/lmSensors", "read_enum_sensor value is %d\n", *value));
  return(error_code);
} /* end of read_enum_sensor() */

/* scale variable handles miniVolts*/
 
static int
process_num_sensor(picl_nodehdl_t childh, 
                  char propname[PICL_PROPNAMELEN_MAX], 
                  char propval[PICL_PROPNAMELEN_MAX], int typ, int scale)
{
  int value = 0;
  picl_errno_t error_code;

  if (sensor_array[typ].n >= MAX_SENSORS){
    DEBUGMSGTL(("ucd-snmp/lmSensors",
               "There are too many sensors of type %d\n",typ));
  } else{
    error_code = read_num_sensor(childh, propval, scale, &value);
    
    if (error_code == PICL_SUCCESS) {
      sensor_array[typ].sensor[sensor_array[typ].n].value = value;
      snprintf(sensor_array[typ].sensor[sensor_array[typ].n].name,
              (PICL_PROPNAMELEN_MAX - 1),"%s",propname);
      sensor_array[typ].sensor[sensor_array[typ].n].
       name[PICL_PROPNAMELEN_MAX - 1] = '\0';
      sensor_array[typ].n++;
    } else
      DEBUGMSGTL(("ucd-snmp/lmSensors",
                 "read of %s in process_num_sensor returned error code %d\n", propname, error_code));
  }
} /* end process_num_sensor() */

static int
process_enum_sensor(picl_nodehdl_t childh, 
                  char propname[PICL_PROPNAMELEN_MAX], 
                  int typ, char **options)
{
  int value = 0;
  picl_errno_t error_code;
 
  if (sensor_array[typ].n >= MAX_SENSORS){
    DEBUGMSGTL(("ucd-snmp/lmSensors",
               "There are too many sensors of type %d\n",typ));
  } else{
    error_code = read_enum_sensor(childh, options, &value);
    
    if (error_code == PICL_SUCCESS) {
      sensor_array[typ].sensor[sensor_array[typ].n].value = value;
      snprintf(sensor_array[typ].sensor[sensor_array[typ].n].name,
              (PICL_PROPNAMELEN_MAX - 1),"%s",propname);
      sensor_array[typ].sensor[sensor_array[typ].n].
       name[PICL_PROPNAMELEN_MAX - 1] = '\0';
      sensor_array[typ].n++;
    } else
      DEBUGMSGTL(("ucd-snmp/lmSensors",
                 "read of %s in process_enum_sensor returned error code %d\n", propname, error_code));
  }
} /* end process_enum_sensor() */

/* The following are modules for dealing with individual sensors types.
   They call the generic modules above.  */

static int
process_individual_fan(picl_nodehdl_t childh, 
                     char propname[PICL_PROPNAMELEN_MAX])
{
  process_num_sensor(childh, propname, "AtoDSensorValue", FAN_TYPE, 1);
}


static int
process_newtype_fan(picl_nodehdl_t childh,
                     char propname[PICL_PROPNAMELEN_MAX])
{
  process_num_sensor(childh, propname, "Speed", FAN_TYPE, 1);
}


static int
process_temperature_sensor(picl_nodehdl_t childh,
                               char propname[PICL_PROPNAMELEN_MAX])
{
  process_num_sensor(childh, propname, "Temperature", TEMP_TYPE, 1000);
} /* MIB asks for mC */

static int
process_voltage_sensor(picl_nodehdl_t childh,
                      char propname[PICL_PROPNAMELEN_MAX])
{
  process_num_sensor(childh, propname, "Voltage", VOLT_TYPE, 1000);
} /* MIB asks for mV */

static int
process_digital_sensor(picl_nodehdl_t childh,
                      char propname[PICL_PROPNAMELEN_MAX])
{
  process_num_sensor(childh, propname, "AtoDSensorValue", VOLT_TYPE, 1);
}


static int
process_switch(picl_nodehdl_t childh,
                   char propname[PICL_PROPNAMELEN_MAX])
{

  char *settings[]={"OFF","ON","NORMAL","LOCKED","UNKNOWN",
                   "DIAG","SECURE",NULL};

  process_enum_sensor(childh, propname, MISC_TYPE, settings);
}

static int
process_led(picl_nodehdl_t childh,
                   char propname[PICL_PROPNAMELEN_MAX])
{

  char *settings[]={"OFF","ON","BLINK",NULL};
  process_enum_sensor(childh, propname, MISC_TYPE, settings);
}

static int
process_i2c(picl_nodehdl_t childh,
                   char propname[PICL_PROPNAMELEN_MAX])
{
  char *settings[]={"OK",NULL};
  process_enum_sensor(childh, propname, MISC_TYPE, settings);
}

/* walks its way recusively through the tree of sensors */

static int
process_sensors(int level, picl_nodehdl_t nodeh)
{
    picl_nodehdl_t  childh;
    picl_nodehdl_t  nexth;

    char            propname[PICL_PROPNAMELEN_MAX];
    char            propclass[PICL_CLASSNAMELEN_MAX];
    picl_errno_t    error_code;

    level++;

    DEBUGMSGTL(("ucd-snmp/lmSensors","in process_sensors() level %d\n",level));

    /* look up first child node */
    error_code = picl_get_propval_by_name(nodeh, PICL_PROP_CHILD, &childh,
                                        sizeof (picl_nodehdl_t));
    if (error_code != PICL_SUCCESS) {
                DEBUGMSGTL(("ucd-snmp/lmSensors",
                           "picl_get_propval_by_name(%s) %d\n",
                           PICL_PROP_CHILD, error_code));
                return (error_code);
    }

    /* step through child nodes, get the name first */
    while (error_code == PICL_SUCCESS) {

        error_code = picl_get_propval_by_name(childh, PICL_PROP_NAME,
                                               propname, (PICL_PROPNAMELEN_MAX - 1));
        if (error_code != PICL_SUCCESS) {  /*we found a node with no name.  Impossible.! */
            DEBUGMSGTL(("ucd-snmp/lmSensors",
                       "picl_get_propval_by_name(%s) = %d\n",
                       PICL_PROP_NAME, error_code));
            return (error_code);
        }

        error_code = picl_get_propval_by_name(childh, PICL_PROP_CLASSNAME,
                                                propclass, sizeof (propclass));
        if (error_code != PICL_SUCCESS) {  /*we found a node with no class.  Impossible.! */
            DEBUGMSGTL(("ucd-snmp/lmSensors",
                       "picl_get_propval_by_name(%s) = %d\n",
                       PICL_PROP_CLASSNAME, error_code));
            return (error_code);
        }

        DEBUGMSGTL(("ucd-snmp/lmSensors","found %s of class %s\n",propname,propclass)); 

        if (strstr(propclass,"fan-tachometer"))
           process_individual_fan(childh,propname);
        else if (strstr(propclass,"fan"))
            process_newtype_fan(childh,propname);
        else if (strstr(propclass,"temperature-sensor"))
            process_temperature_sensor(childh,propname);
        else if (strstr(propclass,"voltage-sensor"))
            process_voltage_sensor(childh,propname);
        else if (strstr(propclass,"digital-sensor"))
            process_digital_sensor(childh,propname);
        else if (strstr(propclass,"switch"))
            process_switch(childh,propname);
        else if (strstr(propclass,"led"))
            process_led(childh,propname);
        else if (strstr(propclass,"i2c"))
            process_i2c(childh,propname);
/*
        else if (strstr(propclass,"gpio"))
            process_gpio(childh,propname); 
*/


        /* look for children of children (note, this is recursive) */
       if (!(strstr(propclass,"picl") && 
             (strstr(propname,"frutree") || strstr(propname,"obp")))) {
           error_code = process_sensors(level,childh);
           DEBUGMSGTL(("ucd-snmp/lmSensors",
                      "process_sensors(%s) returned %d\n",
                       propname, error_code));
        }

         /* get next child node at this level*/
        error_code = picl_get_propval_by_name(childh, PICL_PROP_PEER,
                                   &nexth, sizeof (picl_nodehdl_t));
        if (error_code != PICL_SUCCESS) {/* no more children - buh bye*/
           DEBUGMSGTL(("ucd-snmp/lmSensors","Process sensors is out of children!  Returning...\n"));
           return (error_code);
        }

        childh = nexth;

    } /* while */
    return (error_code);
} /* process sensors */

#endif
/* ******** end of picld sensor procedures * */

#endif /* solaris2 */
static int
_sensor_load(time_t t)
{
#ifdef solaris2
    int i,j;
    int typ;
    int temp=0; /* do not reset this later, more than one typ has temperatures*/
    int other=0;
    const char *fantypes[]={"CPU","PWR","AFB"};
    kstat_ctl_t *kc;
    kstat_t *kp;
    envctrl_fan_t *fan_info;
    envctrl_ps_t *power_info;
    envctrl_encl_t *enc_info;

/* DEBUGMSG(("ucd-snmp/lmSensors", "Reading the sensors\n")); */

/* initialize the array */
    for (i = 0; i < N_TYPES; i++){
        sensor_array[i].n = 0;
        for (j=0; j < MAX_SENSORS; j++){
            sensor_array[i].sensor[j].name[0] = '\0';
            sensor_array[i].sensor[j].value = 0;
             }
        } /*end for i*/

/* try picld (if supported), if that doesn't work, try kstat */
#ifdef HAVE_PICL_H 

/* some more declarations */

    int er_code;
    picl_errno_t     error_code;
    int level=0;
    picl_nodehdl_t  rooth;

er_code = picl_initialize();

if (er_code == PICL_SUCCESS) {

    error_code = picl_get_root(&rooth);

    if (error_code != PICL_SUCCESS) {
        DEBUGMSG(("ucd-snmp/lmSensors", "picld couldn't get root error code->%d\n",error_code));
        }
    else{
        DEBUGMSGTL(("ucd-snmp/lmSensors", "found root\n"));
        error_code = process_sensors(level,rooth);
        if (error_code != 255) 
            if (error_code != 7)
                DEBUGMSG(("ucd-snmp/lmSensors", "picld had an internal problem error code->%d\n",error_code));
        } /* end else */

    picl_shutdown();

}  /* end if err_code for picl_initialize */

else {  
    DEBUGMSG(("ucd-snmp/lmSensors", "picld couldn't initialize picld because error code->%d\n",er_code));

} /*end else picl_initialize */

#endif  /* end of picld section */
/* initialize kstat */

kc = kstat_open();
if (kc == 0) {
    DEBUGMSG(("ucd-snmp/lmSensors", "couldn't open kstat"));
    } /* endif kc */
else{
    temp = 0;
    kp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, 0, ENVCTRL_KSTAT_FANSTAT);
    if (kp == 0) {
        DEBUGMSGTL(("ucd-snmp/lmSensors", "couldn't lookup fan kstat\n"));
        } /* endif lookup fans */
    else{
        if (kstat_read(kc, kp, 0) == -1) {
            DEBUGMSGTL(("ucd-snmp/lmSensors", "couldn't read fan kstat"));
            } /* endif kstatread fan */
        else{
            typ = 1;
            fan_info = (envctrl_fan_t *) kp->ks_data;
            sensor_array[typ].n = kp->ks_ndata;
            for (i=0; i < kp->ks_ndata; i++){
                DEBUGMSG(("ucd-snmp/lmSensors", "found instance %d fan type %d speed %d OK %d bustedfan %d\n",
                    fan_info->instance, fan_info->type,fan_info->fanspeed,fan_info->fans_ok,fan_info->fanflt_num));
                sensor_array[typ].sensor[i].value = fan_info->fanspeed;
                snprintf(sensor_array[typ].sensor[i].name,(MAX_NAME - 1),
                   "fan type %s number %d",fantypes[fan_info->type],fan_info->instance);
                sensor_array[typ].sensor[i].name[MAX_NAME - 1] = '\0';
                fan_info++;
                } /* end for fan_info */
            } /* end else kstatread fan */
        } /* end else lookup fans*/


    kp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, 0, ENVCTRL_KSTAT_PSNAME);
    if (kp == 0) {
        DEBUGMSGTL(("ucd-snmp/lmSensors", "couldn't lookup power supply kstat\n"));
        } /* endif lookup power supply */
    else{
        if (kstat_read(kc, kp, 0) == -1) {
            DEBUGMSGTL(("ucd-snmp/lmSensors", "couldn't read power supply kstat\n"));
            } /* endif kstatread fan */
        else{
            typ = 0; /* this is a power supply temperature, not a voltage*/
            power_info = (envctrl_ps_t *) kp->ks_data;
            sensor_array[typ].n = kp->ks_ndata;
            for (i=0; i < kp->ks_ndata; i++){
                DEBUGMSG(("ucd-snmp/lmSensors", "found instance %d psupply temp mC %d %dW OK %d share %d limit %d\n",
                    power_info->instance, power_info->ps_tempr*1000,power_info->ps_rating,
                    power_info->ps_ok,power_info->curr_share_ok,power_info->limit_ok));
                sensor_array[typ].sensor[temp].value = power_info->ps_tempr*1000;
                snprintf(sensor_array[typ].sensor[temp].name,(MAX_NAME-1),
                         "power supply %d",power_info->instance);
                sensor_array[typ].sensor[temp].name[MAX_NAME - 1] = '\0';
                power_info++; /* increment the data structure */
                temp++; /* increment the temperature sensor array element */
                } /* end for power_info */
            } /* end else kstatread power supply */
        } /* end else lookup power supplies*/

    kp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, 0, ENVCTRL_KSTAT_ENCL);
    if (kp == 0) {
        DEBUGMSGTL(("ucd-snmp/lmSensors", "couldn't lookup enclosure kstat\n"));
        } /* endif lookup enclosure */
    else{
        if (kstat_read(kc, kp, 0) == -1) {
            DEBUGMSGTL(("ucd-snmp/lmSensors", "couldn't read enclosure kstat\n"));
            } /* endif kstatread enclosure */
        else{
            enc_info = (envctrl_encl_t *) kp->ks_data; 
            other = 0;
            for (i=0; i < kp->ks_ndata; i++){
               switch (enc_info->type){
               case ENVCTRL_ENCL_FSP:
                   DEBUGMSG(("ucd-snmp/lmSensors", "front panel value %d\n",enc_info->value));
                   typ = 3; /* misc */
                   sensor_array[typ].sensor[other].value = enc_info->value;
                   strncpy(sensor_array[typ].sensor[other].name,"FSP",MAX_NAME-1);
                   sensor_array[typ].sensor[other].name[MAX_NAME-1]='\0'; /* null terminate */
                   other++;
                   break;
               case ENVCTRL_ENCL_AMBTEMPR:
                   DEBUGMSG(("ucd-snmp/lmSensors", "ambient temp mC %d\n",enc_info->value*1000));
                   typ = 0; /* temperature sensor */
                   sensor_array[typ].sensor[temp].value = enc_info->value*1000;
                   strncpy(sensor_array[typ].sensor[temp].name,"Ambient",MAX_NAME-1);
                   sensor_array[typ].sensor[temp].name[MAX_NAME-1]='\0'; /* null terminate */
                   temp++;
                   break;
               case ENVCTRL_ENCL_BACKPLANE4:
                   DEBUGMSG(("ucd-snmp/lmSensors", "There is a backplane4\n"));
                   typ = 3; /* misc */
                   sensor_array[typ].sensor[other].value = enc_info->value;
                   strncpy(sensor_array[typ].sensor[other].name,"Backplane4",MAX_NAME-1);
                   sensor_array[typ].sensor[other].name[MAX_NAME-1]='\0'; /* null terminate */
                   other++;
                   break;
               case ENVCTRL_ENCL_BACKPLANE8:
                   DEBUGMSG(("ucd-snmp/lmSensors", "There is a backplane8\n"));
                   typ = 3; /* misc */
                   sensor_array[typ].sensor[other].value = enc_info->value;
                   strncpy(sensor_array[typ].sensor[other].name,"Backplane8",MAX_NAME-1);
                   sensor_array[typ].sensor[other].name[MAX_NAME-1]='\0'; /* null terminate */
                   other++;
                   break;
               case ENVCTRL_ENCL_CPUTEMPR:
                   DEBUGMSG(("ucd-snmp/lmSensors", "CPU%d temperature mC %d\n",enc_info->instance,enc_info->value*1000));
                   typ = 0; /* temperature sensor */
                   sensor_array[typ].sensor[temp].value = enc_info->value*1000;
                   snprintf(sensor_array[typ].sensor[temp].name,MAX_NAME,"CPU%d",enc_info->instance);
                   sensor_array[typ].sensor[temp].name[MAX_NAME-1]='\0'; /* null terminate */
                   temp++;
                   break;
               default:
                   DEBUGMSG(("ucd-snmp/lmSensors", "unknown element instance &d type &d value %d\n",
                       enc_info->instance, enc_info->type, enc_info->value));
                   break;
               } /* end switch */
               enc_info++;
               } /* end for enc_info */
               sensor_array[3].n = other;
               sensor_array[0].n = temp;
            } /* end else kstatread enclosure */
        } /* end else lookup enclosure*/

    kstat_close(kc);

} /* end else kstat */
#else /* end solaris2 only ie. ifdef everything else */

    const sensors_chip_name *chip;
    const sensors_feature_data *data;
    int             chip_nr = 0;
    int             rc = 0;
    unsigned int    i = 0;

    for (i = 0; i < N_TYPES; i++)
    {
        sensor_array[i].n = 0;
        sensor_array[i].current_len = 0;

        /* Malloc the default number of sensors. */
        sensor_array[i].sensor = (_sensor*)malloc(sizeof(_sensor) * DEFAULT_SENSORS);
        if (sensor_array[i].sensor == NULL)
        {
           /* Continuing would be unsafe */
           snmp_log(LOG_ERR, "Cannot malloc sensor array!"); 
           return (rc = 1);
        } /* end if */
        sensor_array[i].current_len = DEFAULT_SENSORS;
    } /* end for */

    while ((chip = sensors_get_detected_chips(&chip_nr))) {
	int             a = 0;
	int             b = 0;

        while ((data = sensors_get_all_features(*chip, &a, &b))) {
            char           *label = NULL;
            double          val;

            if ((data->mode & SENSORS_MODE_R) &&
                (data->mapping == SENSORS_NO_MAPPING) &&
                !sensors_get_label(*chip, data->number, &label) &&
                !sensors_get_feature(*chip, data->number, &val)) {
                int             type = -1;
                float           mul;
                _sensor_array  *array;

                /* The label, as determined for a given chip in sensors.conf,
                 * is used to place each sensor in the appropriate bucket.
                 * Volt, Fan, Temp, and Misc.  If the text being looked for below
                 * is not in the label of a given sensor (e.g., the temp1 sensor
                 * has been labeled 'CPU' and not 'CPU temp') it will end up being
                 * lumped in the MISC bucket. */

                if (strstr(label, "V")) {
                    type = VOLT_TYPE;
                    mul = 1000.0;
                }
                if (strstr(label, "fan") || strstr(label, "Fan")) {
                    type = FAN_TYPE;
                    mul = 1.0;
                }
                if (strstr(label, "temp") || strstr(label, "Temp")) {
                    type = TEMP_TYPE;
                    mul = 1000.0;
                }
                if (type == -1) {
                    type = MISC_TYPE;
                    mul = 1000.0;
                }

                array = &sensor_array[type];
                if ( array->current_len <= array->n) {
                    _sensor* old_buffer = array->sensor;
                    size_t new_size = (sizeof(_sensor) * array->current_len) + (sizeof(_sensor) * DEFAULT_SENSORS);
                    array->sensor = (_sensor*)realloc(array->sensor, new_size);
                    if (array->sensor == NULL)
                    {
                       /* Continuing would be unsafe */
                       snmp_log(LOG_ERR, "too many sensors to fit, and failed to alloc more, failing on %s\n", label);
                       free(old_buffer);
                       old_buffer = NULL;
                       if (label) {
                           free(label);
                           label = NULL;
                       } /* end if label */
                       return (rc=1);
                    } /* end if array->sensor */
                    array->current_len = new_size / sizeof(_sensor);
                    DEBUGMSG(("ucd-snmp/lmSensors", "type #%d increased to %d elements\n", type, array->current_len));
                } /* end if array->current */
                strncpy(array->sensor[array->n].name, label, MAX_NAME);
                array->sensor[array->n].value = (int) (val * mul);
                DEBUGMSGTL(("sensors","sensor %d, value %d\n",
                            array->sensor[array->n].name,
                            array->sensor[array->n].value));
                array->n++;
            } /* end if data-mode */
	    if (label) {
		free(label);
		label = NULL;
	    } /* end if label */
        } /* end while data */
    } /* end while chip */
    return rc;
#endif  /* end else ie. ifdef everything else */
    /* Update the timestamp after a load. */
    timestamp = t;
}

#ifndef solaris2
/* Free all the sensor arrays. */
static void
free_sensor_arrays()
{
   unsigned int i = 0;
   DEBUGMSG(("ucd-snmp/lmSensors", "=> free_sensor_arrays\n"));
   for (i = 0; i < N_TYPES; i++){
       if (sensor_array[i].sensor != NULL)
       {
           free(sensor_array[i].sensor);
           sensor_array[i].sensor = NULL;
       }
       /* For good measure, reset the other values. */
       sensor_array[i].n = 0;
       sensor_array[i].current_len = 0;
   }
   DEBUGMSG(("ucd-snmp/lmSensors", "<= free_sensor_arrays\n"));
}
#endif
