/* 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;
}
sprintf(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;
}
sprintf(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);
}
syntax highlighted by Code2HTML, v. 0.9.1