Logo Search packages:      
Sourcecode: nut version File versions  Download package

snmp-ups.c

/*  snmp-ups.c - NUT Meta SNMP driver (support different MIBS)
 *
 *  Based on NetSNMP API (Simple Network Management Protocol V1-2)
 *
 *  Copyright (C) 2002-2006 
 *                Arnaud Quette <arnaud.quette@free.fr>
 *                Dmitry Frolov <frolov@riss-telecom.ru>
 *                J.W. Hoogervorst <jeroen@hoogervorst.net>
 *                Niels Baggesen <niels@baggesen.net>
 *
 *  Sponsored by MGE UPS SYSTEMS <http://opensource.mgeups.com/>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

/* NUT SNMP common functions */
#include "snmp-ups.h"
#include "main.h"
#include "parseconf.h"

/* include all known mib2nut lookup tables */
#include "apccmib.h"
#include "ietfmib.h"
#include "mgemib.h"
#include "netvisionmib.h"
#include "pwmib.h"

mib2nut_info_t mib2nut[] = {
      { "apcc", APCC_MIB_VERSION, APCC_OID_POWER_STATUS,
            ".1.3.6.1.4.1.318.1.1.1.1.1.1.0", apcc_mib },
      { "mge", MGE_MIB_VERSION, "",
            MGE_OID_MODEL_NAME, mge_mib },
      { "netvision", NETVISION_MIB_VERSION, "",
            NETVISION_OID_UPSIDENTMODEL, netvision_mib },
      { "pw", PW_MIB_VERSION, "",
            PW_OID_MODEL_NAME, pw_mib },
      { "ietf", IETF_MIB_VERSION, IETF_OID_POWER_STATUS,
            IETF_OID_MFR_NAME, ietf_mib },
      { NULL }
};

/* pointer to the Snmp2Nut lookup table */
snmp_info_t *snmp_info;
const char *mibname;
const char *mibvers;

time_t lastpoll;

/* ---------------------------------------------
 * driver functions implementations
 * --------------------------------------------- */
void upsdrv_initinfo(void)
{
      snmp_info_t *su_info_p;
      char version[128];

      upsdebugx(1, "SNMP UPS driver : entering upsdrv_initinfo()");

      snprintf(version, sizeof version, "%s (mib: %s %s)",
            DRIVER_VERSION, mibname, mibvers);
      dstate_setinfo("driver.version.internal", version);
      
      /* add instant commands to the info database. */
      for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++)               
            su_info_p->flags |= SU_FLAG_OK;
            if (SU_TYPE(su_info_p) == SU_TYPE_CMD)
                  dstate_addcmd(su_info_p->info_type);

      /* setup handlers for instcmd and setvar functions */
      upsh.setvar = su_setvar;
      upsh.instcmd = su_instcmd;

      if (testvar("notransferoids"))
            disable_transfer_oids();

      /* initialize all other INFO_ fields from list */
      if (snmp_ups_walk(SU_WALKMODE_INIT))
            dstate_dataok();
      else        
            dstate_datastale();

      /* store timestamp */
      lastpoll = time(NULL);
}

void upsdrv_updateinfo(void)
{
      upsdebugx(1,"SNMP UPS driver : entering upsdrv_updateinfo()");
      
      /* only update every pollfreq */
      if (time(NULL) > (lastpoll + pollfreq)) {

            /* update all dynamic info fields */
            if (snmp_ups_walk(SU_WALKMODE_UPDATE))
                  dstate_dataok();
            else        
                  dstate_datastale();     
      
            /* store timestamp */
            lastpoll = time(NULL);
      }
}

void upsdrv_shutdown(void)
{
      /* TODO: su_shutdown_ups(); */
      
      /* replace with a proper shutdown function */
      fatalx(EXIT_FAILURE, "shutdown not supported");
}

void upsdrv_help(void)
{
      upsdebugx(1, "entering upsdrv_help");
}

/* list flags and values that you want to receive via -x */
void upsdrv_makevartable(void)
{
      upsdebugx(1, "entering upsdrv_makevartable()");

      addvar(VAR_VALUE, SU_VAR_MIBS,
          "Set MIB compliance (default=ietf, allowed mge,apcc,netvision,pw)");
      addvar(VAR_VALUE | VAR_SENSITIVE, SU_VAR_COMMUNITY,
          "Set community name (default=public)");
      addvar(VAR_VALUE, SU_VAR_VERSION,
          "Set SNMP version (default=v1, allowed v2c)");
      addvar(VAR_VALUE, SU_VAR_POLLFREQ,
          "Set polling frequency in seconds, to reduce network flow (default=30)");
      addvar(VAR_FLAG, "notransferoids",
          "Disable transfer OIDs (use on APCC Symmetras)");
}

void upsdrv_banner(void)
{
      upslogx(1,"Network UPS Tools - Multi-MIBS SNMP UPS driver %s (%s)", 
            DRIVER_VERSION, UPS_VERSION);
      
      experimental_driver = 1;
}

void upsdrv_initups(void)
{
      snmp_info_t *su_info_p;
      char model[SU_INFOSIZE];
      bool_t status;
      const char *community, *version, *mibs;

      upsdebugx(1, "SNMP UPS driver : entering upsdrv_initups()");
      
      community = testvar(SU_VAR_COMMUNITY) ? getval(SU_VAR_COMMUNITY) : "public";
      version = testvar(SU_VAR_VERSION) ? getval(SU_VAR_VERSION) : "v1";
      mibs = testvar(SU_VAR_MIBS) ? getval(SU_VAR_MIBS) : "auto";

      /* init SNMP library, etc... */
      nut_snmp_init(progname, device_path, version, community);

      /* Load the SNMP to NUT translation data */
      /* read_mibconf(SU_VAR_MIBS) ? getval(SU_VAR_MIBS) : "ietf"); */
      load_mib2nut(mibs);

      /* init polling frequency */
      if (getval(SU_VAR_POLLFREQ))
            pollfreq = atoi(getval(SU_VAR_POLLFREQ));
      else
            pollfreq = DEFAULT_POLLFREQ;
      
      /* Get UPS Model node to see if there's a MIB */
      su_info_p = su_find_info("ups.model");
      status = nut_snmp_get_str(su_info_p->OID, model, sizeof(model), NULL);

      if (status == TRUE)
            upslogx(0, "Detected %s on host %s (mib: %s %s)",
                   model, device_path, mibname, mibvers);
      else
            fatalx(EXIT_FAILURE, "%s MIB wasn't found on %s", mibs, g_snmp_sess.peername);   
}

void upsdrv_cleanup(void)
{
      nut_snmp_cleanup();
}

/* -----------------------------------------------------------
 * SNMP functions.
 * ----------------------------------------------------------- */

void nut_snmp_init(const char *type, const char *hostname, const char *version,
            const char *community)
{  
      upsdebugx(2, "SNMP UPS driver : entering nut_snmp_init(%s, %s, %s, %s)",
            type, hostname, version, community);

      /* Initialize the SNMP library */
      init_snmp(type);

      /* Initialize session */
      snmp_sess_init(&g_snmp_sess);

      g_snmp_sess.peername = xstrdup(hostname);
      g_snmp_sess.community = xstrdup(community);
      g_snmp_sess.community_len = strlen(community);
      if (strcmp(version, "v1") == 0)
            g_snmp_sess.version = SNMP_VERSION_1;
      else if (strcmp(version, "v2c") == 0)
            g_snmp_sess.version = SNMP_VERSION_2c;
      else
            fatalx(EXIT_FAILURE, "Bad SNMP version: %s", version);

      /* Open the session */
      SOCK_STARTUP;
      g_snmp_sess_p = snmp_open(&g_snmp_sess);  /* establish the session */
      if (g_snmp_sess_p == NULL) {
            nut_snmp_perror(&g_snmp_sess, 0, NULL, "nut_snmp_init: snmp_open");
            fatalx(EXIT_FAILURE, "Unable to establish communication");
      }
}

void nut_snmp_cleanup(void)
{
      /* close snmp session. */
      if (g_snmp_sess_p) {
            snmp_close(g_snmp_sess_p);
            g_snmp_sess_p = NULL;
      }
      SOCK_CLEANUP;
}

struct snmp_pdu *nut_snmp_get(const char *OID)
{
      int status;
      struct snmp_pdu *pdu, *response = NULL;
      oid name[MAX_OID_LEN];
      size_t name_len = MAX_OID_LEN;
      static unsigned int numerr = 0;

      /* create and send request. */
      if (!snmp_parse_oid(OID, name, &name_len)) {
            upslogx(LOG_ERR, "[%s] nut_snmp_get: %s: %s",
                  upsname?upsname:device_name, OID, snmp_api_errstring(snmp_errno));
            return NULL;
      }

      pdu = snmp_pdu_create(SNMP_MSG_GET);
      
      if (pdu == NULL)
            fatalx(EXIT_FAILURE, "Not enough memory");
      
      snmp_add_null_var(pdu, name, name_len);

      status = snmp_synch_response(g_snmp_sess_p, pdu, &response);

      if (!response)
            return NULL;

      if (!((status == STAT_SUCCESS) && (response->errstat == SNMP_ERR_NOERROR)))
      {
            if (mibname == NULL) {
                  /* We are probing for proper mib - ignore errors */
                  snmp_free_pdu(response);
                  return NULL;
            }

            numerr++;

            if ((numerr == SU_ERR_LIMIT) || ((numerr % SU_ERR_RATE) == 0))
                  upslogx(LOG_WARNING, "[%s] Warning: excessive poll "
                        "failures, limiting error reporting",
                        upsname?upsname:device_name);

            if ((numerr < SU_ERR_LIMIT) || ((numerr % SU_ERR_RATE) == 0))
                  nut_snmp_perror(g_snmp_sess_p, status, response, 
                        "nut_snmp_get: %s", OID);

            snmp_free_pdu(response);
            response = NULL;
      } else {
            numerr = 0;
      }

      return response;
}

bool_t nut_snmp_get_str(const char *OID, char *buf, size_t buf_len, info_lkp_t *oid2info)
{
      size_t len = 0;
      struct snmp_pdu *pdu;

      /* zero out buffer. */
      memset(buf, 0, buf_len);

      pdu = nut_snmp_get(OID);
      if (pdu == NULL)
            return FALSE;

      switch (pdu->variables->type) {
      case ASN_OCTET_STR:
      case ASN_OPAQUE:
            len = pdu->variables->val_len > buf_len - 1 ?
                  buf_len - 1 : pdu->variables->val_len;
            memcpy(buf, pdu->variables->val.string, len);
            buf[len] = '\0';
            break;
      case ASN_INTEGER:
      case ASN_GAUGE:
            if(oid2info) {
                  const char *str;
                  if((str=su_find_infoval(oid2info, *pdu->variables->val.integer))) {
                        strncpy(buf, str, buf_len-1);
                  }
                  else {
                        strncpy(buf, "UNKNOWN", buf_len-1);
                  }
                  buf[buf_len-1]='\0';
            }
            else {
                  len = snprintf(buf, buf_len, "%ld", *pdu->variables->val.integer);
            }
            break;
      case ASN_TIMETICKS:
            /* convert timeticks to seconds */
            len = snprintf(buf, buf_len, "%ld", *pdu->variables->val.integer / 100);
            break;
      default:
            upslogx(LOG_ERR, "[%s] unhandled ASN 0x%x recieved from %s",
                  upsname?upsname:device_name, pdu->variables->type, OID);
            return FALSE;
            break;
      }

      snmp_free_pdu(pdu);

      return TRUE;
}

bool_t nut_snmp_get_int(const char *OID, long *pval)
{
      struct snmp_pdu *pdu;
      long value;
      char *buf;

      pdu = nut_snmp_get(OID);
      if (pdu == NULL)
            return FALSE;

      switch (pdu->variables->type) {
      case ASN_OCTET_STR:
      case ASN_OPAQUE:
            buf = xmalloc(pdu->variables->val_len + 1);
            memcpy(buf, pdu->variables->val.string, pdu->variables->val_len);
            buf[pdu->variables->val_len] = '\0';
            value = strtol(buf, NULL, 0);
            free(buf);
            break;
      case ASN_INTEGER:
      case ASN_GAUGE:
            value = *pdu->variables->val.integer;
            break;
      case ASN_TIMETICKS:
            /* convert timeticks to seconds */
            value = *pdu->variables->val.integer / 100;
            break;
      default:
            upslogx(LOG_ERR, "[%s] unhandled ASN 0x%x recieved from %s",
                  upsname?upsname:device_name, pdu->variables->type, OID);
            return FALSE;
            break;
      }

      snmp_free_pdu(pdu);

      if (pval != NULL)
            *pval = value;

      return TRUE;
}

bool_t nut_snmp_set(const char *OID, char type, const char *value)
{
      int status;
      bool_t ret = FALSE;
      struct snmp_pdu *pdu, *response = NULL;
      oid name[MAX_OID_LEN];
      size_t name_len = MAX_OID_LEN;
      
      if (!snmp_parse_oid(OID, name, &name_len)) {
            upslogx(LOG_ERR, "[%s] nut_snmp_set: %s: %s",
                  upsname?upsname:device_name, OID, snmp_api_errstring(snmp_errno));
            return FALSE;
      }

      pdu = snmp_pdu_create(SNMP_MSG_SET);
      if (pdu == NULL)
            fatalx(EXIT_FAILURE, "Not enough memory");

      if (snmp_add_var(pdu, name, name_len, type, value)) {
            upslogx(LOG_ERR, "[%s] nut_snmp_set: %s: %s",
                  upsname?upsname:device_name, OID, snmp_api_errstring(snmp_errno));
            
            return FALSE;
      }

      status = snmp_synch_response(g_snmp_sess_p, pdu, &response);

      if ((status == STAT_SUCCESS) && (response->errstat == SNMP_ERR_NOERROR))
            ret = TRUE;
      else
            nut_snmp_perror(g_snmp_sess_p, status, response,
                  "nut_snmp_set: can't set %s", OID);

      snmp_free_pdu(response);
      return ret;
}

bool_t nut_snmp_set_str(const char *OID, const char *value)
{
      return nut_snmp_set(OID, 's', value);
}

bool_t nut_snmp_set_int(const char *OID, long value)
{
      char buf[SU_BUFSIZE];

      snprintf(buf, sizeof(buf), "%ld", value);
      return nut_snmp_set(OID, 'i', buf);
}

bool_t nut_snmp_set_time(const char *OID, long value)
{
      char buf[SU_BUFSIZE];

      snprintf(buf, SU_BUFSIZE, "%ld", value * 100);
      return nut_snmp_set(OID, 't', buf);
}

/* log descriptive SNMP error message. */
void nut_snmp_perror(struct snmp_session *sess, int status,
      struct snmp_pdu *response, const char *fmt, ...)
{
      va_list va;
      int cliberr, snmperr;
      char *snmperrstr;
      char buf[SU_LARGEBUF];

      va_start(va, fmt);
      vsnprintf(buf, sizeof(buf), fmt, va);
      va_end(va);

      if (response == NULL) {
            snmp_error(sess, &cliberr, &snmperr, &snmperrstr);
            upslogx(LOG_ERR, "[%s] %s: %s",
                  upsname?upsname:device_name, buf, snmperrstr);
            free(snmperrstr);
      } else if (status == STAT_SUCCESS) {
            if (response->errstat != SNMP_ERR_NOERROR)
                  upslogx(LOG_ERR, "[%s] %s: Error in packet: %s",
                        upsname?upsname:device_name, buf, snmp_errstring(response->errstat));
      } else if (status == STAT_TIMEOUT) {
            upslogx(LOG_ERR, "[%s] %s: Timeout: no response from %s",
                  upsname?upsname:device_name, buf, sess->peername);
      } else {
            snmp_sess_error(sess, &cliberr, &snmperr, &snmperrstr);
            upslogx(LOG_ERR, "[%s] %s: %s",
                  upsname?upsname:device_name, buf, snmperrstr);
            free(snmperrstr);
      }
}

/* -----------------------------------------------------------
 * utility functions.
 * ----------------------------------------------------------- */

/* deal with APCC weirdness on Symmetras */
static void disable_transfer_oids(void)
{
      snmp_info_t *su_info_p;

      upslogx(LOG_INFO, "Disabling transfer OIDs");

      for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++) {
            if (!strcasecmp(su_info_p->info_type, "input.transfer.low")) {
                  su_info_p->flags &= ~SU_FLAG_OK;
                  continue;
            }

            if (!strcasecmp(su_info_p->info_type, "input.transfer.high")) {
                  su_info_p->flags &= ~SU_FLAG_OK;
                  continue;
            }
      }
}

/* universal function to add or update info element. */
void su_setinfo(const char *type, const char *value, int flags, int auxdata)
{
      snmp_info_t *su_info_p;
      
      upsdebugx(1, "SNMP UPS driver : entering su_setinfo(%s)", type);
                  
      su_info_p = su_find_info(type);
      
      if (SU_TYPE(su_info_p) == SU_TYPE_CMD)
            return;

      if (strcasecmp(type, "ups.status")) {
            dstate_setinfo(type, value);
            dstate_setflags(type, flags);
            dstate_setaux(type, auxdata);
      }
}

void su_status_set(snmp_info_t *su_info_p, long value)
{     
      const char *info_value = NULL;

      upsdebugx(2, "SNMP UPS driver : entering su_status_set()");

      if ((info_value = su_find_infoval(su_info_p->oid2info, value)) != NULL)
      {
            if (strcmp(info_value, "")) {
                  status_init();
                  status_set(info_value);
                  status_commit();
            }
      }
      /* TODO: else */
}

/* find info element definition in my info array. */
snmp_info_t *su_find_info(const char *type)
{
      snmp_info_t *su_info_p;

      for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++)
            if (!strcasecmp(su_info_p->info_type, type))
                  return su_info_p;
            
      fatalx(EXIT_FAILURE, "nut_snmp_find_info: unknown info type: %s", type);
      return NULL;
}

/* Load the right snmp_info_t structure matching mib parameter */
void load_mib2nut(const char *mib)
{
      mib2nut_info_t *mp = mib2nut;
      upsdebugx(2, "SNMP UPS driver : entering load_mib2nut(%s)", mib);
      
/*    read_mibconf(mib); */
      
      while (mp->mib_name) {
            if (strcmp(mib, mp->mib_name) == 0)
                  break;
            else if (strcmp(mib, "auto") == 0) {
                  int status;
                  char buf[1024];
                  upsdebugx(1, "load_mib2nut: trying %s mib", mp->mib_name);
                  status = nut_snmp_get_str(mp->oid_auto_check,
                                    buf, sizeof buf, NULL);
                  if (status)
                        break;
            }
            mp++;
      }
      if (mp->mib_name) {
            snmp_info = mp->snmp_info;
            OID_pwr_status = mp->oid_pwr_status;
            mibname = mp->mib_name;
            mibvers = mp->mib_version;
            upsdebugx(1, "load_mib2nut: using %s mib", mibname);
      }
      else
            fatalx(EXIT_FAILURE, "Unknown mibs value: %s", mib);
}

/* find the OID value matching that INFO_* value */
long su_find_valinfo(info_lkp_t *oid2info, char* value)
{
      info_lkp_t *info_lkp;

      for (info_lkp = oid2info; (info_lkp != NULL) &&
            (strcmp(info_lkp->info_value, "NULL")); info_lkp++) {
                  
            if (!(strcmp(info_lkp->info_value, value))) {
                  upsdebugx(1, "su_find_valinfo: found %s (value: %s)",
                              info_lkp->info_value, value);
                  
                  return info_lkp->oid_value;
            }
      }
      upsdebugx(1, "su_find_valinfo: no matching INFO_* value for this OID value (%s)", value);
      return -1;
}

/* find the INFO_* value matching that OID value */
const char *su_find_infoval(info_lkp_t *oid2info, long value)
{
      info_lkp_t *info_lkp;
            
      for (info_lkp = oid2info; (info_lkp != NULL) &&
            (strcmp(info_lkp->info_value, "NULL")); info_lkp++) {
                  
            if (info_lkp->oid_value == value) {
                  upsdebugx(1, "su_find_infoval: found %s (value: %ld)",
                              info_lkp->info_value, value);
                  
                  return info_lkp->info_value;
            }
      }
      upsdebugx(1, "su_find_infoval: no matching INFO_* value for this OID value (%ld)", value);
      return NULL;
}

static void disable_competition(snmp_info_t *entry)
{
      snmp_info_t *p;

      for(p=snmp_info; p->info_type!=NULL; p++) {
            if(p!=entry && !strcmp(p->info_type, entry->info_type)) {
                  upsdebugx(2, "disable_competition: disabling %s %s",
                              p->info_type, p->OID);
                  p->flags &= ~SU_FLAG_OK;
            }
      }
}

/* walk ups variables and set elements of the info array. */
bool_t snmp_ups_walk(int mode)
{
      static unsigned long iterations = 0;
      snmp_info_t *su_info_p;
      bool_t status = FALSE;
      
      for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++) {

            /* skip instcmd. */
            if (SU_TYPE(su_info_p) == SU_TYPE_CMD) {
                  upsdebugx(1, "SU_CMD_MASK => %s", su_info_p->OID);
                  continue;
            }
            /* skip elements we shouldn't show. */
            if (!(su_info_p->flags & SU_FLAG_OK))
                  continue;
            
            /* skip static elements in update mode. */
            if (mode == SU_WALKMODE_UPDATE && 
                        su_info_p->flags & SU_FLAG_STATIC)
                  continue;

            /* set default value if we cannot fetch it */
            /* and set static flag on this element. */
            if (su_info_p->flags & SU_FLAG_ABSENT) {
                  if (mode == SU_WALKMODE_INIT) {
                        if (su_info_p->dfl) {
                              /* Set default value if we cannot fetch it from ups. */
                              su_setinfo(su_info_p->info_type, su_info_p->dfl,
                                    su_info_p->info_flags, su_info_p->info_len);
                        }
                        su_info_p->flags |= SU_FLAG_STATIC;
                  }
                  continue;
            }

            /* check stale elements only on each PN_STALE_RETRY iteration. */
            if ((su_info_p->flags & SU_FLAG_STALE) &&
                        (iterations % SU_STALE_RETRY) != 0)
                  continue;

            if (su_info_p->flags & SU_INPHASES) {
                  upsdebugx(1, "Check inphases");
                  if (input_phases == 0) continue;
                  upsdebugx(1, "inphases is set");
                  if (su_info_p->flags & SU_INPUT_1) {
                        if (input_phases == 1)
                              su_info_p->flags &= ~SU_INPHASES;
                        else {
                              upsdebugx(1, "inphases is not 1");
                              su_info_p->flags &= ~SU_FLAG_OK;
                              continue;
                        }
                  }
                  else if (su_info_p->flags & SU_INPUT_3) {
                        if (input_phases == 3)
                              su_info_p->flags &= ~SU_INPHASES;
                        else {
                              upsdebugx(1, "inphases is not 3");
                              su_info_p->flags &= ~SU_FLAG_OK;
                              continue;
                        }
                  }
            }

            if (su_info_p->flags & SU_OUTPHASES) {
                  if (output_phases == 0) continue;
                  if (su_info_p->flags & SU_OUTPUT_1) {
                        if (output_phases == 1)
                              su_info_p->flags &= ~SU_OUTPHASES;
                        else {
                              su_info_p->flags &= ~SU_FLAG_OK;
                              continue;
                        }
                  }
                  else if (su_info_p->flags & SU_OUTPUT_3) {
                        if (output_phases == 3)
                              su_info_p->flags &= ~SU_OUTPHASES;
                        else {
                              su_info_p->flags &= ~SU_FLAG_OK;
                              continue;
                        }
                  }
            }

            /* ok, update this element. */
            status = su_ups_get(su_info_p);
            
            /* set stale flag if data is stale, clear if not. */
            if (status == TRUE) {
                  if (su_info_p->flags & SU_FLAG_STALE) {
                        upslogx(LOG_INFO, "[%s] snmp_ups_walk: data resumed for %s",
                              upsname?upsname:device_name, su_info_p->info_type);
                        su_info_p->flags &= ~SU_FLAG_STALE;
                  }
                  if(su_info_p->flags & SU_FLAG_UNIQUE) {
                        /* We should be the only provider of this */
                        disable_competition(su_info_p);
                        su_info_p->flags &= ~SU_FLAG_UNIQUE;
                  }
                  dstate_dataok();
            } else {
                  if (mode == SU_WALKMODE_INIT) {
                        /* handle unsupported vars */
                        su_info_p->flags &= ~SU_FLAG_OK;
                  } else      {
                        if (!(su_info_p->flags & SU_FLAG_STALE)) {
                              upslogx(LOG_INFO, "[%s] snmp_ups_walk: data stale for %s",
                                    upsname?upsname:device_name, su_info_p->info_type);
                              su_info_p->flags |= SU_FLAG_STALE;
                        }
                        dstate_datastale();
                  }
            }
      }     /* for (su_info_p... */

      iterations++;
      
      return status;    
}

bool_t su_ups_get(snmp_info_t *su_info_p)
{
      static char buf[SU_INFOSIZE];
      bool_t status;
      long value;

      upsdebugx(2, "su_ups_get: %s %s", su_info_p->info_type, su_info_p->OID);
      
      if (!strcasecmp(su_info_p->info_type, "ups.status")) {

            status = nut_snmp_get_int(su_info_p->OID, &value);
            if (status == TRUE)
            {
                  su_status_set(su_info_p, value);
                  upsdebugx(2, "=> value: %ld", value);
            }
            else upsdebugx(2, "=> Failed");

            return status;
      }

      /* another special case */
      if (!strcasecmp(su_info_p->info_type, "ambient.temperature")) {
            float temp=0;

            status = nut_snmp_get_int(su_info_p->OID, &value);

            if(status != TRUE) {
                  return status;
            }

            /* only do this if using the IEM sensor */
            if (!strcmp(su_info_p->OID, APCC_OID_IEM_TEMP)) {
                  int   su;
                  long  units;

                  su = nut_snmp_get_int(APCC_OID_IEM_TEMP_UNIT, &units);

                  /* no response, or units == F */
                  if ((su == FALSE) || (units == APCC_IEM_FAHRENHEIT))
                        temp = (value - 32) / 1.8;
            }
            else {
                  temp = value * su_info_p->info_len;
            }

            snprintf(buf, sizeof(buf), "%.1f", temp);
            su_setinfo(su_info_p->info_type, buf,
                  su_info_p->info_flags, su_info_p->info_len);

            return TRUE;
      }                 

      if (su_info_p->info_flags == 0) {
            status = nut_snmp_get_int(su_info_p->OID, &value);
            if (status == TRUE) {
                  if (su_info_p->flags&SU_FLAG_NEGINVALID && value<0) {
                        su_info_p->flags &= ~SU_FLAG_OK;
                        if(su_info_p->flags&SU_FLAG_UNIQUE) {
                              disable_competition(su_info_p);
                              su_info_p->flags &= ~SU_FLAG_UNIQUE;
                        }
                        return FALSE;
                  }
                  if (su_info_p->flags & SU_FLAG_SETINT) {
                        upsdebugx(1, "setvar %s", su_info_p->OID);
                        *su_info_p->setvar = value;
                  }
                  snprintf(buf, sizeof(buf), "%.1f", value * su_info_p->info_len);
            }
      } else {
            status = nut_snmp_get_str(su_info_p->OID, buf, sizeof(buf), su_info_p->oid2info);
      }
            
      if (status == TRUE) {
            su_setinfo(su_info_p->info_type, buf,
                  su_info_p->info_flags, su_info_p->info_len);
            
            upsdebugx(2, "=> value: %s", buf);
      }
      else upsdebugx(2, "=> Failed");
      return status;
}

/* set r/w INFO_ element to a value. */
int su_setvar(const char *varname, const char *val)
{
      snmp_info_t *su_info_p;
      bool_t ret;

      upsdebugx(2, "entering su_setvar()");

      su_info_p = su_find_info(varname);

      if (su_info_p == NULL || su_info_p->info_type == NULL ||
            !(su_info_p->flags & SU_FLAG_OK))
      {
            upslogx(LOG_ERR, "su_setvar: info element unavailable %s", varname);
            return STAT_SET_UNKNOWN;
      }

      if (!(su_info_p->info_flags & ST_FLAG_RW) || su_info_p->OID == NULL) {
            upslogx(LOG_ERR, "su_setvar: not writable %s", varname);
            return STAT_SET_UNKNOWN; /* STAT_SET_UNHANDLED would be better */
      }

      /* set value. */
      if (SU_TYPE(su_info_p) == SU_TYPE_STRING) {
            ret = nut_snmp_set_str(su_info_p->OID, val);
      } else {
            ret = nut_snmp_set_int(su_info_p->OID, strtol(val, NULL, 0));
      }

      if (ret == FALSE)
            upslogx(LOG_ERR, "su_setvar: cannot set value %s for %s", val, su_info_p->OID);
      else
            upsdebugx(1, "su_setvar: sucessfully set %s to \"%s\"", su_info_p->info_type, val);

      /* update info array. */
      su_setinfo(varname, val, su_info_p->info_flags, su_info_p->info_len);

      /* TODO: check su_setinfo() retcode */
      return STAT_SET_HANDLED;
}

/* process instant command and take action. */
int su_instcmd(const char *cmdname, const char *extradata)
{
      snmp_info_t *su_info_p;
      int status;

      upsdebugx(2, "entering su_instcmd()");

      su_info_p = su_find_info(cmdname);

      if ((su_info_p->info_type == NULL) || !(su_info_p->flags & SU_FLAG_OK) ||
            (su_info_p->OID == NULL))
      {
            upslogx(LOG_ERR, "su_instcmd: %s unavailable", cmdname);
            return STAT_INSTCMD_UNKNOWN;
      }

      /* set value. */
      if (su_info_p->info_flags & ST_FLAG_STRING) {
            status = nut_snmp_set_str(su_info_p->OID, su_info_p->dfl);
      } else {
            status = nut_snmp_set_int(su_info_p->OID, su_info_p->info_len);
      }

      if (status == FALSE) {
            upslogx(LOG_ERR, "su_instcmd: cannot set value for %s", cmdname);
            return STAT_INSTCMD_UNKNOWN;
      } else {
            upsdebugx(1, "su_instcmd: successfully sent command %s", cmdname);
            return STAT_INSTCMD_HANDLED;
      }     
}

/* TODO: complete rewrite */
void su_shutdown_ups(void)
{
      int sdtype = 0;
      long pwr_status;

      if (nut_snmp_get_int(OID_pwr_status, &pwr_status) == FALSE)
            fatalx(EXIT_FAILURE, "cannot determine UPS status");

      if (testvar(SU_VAR_SDTYPE))
            sdtype = atoi(getval(SU_VAR_SDTYPE));

      /* logic from newapc.c */
      switch (sdtype) {
      case 3:           /* shutdown with grace period */
            upslogx(LOG_INFO, "sending delayed power off command to UPS");
            su_instcmd("shutdown.stayoff", "0");
            break;
      case 2:           /* instant shutdown */
            upslogx(LOG_INFO, "sending power off command to UPS");
            su_instcmd("load.off", "0");
            break;
      case 1:
            /* Send a combined set of shutdown commands which can work better */
            /* if the UPS gets power during shutdown process */
            /* Specifically it sends both the soft shutdown 'S' */
            /* and the powerdown after grace period - '@000' commands */
/*          upslogx(LOG_INFO, "UPS - sending shutdown/powerdown");
            if (pwr_status == g_pwr_battery)
                  su_ups_instcmd(CMD_SOFTDOWN, 0, 0);
            su_ups_instcmd(CMD_SDRET, 0, 0);
            break;
*/    
      default:
            /* if on battery... */
/*          if (pwr_status == su_find_valinfo(info_lkp_t *oid2info, "OB")) {
                  upslogx(LOG_INFO,
                        "UPS is on battery, sending shutdown command...");
                  su_ups_instcmd(CMD_SOFTDOWN, 0, 0);
            } else {
                  upslogx(LOG_INFO, "UPS is online, sending shutdown+return command...");
                  su_ups_instcmd(CMD_SDRET, 0, 0);
            }
*/
            break;
      }
}

/* return 1 if usable, 0 if not */
static int parse_mibconf_args(int numargs, char **arg)
{
      bool_t ret;
      
      /* everything below here uses up through arg[1] */
      if (numargs < 6)
            return 0;

      /* <info type> <info flags> <info len> <OID name> <default value> <value lookup> */

      /* special case for setting some OIDs value at driver startup */
      if (!strcmp(arg[0], "init")) {
            /* set value. */
            if (!strcmp(arg[1], "str")) {
                  ret = nut_snmp_set_str(arg[3], arg[4]);
            } else {
                  ret = nut_snmp_set_int(arg[3], strtol(arg[4], NULL, 0));
            }
      
            if (ret == FALSE)
                  upslogx(LOG_ERR, "su_setvar: cannot set value %s for %s", arg[4], arg[3]);
            else
                  upsdebugx(1, "su_setvar: sucessfully set %s to \"%s\"", arg[0], arg[4]);

            return 1;
      }

      /* TODO: create the lookup table */
      upsdebugx(2, "%s, %s, %s, %s, %s, %s", arg[0], arg[1], arg[2], arg[3], arg[4], arg[5]);

      return 1;
}
/* called for fatal errors in parseconf like malloc failures */
static void mibconf_err(const char *errmsg)
{
      upslogx(LOG_ERR, "Fatal error in parseconf (*mib.conf): %s", errmsg);
}
/* load *mib.conf into an snmp_info_t structure */
void read_mibconf(char *mib)
{
      char  fn[SMALLBUF];
      PCONF_CTX_t ctx;

      upsdebugx(2, "SNMP UPS driver : entering read_mibconf(%s)", mib);
      
      snprintf(fn, sizeof(fn), "%s/snmp/%s.conf", CONFPATH, mib);

      pconf_init(&ctx, mibconf_err);

      if (!pconf_file_begin(&ctx, fn))
            fatalx(EXIT_FAILURE, "%s", ctx.errmsg);

      while (pconf_file_next(&ctx)) {
            if (pconf_parse_error(&ctx)) {
                  upslogx(LOG_ERR, "Parse error: %s:%d: %s",
                        fn, ctx.linenum, ctx.errmsg);
                  continue;
            }

            if (ctx.numargs < 1)
                  continue;

            if (!parse_mibconf_args(ctx.numargs, ctx.arglist)) {
                  unsigned int      i;
                  char  errmsg[SMALLBUF];

                  snprintf(errmsg, sizeof(errmsg), 
                        "mib.conf: invalid directive");

                  for (i = 0; i < ctx.numargs; i++)
                        snprintfcat(errmsg, sizeof(errmsg), " %s", 
                              ctx.arglist[i]);

                  upslogx(LOG_WARNING, "%s", errmsg);
            }
      }
      pconf_finish(&ctx);           
}

Generated by  Doxygen 1.6.0   Back to index