/* $Id: main.c 275 2007-09-14 06:38:16Z fil $ */ /* Copyright 2007 Philip Gwyn. All rights reserved. Roughly based on apsmastersnmp.c Copyright (c) 2001 Andreas Piesk Roughly based on vacm.c Copyright 2000 Sistina Software, Inc. Tiny bits Copyright 2000 Alan Robertson Tiny bits Copyright 2000 Zac Sprackett, VA Linux Systems Tiny bits Copyright 2005 International Business Machines Significantly Mangled by Sun Jiang Dong , IBM, 2005 */ /* This STONITH plugin allows one to talk to an APC PDU via SNMP. See apcsnmp.c for description of the SNMP used. Fastest way to see if your PDU is supported: stonith -t apcsnmp community=apcread ipaddr=$IP -l Further testes: # NOTE! THIS WILL TURN OFF A PORT! I hope nothing is plugged in. stonith -t apcsnmp community=private ipaddr=$IP -T off "Outlet 7" # Now go see if the light turned off stonith -t apcsnmp community=private ipaddr=$IP -T on "Outlet 7" # Now go see if the light turned back on More fun: stonith -t apcsnmp community=private ipaddr=$IP -T on "Outlet *" # Will turn all the ports off, unless you renamed them. This plugin implements the ST_POWERON, ST_POWEROFF and ST_GENERIC_RESET. If the PDU's MIB doesn't have a RESET command, it will simulate ST_GENERIC_RESET with ST_POWERON and ST_POWEROFF. This means the reboot delays you carefully configured in the PDU aren't respected. This file is maintained by: Philip Gwyn */ /* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #undef FREE /* defined by snmp stuff */ #include #include #define DEVICE "APC SNMP" #define DOESNT_USE_STONITHKILLCOMM 1 #include "stonith_plugin_common.h" #define PIL_PLUGIN apcsnmp #define PIL_PLUGIN_S "apcsnmp" #define PIL_PLUGINLICENSE LICENSE_LGPL #define PIL_PLUGINLICENSEURL URL_LGPL #include #include "stonith_signal.h" #include "stonith_config_xml.h" #include "apcsnmp.h" #define DEBUGCALL \ if (Debug) { \ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); \ } /*******************************************************************/ /* structure of stonith object */ struct pluginDevice { StonithPlugin sp; /* StonithPlugin object */ const char *pluginid; /* id of object */ const char *idinfo; /* type of device */ host_t *host; /* information about the PDU */ unsigned int delay; /* time to sleep between off / on cycle */ char *outlet_name; /* name of outlet to modify */ }; /*******************************************************************/ static StonithPlugin *apcsnmp_new(const char *); static void apcsnmp_destroy(StonithPlugin *); static const char **apcsnmp_get_confignames(StonithPlugin *); static int apcsnmp_set_config(StonithPlugin *, StonithNVpair *); static const char * apcsnmp_getinfo(StonithPlugin * s, int InfoType); static int apcsnmp_status(StonithPlugin * ); static int apcsnmp_request(StonithPlugin * s, int request, const char * host); static char ** apcsnmp_hostlist(StonithPlugin *); static int set_config_port( struct pluginDevice *plugin, StonithNVpair *list ); static int set_config_delay( struct pluginDevice *plugin, StonithNVpair *list ); static int set_config_timeout( struct pluginDevice *plugin, StonithNVpair *list ); static int set_config_userPDU( struct pluginDevice *plugin, StonithNVpair *list ); static int set_config_userPDU_OID( const char **dest, const char *value, const char *name, int append ); static int set_config_userPDU_Ctl( const char **dest, const char *value, const char *name, const char *_default ); static int set_config_int( unsigned int *dest, StonithNVpair *list, const char *name, int min, int max ); static struct stonith_ops apcsnmpOps ={ apcsnmp_new, /* Create new STONITH object */ apcsnmp_destroy, /* Destroy STONITH object */ apcsnmp_getinfo, /* Return STONITH info string */ apcsnmp_get_confignames, /* Get configuration parameters */ apcsnmp_set_config, /* Set configuration */ apcsnmp_status, /* Return STONITH device status */ apcsnmp_request, /* Request a change in status */ apcsnmp_hostlist, /* Return list of supported hosts */ }; /*******************************************************************/ PIL_PLUGIN_BOILERPLATE2("1.0", Debug) const PILPluginImports* PluginImports; const PILPluginOps* PluginOps; static PILPlugin* OurPlugin; static PILInterface* OurInterface; static StonithImports* OurImports; static void* interfprivate; static const char * pluginid = "APC SNMP-Stonith"; static const char * NOTpluginid = "APC SNMP device has been destroyed"; /*******************************************************************/ #define my_FREE( s ) if( (s) != NULL ) FREE((s)); (s)=NULL #define my_PARM( name, s_desc, l_desc ) \ XML_PARAMETER_BEGIN(name, "string", "0" ) \ XML_PARM_SHORTDESC_BEGIN("en") \ s_desc \ XML_PARM_SHORTDESC_END \ XML_PARM_LONGDESC_BEGIN("en") \ l_desc \ XML_PARM_LONGDESC_END \ XML_PARAMETER_END /*******************************************************************/ #define ST_PORT "port" #define XML_PORT_PARM my_PARM( ST_PORT, "SNMP port", \ "The port number on which the SNMP server is running " \ "on the STONITH device. [Ignored by net-snmp]" ) #define ST_DELAY "delay" #define ST_DELAY_DEFAULT 60 #define XML_DELAY_PARM my_PARM( ST_DELAY, "Reboot delay", \ "If you do not define a ctlReset for the userPDU, a "\ "reset operation will be simulated by sending ctlOff, waiting " \ "this number of seconds, then sending ctlOn. " \ "Defaults to 60 seconds, which most motherboards are OK with." ) #define ST_TIMEOUT "timeout" #define ST_TIMEOUT_DEFAULT 90 #define XML_TIMEOUT_PARM my_PARM( ST_TIMEOUT, "Wait timeout", \ "Delay (in seconds) to wait for an outlet to change state. " \ "Defaults to 90 seconds, more then enough for the max RESET of 60 seconds APC allows." ) /*******************************************************************/ #define ST_SYSOID "sysOID" #define XML_SYSOID_PARM my_PARM( ST_SYSOID, "System OID", \ "sysOID used by the PDU" ) #define ST_OIDPORTS "oidPorts" #define XML_OIDPORTS_PARM my_PARM( ST_OIDPORTS, \ "Ports OID", \ "OID to find the number of outlets on the PDU" ) #define ST_OIDNAMES "oidNames" #define XML_OIDNAMES_PARM my_PARM( ST_OIDNAMES, \ "Names OID", \ "OID to find the name of an outlet on the PDU." \ " %i is replaced with the outlet's number." ) #define ST_OIDCTL "oidCtl" #define XML_OIDCTL_PARM my_PARM( ST_OIDCTL, \ "Control OID", \ "OID to control an outlet on the PDU." \ " %i is replaced with the outlet's number." ) #define ST_OIDSTATE "oidState" #define XML_OIDSTATE_PARM my_PARM( ST_OIDSTATE, \ "State OID", \ "OID to find the current state of an outlet on the PDU." \ " %i is replaced with the outlet's number." \ " Defaults to " ST_OIDCTL ) #define ST_OIDON "ctlOn" #define XML_OIDON_PARM my_PARM( ST_OIDON, \ "Control on", \ "Value to set " ST_OIDCTL " to turn an outlet on." \ " Defaults to 1." ) #define ST_OIDOFF "ctlOff" #define XML_OIDOFF_PARM my_PARM( ST_OIDOFF, \ "Control off", \ "Value to set " ST_OIDCTL " to turn an outlet off." \ " Defaults to 2." ) #define ST_OIDRESET "ctlReset" #define XML_OIDRESET_PARM my_PARM( ST_OIDRESET, \ "Control reset", \ "Value to set " ST_OIDRESET " to reboot an outlet." \ " Defaults to 3. [Currently unused]" ) /*******************************************************************/ static const char *apcsnmpXML = XML_PARAMETERS_BEGIN XML_IPADDR_PARM XML_PORT_PARM XML_COMMUNITY_PARM XML_DELAY_PARM XML_TIMEOUT_PARM XML_SYSOID_PARM XML_OIDPORTS_PARM XML_OIDNAMES_PARM XML_OIDSTATE_PARM XML_OIDCTL_PARM XML_OIDON_PARM XML_OIDOFF_PARM XML_OIDRESET_PARM XML_PARAMETERS_END; /*******************************************************************/ /* register this plugin */ PIL_rc PIL_PLUGIN_INIT( PILPlugin*us, const PILPluginImports* imports ) { DEBUGCALL; PluginOps = &OurPIExports; /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S, &apcsnmpOps, NULL, /*close */ &OurInterface, (void*)&OurImports, &interfprivate ); } /*******************************************************************/ /* Create a new instance of this plugin */ StonithPlugin *apcsnmp_new(const char *subplugin) { struct pluginDevice *plugin; DEBUGCALL; /* Freed in apcsnmp_destroy */ plugin = MALLOC( sizeof( struct pluginDevice ) ); if( plugin == NULL ) { LOG( PIL_CRIT, "out of memory" ); return NULL; } memset( plugin, 0, sizeof(*plugin) ); plugin->sp.s_ops = &apcsnmpOps; plugin->pluginid = pluginid; plugin->idinfo = DEVICE; plugin->delay = ST_DELAY_DEFAULT; /* Freed in apcsnmp_destroy */ plugin->host = MALLOC( sizeof( host_t ) ); if( plugin->host == NULL ) { LOG( PIL_CRIT, "out of memory" ); return NULL; } memset( plugin->host, 0, sizeof( host_t ) ); plugin->host->timeout = ST_TIMEOUT_DEFAULT; /* nothing is set to NULL because the memset()s does it */ /* return the object */ return &(plugin->sp); } /*******************************************************************/ /* Tear down this instance of a plugin */ static void apcsnmp_destroy(StonithPlugin *s) { struct pluginDevice *plugin; DEBUGCALL; VOIDERRIFWRONGDEV(s); plugin = (struct pluginDevice*)s; if( plugin->host != NULL ) { my_FREE( plugin->host->name ); my_FREE( plugin->host->community ); host_deinit( plugin->host ); my_FREE( plugin->host ); } my_FREE( userPDU.sysOID ); my_FREE( userPDU.OID->outletPorts ); my_FREE( userPDU.OID->outletNames ); my_FREE( userPDU.OID->outletCtl ); my_FREE( userPDU.OID->outletState ); my_FREE( userPDU.OID->ctlOn ); my_FREE( userPDU.OID->ctlOff ); my_FREE( userPDU.OID->ctlReset ); plugin->pluginid = NOTpluginid; FREE( plugin ); } /*******************************************************************/ /* Get the names we want to find in the config file Stonith uses this to a- loading config file b- inform the user. So presence on this list doesn't me it is required. */ static const char **apcsnmp_get_confignames(StonithPlugin *s) { static const char *ret[] = { ST_IPADDR, ST_COMMUNITY, ST_PORT, ST_DELAY, ST_TIMEOUT, ST_SYSOID, ST_OIDPORTS, ST_OIDNAMES, ST_OIDSTATE, ST_OIDCTL, ST_OIDON, ST_OIDOFF, ST_OIDRESET, NULL }; DEBUGCALL; return ret; } /*******************************************************************/ /* Set the configuration */ static int apcsnmp_set_config(StonithPlugin *s, StonithNVpair *list) { int rc; struct pluginDevice *plugin = (struct pluginDevice*)s; StonithNamesToGet namestocopy[] = { { ST_IPADDR, NULL }, { ST_COMMUNITY, NULL }, { NULL, NULL }, }; DEBUGCALL; ERRIFWRONGDEV( s, S_OOPS ); if( plugin->sp.isconfigured ) { return S_OOPS; } /* Freed in apcsnmp_destroy */ rc = OurImports->CopyAllValues( namestocopy, list ); if( rc != S_OK ) return rc; /* The IP address */ plugin->host->name = namestocopy[0].s_value; if( Debug ) LOG( PIL_DEBUG, "host=%s", plugin->host->name ); /* The SNMP community */ plugin->host->community = namestocopy[1].s_value; if( Debug ) LOG( PIL_DEBUG, "community=%s", plugin->host->community ); /* The port (not required) */ rc = set_config_port( plugin, list ); if( rc != S_OK ) return rc; /* The timeout (not required) */ rc = set_config_timeout( plugin, list ); if( rc != S_OK ) return rc; /* The user PDU definition (not required) */ rc = set_config_userPDU( plugin, list ); if( rc != S_OK ) return rc; /* The delay (not required) */ rc = set_config_delay( plugin, list ); if( rc != S_OK ) return rc; /* Now set up our NET-SNMP session */ rc = host_init( plugin->host ); if( rc == APC_OK ) { rc = S_OK; } else { rc = S_BADCONFIG; } return rc; } /*******************************************************************/ /* Get the SNMP port number from the config */ static int set_config_port( struct pluginDevice *plugin, StonithNVpair *list ) { int port; const char *param = OurImports->GetValue( list, ST_PORT ); DEBUGCALL; if( param == NULL ) return S_OK; errno = 0; port = strtol( param, NULL, 10 ); if( errno ) { LOG( PIL_CRIT, "Port is not a number %s", param ); return S_BADCONFIG; } if( port < 0 ) { LOG( PIL_CRIT, "Port number must be greater then zero" ); return S_BADCONFIG; } if( port > 32767 ) { LOG( PIL_CRIT, "Port number must be less then 32768" ); return S_BADCONFIG; } plugin->host->port = port; if( Debug ) LOG( PIL_DEBUG, "port=%i", plugin->host->port ); return S_OK; } /*******************************************************************/ /* Get the reset delay from the config */ static int set_config_delay( struct pluginDevice *plugin, StonithNVpair *list ) { int rc; const char *param = OurImports->GetValue( list, ST_DELAY ); DEBUGCALL; if( param == NULL ) return S_OK; if( userPDU.OID->ctlReset != NULL ) { LOG( PIL_INFO, "The delay=%s won't be used if you specify ctlReset", param ); } rc = set_config_int( &plugin->delay, list, ST_DELAY, 0, 5*60 ); if( rc != S_OK ) return rc; if( Debug ) LOG( PIL_DEBUG, "%s=%i", ST_DELAY, plugin->delay ); return S_OK; } /*******************************************************************/ /* Get the timeout delay from the config */ static int set_config_timeout( struct pluginDevice *plugin, StonithNVpair *list ) { int rc; DEBUGCALL; rc = set_config_int( &plugin->host->timeout, list, ST_TIMEOUT, -1, 5*60 ); if( rc != S_OK ) return rc; if( Debug ) LOG( PIL_DEBUG, "%s=%i", ST_TIMEOUT, plugin->host->timeout ); return S_OK; } /*******************************************************************/ static int set_config_int( unsigned int *dest, StonithNVpair *list, const char *name, int min, int max ) { int value; const char *param = OurImports->GetValue( list, name ); if( param == NULL ) return S_OK; errno = 0; value = strtol( param, NULL, 10 ); if( errno ) { LOG( PIL_CRIT, "Parameter %s is not a number (%s)", name, param ); return S_BADCONFIG; } if( value < min ) { LOG( PIL_WARN, "A %s below %i is not allowed (%i)", name, min, value ); return S_BADCONFIG; } *dest = (unsigned int)value; if( *dest > max ) { LOG( PIL_WARN, "A %s above %i seconds is probably excessive", name, *dest ); } return S_OK; } /*******************************************************************/ /* Get the user PDU definition */ static int set_config_userPDU( struct pluginDevice *plugin, StonithNVpair *list ) { int rc; const char *param; StonithNamesToGet namestocopy[] = { { ST_OIDPORTS, NULL }, { ST_OIDNAMES, NULL }, { ST_OIDCTL, NULL }, { NULL, NULL }, }; DEBUGCALL; /* Get the OID of the user PDU */ param = OurImports->GetValue( list, ST_SYSOID ); if( param == NULL ) /* without it, we ignore all the others */ return S_OK; rc = set_config_userPDU_OID( &userPDU.sysOID, param, ST_SYSOID, 0 ); if( rc != S_OK ) return S_OK; if( Debug ) LOG( PIL_DEBUG, "%s.%s=%s", userPDU.name, ST_SYSOID, userPDU.sysOID ); /* Get the 3 required OIDs */ rc = OurImports->CopyAllValues( namestocopy, list ); if( rc != S_OK ) { LOG( PIL_CRIT, "If you set " ST_SYSOID " you must also set " ST_OIDPORTS ", " ST_OIDNAMES " and " ST_OIDCTL ); return rc; } /* Ports OID */ rc = set_config_userPDU_OID( &userPDU.OID->outletPorts, namestocopy[0].s_value, ST_OIDPORTS, 0 ); FREE(namestocopy[0].s_value); if( rc != S_OK ) return rc; if( Debug ) LOG( PIL_DEBUG, "%s.%s=%s", userPDU.name, ST_OIDPORTS, userPDU.OID->outletPorts ); /* Names OID */ rc = set_config_userPDU_OID( &userPDU.OID->outletNames, namestocopy[1].s_value, ST_OIDNAMES, 1 ); FREE(namestocopy[1].s_value); if( rc != S_OK ) return rc; if( Debug ) LOG( PIL_DEBUG, "%s.%s=%s", userPDU.name, ST_OIDNAMES, userPDU.OID->outletNames ); /* Control OID */ rc = set_config_userPDU_OID( &userPDU.OID->outletCtl, namestocopy[2].s_value, ST_OIDCTL, 1 ); FREE(namestocopy[2].s_value); if( rc != S_OK ) return rc; if( Debug ) LOG( PIL_DEBUG, "%s.%s=%s", userPDU.name, ST_OIDCTL, userPDU.OID->outletCtl ); /* outletState defaults to outletCtl */ param = OurImports->GetValue( list, ST_OIDSTATE ); if( param == NULL ) { param = userPDU.OID->outletCtl; } rc = set_config_userPDU_OID( &userPDU.OID->outletState, param, ST_OIDSTATE, 1 ); if( rc != S_OK ) return rc; if( Debug ) LOG( PIL_DEBUG, "%s.%s=%s", userPDU.name, ST_OIDSTATE, userPDU.OID->outletState ); /* Non-required ctlOn */ param = OurImports->GetValue( list, ST_OIDON ); rc = set_config_userPDU_Ctl( &userPDU.OID->ctlOn, param, ST_OIDON, "1" ); if( rc != S_OK ) return rc; if( Debug ) LOG( PIL_DEBUG, "%s.%s=%s", userPDU.name, ST_OIDON, userPDU.OID->ctlOn ); /* Non-required ctlOff */ param = OurImports->GetValue( list, ST_OIDOFF ); rc = set_config_userPDU_Ctl( &userPDU.OID->ctlOff, param, ST_OIDOFF, "2" ); if( rc != S_OK ) return rc; if( Debug ) LOG( PIL_DEBUG, "%s.%s=%s", userPDU.name, ST_OIDOFF, userPDU.OID->ctlOff ); /* Non-required ctlReset (if not specified, use 2 + 1) */ param = OurImports->GetValue( list, ST_OIDRESET ); rc = set_config_userPDU_Ctl( &userPDU.OID->ctlReset, param, ST_OIDRESET, NULL ); if( rc != S_OK ) return rc; if( Debug ) LOG( PIL_DEBUG, "%s.%s=%s", userPDU.name, ST_OIDRESET, userPDU.OID->ctlReset ? userPDU.OID->ctlReset : "(simulated)" ); return S_OK; } /*******************************************************************/ static int set_config_userPDU_OID( const char **dest, const char *value, const char *name, int append ) { char *full_oid, *found; int l, need; /* Copy the string */ l = strlen( value ); need = l + 1; if( append ) need += 3; /* + .%i */ /* Freed in apcsnmp_destroy */ full_oid = MALLOC( need ); if( full_oid == NULL ) { LOG( PIL_CRIT, "Out of memory for %i bytes", need ); return S_OOPS; } strncpy( full_oid, value, l+1 ); if( append ) { /* Append a .%i if there isn't one already */ found = strstr( full_oid, ".%i" ); if( found == NULL ) { if( full_oid[l-1] == '.' ) /* we want to allow ....3.4. */ full_oid[l-1] = '\0'; strncat( full_oid, ".%i", 3 ); } } /* TODO : verify the OID is well-formed */ *dest = full_oid; return S_OK; } /*******************************************************************/ static int set_config_userPDU_Ctl( const char **dest, const char *value, const char *name, const char *default_value ) { char *full_oid; int l, need; if( value == NULL ) { value = default_value; } if( value == NULL ) { return S_OK; } /* Copy the string */ l = strlen( value ); need = l + 1; /* Freed in apcsnmp_destroy */ full_oid = MALLOC( need ); if( full_oid == NULL ) { LOG( PIL_CRIT, "Out of memory for %i bytes", l ); return S_OOPS; } strncpy( full_oid, value, l ); /* TODO : verify the OID is well-formed */ *dest = full_oid; return S_OK; } /*******************************************************************/ /* Get information about this instance of the plugin */ static const char * apcsnmp_getinfo(StonithPlugin *s, int type) { struct pluginDevice *plugin = (struct pluginDevice *)s; const char *ret = NULL; DEBUGCALL; ERRIFWRONGDEV(s, NULL); switch( type ) { case ST_DEVICEID: ret = plugin->idinfo; break; case ST_DEVICENAME: ret = plugin->host->name; break; case ST_DEVICEDESCR: ret = "APC MasterSwitch via SNMP\n" "Supports both AP96xx (MasterSwitchV2) and AP79xx (MasterSwitchrPDU)"; break; case ST_DEVICEURL: ret = "http://www.apcc.com/"; break; case ST_CONF_XML: ret = apcsnmpXML; break; } return ret; } /*******************************************************************/ /* Return the status for this device */ static int apcsnmp_status(StonithPlugin *s) { struct pluginDevice *plugin = (struct pluginDevice *)s; int rc; DEBUGCALL; ERRIFNOTCONFIGED( s, S_OOPS ); rc = host_identify( plugin->host ); if( APC_OK != rc ) { return S_ACCESS; } return S_OK; } /*******************************************************************/ /* get a list of all hosts (that is, plugins) that we control */ static char **apcsnmp_hostlist(StonithPlugin *s) { struct pluginDevice *plugin = (struct pluginDevice *)s; char **outlets; int l, o_l, q, w, rc; DEBUGCALL; ERRIFNOTCONFIGED( s, NULL ); /* make sure the host is identified */ if( plugin->host->PDU == NULL ) { rc = host_identify( plugin->host ); if( APC_OK != rc || plugin->host->PDU == NULL ) { LOG( PIL_CRIT, "Failed to identify %s", plugin->host->name ); return NULL; } } /* count the number of outlets */ if( plugin->host->PDU->outlets == 0 ) { rc = host_outlets( plugin->host ); if( rc == APC_ERROR ) { LOG( PIL_CRIT, "Error getting number of ports from %s", plugin->host->name ); return NULL; } if( plugin->host->PDU->outlets == 0 ) { LOG( PIL_CRIT, "Failed to get number of ports from %s", plugin->host->name ); return NULL; } } /* get array for the array of strings */ l = (plugin->host->PDU->outlets+1) * sizeof(char *); /* Freed by stonith */ outlets = (char **)MALLOC( l ); if( outlets == NULL) { LOG( PIL_CRIT, "%s: out of memory.", __FUNCTION__ ); return NULL; } memset( outlets, 0, l ); o_l = 0; for( q = 1; q <= plugin->host->PDU->outlets ; q++ ) { char name[MAX_STRING]; rc = host_outlet_name( plugin->host, q, name ); if( rc == APC_OK ) { /* make sure the outlet only appears in the list once */ for( w = 0; w < o_l; w++ ) { if( strcasecmp( outlets[ w ], name ) == 0 ) break; } /* It's a new one */ if( w >= o_l ) { /* Freed by stonith */ outlets[ o_l ] = STRDUP( name ); if( outlets[ o_l ] == NULL ) { LOG( PIL_CRIT, "out of %s", "memory" ); stonith_free_hostlist( outlets ); return NULL; } g_strdown( outlets[o_l] ); o_l++; } } } if(Debug) { LOG( PIL_DEBUG, "Found %i unique outlets on %s", o_l, plugin->host->name ); } return outlets; } /*******************************************************************/ /* Return the list of hosts configured for this device The name of an oulet must be the name of the node attached. This is how STONITH matches node names to outlets. */ static int apcsnmp_request(StonithPlugin *s, int request, const char *host) { struct pluginDevice *plugin = (struct pluginDevice *)s; int rc = APC_ERROR; DEBUGCALL; ERRIFNOTCONFIGED( s, S_OOPS ); /* make sure the host is identified */ if( plugin->host->PDU == NULL ) { rc = host_identify( plugin->host ); if( APC_OK != rc || plugin->host->PDU == NULL ) { LOG( PIL_CRIT, "Failed to identify %s", plugin->host->name ); return S_BADHOST; } rc = APC_ERROR; } switch(request) { #ifdef ST_POWERON case ST_POWERON: rc = host_power( plugin->host, host, APC_OP_ON ); break; #endif #ifdef ST_POWEROFF case ST_POWEROFF: rc = host_power( plugin->host, host, APC_OP_OFF ); break; #endif case ST_GENERIC_RESET: if( plugin->host->PDU->OID->ctlReset == NULL ) { LOG( PIL_INFO, "%s simulating a RESET with OFF+ON", plugin->host->sysName ); rc = host_power( plugin->host, host, APC_OP_OFF ); if( rc == APC_OK ) { LOG( PIL_INFO, "%s waiting %i seconds", plugin->host->sysName, plugin->delay ); sleep( plugin->delay ); rc = host_power( plugin->host, host, APC_OP_ON ); } } else { rc = host_power( plugin->host, host, APC_OP_RESET ); } break; default: return S_INVAL; } if( rc == APC_OK ) return S_OK; else if( rc == APC_TIMEOUT ) return S_TIMEOUT; else return S_RESETFAIL; }