You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
heartbeat/lib/plugins/stonith/apcsmart.c

1029 lines
23 KiB

/*
* Stonith module for APCSmart Stonith device
* Copyright (c) 2000 Andreas Piesk <a.piesk@gmx.net>
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Original version of this UPS code was taken from:
* 'Network UPS Tools' by Russell Kroll <rkroll@exploits.org>
* homepage: http://www.networkupstools.org/
*
* Significantly mangled by Alan Robertson <alanr@unix.sh>
*/
#include <lha_internal.h>
#define DEVICE "APCSmart"
#include "stonith_plugin_common.h"
/*
* APCSmart (tested with old 900XLI, APC SmartUPS 700 and SmartUPS-1000)
*
* The reset is a combined reset: "S" and "@000"
* The "S" command tells the ups that if it is on-battery, it should
* remain offline until the power is back.
* If that command is not accepted, the "@000" command will be sent
* to tell the ups to turn off and back on right away.
* In both cases, if the UPS supports a 20 second shutdown grace
* period (such as on the 900XLI), the shutdown will delay that long,
* otherwise the shutdown will happen immediately (the code searches
* for the smallest possible delay).
*/
#define CFG_FILE "/etc/ha.d/apcsmart.cfg"
#define MAX_DEVICES 1
#define SERIAL_TIMEOUT 3 /* timeout in sec */
#define SEND_DELAY 50000 /* in microseconds */
#define ENDCHAR 10 /* use LF */
#define MAX_STRING 512
#define MAX_DELAY_STRING 16
#define SWITCH_TO_NEXT_VAL "-" /* APC cmd for cycling through
* the values
*/
#define CMD_SMART_MODE "Y"
#define RSP_SMART_MODE "SM"
#define CMD_GET_STATUS "Q"
#define RSP_GET_STATUS NULL
#define CMD_RESET "S" /* turn off & stay off if on battery */
#define CMD_RESET2 "@000" /* turn off & immediately turn on */
#define RSP_RESET "*" /* RESET response from older models */
#define RSP_RESET2 "OK" /* RESET response from newer models */
#define RSP_NA "NA"
#define CMD_READREG1 "~"
#define CMD_OFF "Z"
#define CMD_ON "\016" /* (control-n) */
#define CMD_SHUTDOWN_DELAY "p"
#define CMD_WAKEUP_DELAY "r"
#define CR 13
struct pluginDevice {
StonithPlugin sp;
const char * pluginid; /* of object */
const char * idinfo; /* type of device */
char ** hostlist; /* served by the device (only 1) */
int hostcount;/* of hosts (1) */
char * upsdev; /* */
int upsfd; /* for serial port */
int retries;
char shutdown_delay[MAX_DELAY_STRING];
char old_shutdown_delay[MAX_DELAY_STRING];
char wakeup_delay[MAX_DELAY_STRING];
char old_wakeup_delay[MAX_DELAY_STRING];
};
/* saving old settings */
/* FIXME! These should be part of pluginDevice struct above */
static struct termios old_tio;
static int f_serialtimeout; /* flag for timeout */
static const char *pluginid = "APCSmart-Stonith";
static const char *NOTpluginID = "APCSmart device has been destroyed";
/*
* stonith prototypes
*/
#define PIL_PLUGIN apcsmart
#define PIL_PLUGIN_S "apcsmart"
#define PIL_PLUGINLICENSE LICENSE_LGPL
#define PIL_PLUGINLICENSEURL URL_LGPL
#include <pils/plugin.h>
#include "stonith_signal.h"
static StonithPlugin * apcsmart_new(const char *);
static void apcsmart_destroy(StonithPlugin *);
static const char** apcsmart_get_confignames(StonithPlugin*);
static int apcsmart_set_config(StonithPlugin *, StonithNVpair*);
static const char * apcsmart_get_info(StonithPlugin * s, int InfoType);
static int apcsmart_status(StonithPlugin * );
static int apcsmart_reset_req(StonithPlugin * s, int request, const char * host);
static char ** apcsmart_hostlist(StonithPlugin *);
static struct stonith_ops apcsmartOps ={
apcsmart_new, /* Create new STONITH object */
apcsmart_destroy, /* Destroy STONITH object */
apcsmart_get_info, /* Return STONITH info string */
apcsmart_get_confignames, /* Return STONITH info string */
apcsmart_set_config, /* Get configuration from NVpairs */
apcsmart_status, /* Return STONITH device status */
apcsmart_reset_req, /* Request a reset */
apcsmart_hostlist, /* Return list of supported hosts */
};
PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
static const PILPluginImports* PluginImports;
static PILPlugin* OurPlugin;
static PILInterface* OurInterface;
static StonithImports* OurImports;
static void* interfprivate;
PIL_rc
PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
PIL_rc
PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
{
/* 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
, &apcsmartOps
, NULL /*close */
, &OurInterface
, (void*)&OurImports
, &interfprivate);
}
#include "stonith_config_xml.h"
static const char *apcsmartXML =
XML_PARAMETERS_BEGIN
XML_TTYDEV_PARM
XML_HOSTLIST_PARM
XML_PARAMETERS_END;
/*
* own prototypes
*/
int APC_open_serialport(const char *port, speed_t speed);
void APC_close_serialport(const char *port, int upsfd);
void APC_sh_serial_timeout(int sig);
int APC_send_cmd(int upsfd, const char *cmd);
int APC_recv_rsp(int upsfd, char *rsp);
int APC_enter_smartmode(int upsfd);
int APC_set_ups_var(int upsfd, const char *cmd, char *newval);
int APC_get_smallest_delay(int upsfd, const char *cmd, char *smdelay);
int APC_init( struct pluginDevice *ad );
void APC_deinit( struct pluginDevice *ad );
/*
* Signal handler for serial port timeouts
*/
void
APC_sh_serial_timeout(int sig)
{
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
STONITH_IGNORE_SIG(SIGALRM);
if (Debug) {
LOG(PIL_DEBUG, "%s: serial port timed out.", __FUNCTION__);
}
f_serialtimeout = TRUE;
return;
}
/*
* Open serial port and set it to b2400
*/
int
APC_open_serialport(const char *port, speed_t speed)
{
struct termios tio;
int fd;
int rc;
int errno_save;
int fflags;
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
if ((rc = OurImports->TtyLock(port)) < 0) {
LOG(PIL_CRIT, "%s: Could not lock tty %s [rc=%d]."
, __FUNCTION__, port, rc);
return -1;
}
STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout);
alarm(SERIAL_TIMEOUT);
f_serialtimeout = FALSE;
fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK | O_EXCL);
errno_save = errno;
alarm(0);
STONITH_IGNORE_SIG(SIGALRM);
if (fd < 0) {
LOG(PIL_CRIT, "%s: Open of %s %s [%s].", __FUNCTION__
, port
, f_serialtimeout ? "timed out" : "failed"
, strerror(errno_save));
OurImports->TtyUnlock(port);
return -1;
}
if ((fflags = fcntl(fd, F_GETFL)) < 0
|| fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
LOG(PIL_CRIT, "%s: Setting flags on %s failed [%s]."
, __FUNCTION__
, port
, strerror(errno_save));
close(fd);
OurImports->TtyUnlock(port);
return -1;
}
if (tcgetattr(fd, &old_tio) < 0) {
LOG(PIL_CRIT, "%s: tcgetattr of %s failed [%s].", __FUNCTION__
, port
, strerror(errno));
close(fd);
OurImports->TtyUnlock(port);
return -1;
}
memcpy(&tio, &old_tio, sizeof(struct termios));
tio.c_cflag = CS8 | CLOCAL | CREAD;
tio.c_iflag = IGNPAR;
tio.c_oflag = 0;
tio.c_lflag = 0;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
cfsetispeed(&tio, speed);
cfsetospeed(&tio, speed);
tcflush(fd, TCIOFLUSH);
tcsetattr(fd, TCSANOW, &tio);
return (fd);
}
/*
* Close serial port and restore old settings
*/
void
APC_close_serialport(const char *port, int upsfd)
{
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
if (upsfd < 0) {
return;
}
tcflush(upsfd, TCIFLUSH);
tcsetattr(upsfd, TCSANOW, &old_tio);
close(upsfd);
if (port != NULL) {
OurImports->TtyUnlock(port);
}
}
/*
* Send a command to the ups
*/
int
APC_send_cmd(int upsfd, const char *cmd)
{
int i;
if (Debug) {
LOG(PIL_DEBUG, "%s(\"%s\")", __FUNCTION__, cmd);
}
tcflush(upsfd, TCIFLUSH);
for (i = strlen(cmd); i > 0; i--) {
if (write(upsfd, cmd++, 1) != 1) {
return (S_ACCESS);
}
usleep(SEND_DELAY);
}
return (S_OK);
}
/*
* Get the response from the ups
*/
int
APC_recv_rsp(int upsfd, char *rsp)
{
char *p = rsp;
char inp;
int num = 0;
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
*p = '\0';
STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout);
alarm(SERIAL_TIMEOUT);
while (num < MAX_STRING) {
if (read(upsfd, &inp, 1) == 1) {
/* shutdown sends only a '*' without LF */
if ((inp == '*') && (num == 0)) {
*p++ = inp;
num++;
inp = ENDCHAR;
}
if (inp == ENDCHAR) {
alarm(0);
STONITH_IGNORE_SIG(SIGALRM);
*p = '\0';
if (Debug) {
LOG(PIL_DEBUG, "return(\"%s\")/*%s*/;"
, rsp, __FUNCTION__);
}
return (S_OK);
}
if (inp != CR) {
*p++ = inp;
num++;
}
}else{
alarm(0);
STONITH_IGNORE_SIG(SIGALRM);
*p = '\0';
LOG(PIL_DEBUG, "%s: %s.", __FUNCTION__,
f_serialtimeout ? "timeout" :
"can't access device" );
return (f_serialtimeout ? S_TIMEOUT : S_ACCESS);
}
}
return (S_ACCESS);
}
/*
* Enter smart mode
*/
int
APC_enter_smartmode(int upsfd)
{
int rc;
char resp[MAX_STRING];
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
strcpy(resp, RSP_SMART_MODE);
if (((rc = APC_send_cmd(upsfd, CMD_SMART_MODE)) == S_OK)
&& ((rc = APC_recv_rsp(upsfd, resp)) == S_OK)
&& (strcmp(RSP_SMART_MODE, resp) == 0)) {
return (S_OK);
}
return (S_ACCESS);
}
/*
* Set a value in the hardware using the <cmdchar> '-' (repeat) approach
*/
int
APC_set_ups_var(int upsfd, const char *cmd, char *newval)
{
char resp[MAX_STRING];
char orig[MAX_STRING];
int rc;
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
|| ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
|| ((rc = APC_recv_rsp(upsfd, orig)) != S_OK)) {
return (rc);
}
if (Debug) {
LOG(PIL_DEBUG, "%s: var '%s' original val %s"
, __FUNCTION__, cmd, orig);
}
if (strcmp(orig, newval) == 0) {
return (S_OK); /* already set */
}
*resp = '\0';
while (strcmp(resp, orig) != 0) {
if (((rc = APC_send_cmd(upsfd, SWITCH_TO_NEXT_VAL)) != S_OK)
|| ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
return (rc);
}
if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
|| ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
|| ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
return (rc);
}
if (strcmp(resp, newval) == 0) {
if (Debug) {
LOG(PIL_DEBUG, "%s: var '%s' set to %s"
, __FUNCTION__, cmd, newval);
}
strcpy(newval, orig); /* return the old value */
return (S_OK); /* got it */
}
}
LOG(PIL_CRIT, "%s(): Could not set variable '%s' to %s!"
, __FUNCTION__, cmd, newval);
LOG(PIL_CRIT, "%s(): This UPS may not support STONITH :-("
, __FUNCTION__);
return (S_OOPS);
}
/*
* Query the smallest delay supported by the hardware using the
* <cmdchar> '-' (repeat) approach and looping through all possible values,
* saving the smallest
*/
int
APC_get_smallest_delay(int upsfd, const char *cmd, char *smdelay)
{
char resp[MAX_DELAY_STRING];
char orig[MAX_DELAY_STRING];
int delay, smallest;
int rc;
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
|| ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
|| ((rc = APC_recv_rsp(upsfd, orig)) != S_OK)) {
return (rc);
}
smallest = atoi(orig);
strcpy(smdelay, orig);
*resp = '\0';
/* search for smallest delay; need to loop through all possible
* values so that we leave delay the way we found it */
while (strcmp(resp, orig) != 0) {
if (((rc = APC_send_cmd(upsfd, SWITCH_TO_NEXT_VAL)) != S_OK)
|| ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
return (rc);
}
if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
|| ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
|| ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
return (rc);
}
if ((delay = atoi(resp)) < smallest) {
smallest = delay;
strcpy(smdelay, resp);
}
}
return (S_OK);
}
/*
* Initialize the ups
*/
int
APC_init(struct pluginDevice *ad)
{
int upsfd;
char value[MAX_DELAY_STRING];
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
/* if ad->upsfd != -1 device has already been configured. */
/* Just enter smart mode again because otherwise a SmartUPS-1000 */
/* has been observed to sometimes not respond. */
if(ad->upsfd >= 0) {
if(APC_enter_smartmode(ad->upsfd) != S_OK) {
return(S_OOPS);
}
return S_OK;
}
/* open serial port and store the fd in ad->upsfd */
if ((upsfd = APC_open_serialport(ad->upsdev, B2400)) == -1) {
return S_OOPS;
}
/* switch into smart mode */
if (APC_enter_smartmode(upsfd) != S_OK) {
APC_close_serialport(ad->upsdev, upsfd);
ad->upsfd = -1;
return S_OOPS;
}
/* get the smallest possible delays for this particular hardware */
if (APC_get_smallest_delay(upsfd, CMD_SHUTDOWN_DELAY
, ad->shutdown_delay) != S_OK
|| APC_get_smallest_delay(upsfd, CMD_WAKEUP_DELAY
, ad->wakeup_delay) != S_OK) {
LOG(PIL_CRIT, "%s: couldn't retrieve smallest delay from UPS"
, __FUNCTION__);
APC_close_serialport(ad->upsdev, upsfd);
ad->upsfd = -1;
return S_OOPS;
}
/* get the old settings and store them */
strcpy(value, ad->shutdown_delay);
if (APC_set_ups_var(upsfd, CMD_SHUTDOWN_DELAY, value) != S_OK) {
LOG(PIL_CRIT, "%s: couldn't set shutdown delay to %s"
, __FUNCTION__, ad->shutdown_delay);
APC_close_serialport(ad->upsdev, upsfd);
ad->upsfd = -1;
return S_OOPS;
}
strcpy(ad->old_shutdown_delay, value);
strcpy(value, ad->wakeup_delay);
if (APC_set_ups_var(upsfd, CMD_WAKEUP_DELAY, value) != S_OK) {
LOG(PIL_CRIT, "%s: couldn't set wakeup delay to %s"
, __FUNCTION__, ad->wakeup_delay);
APC_close_serialport(ad->upsdev, upsfd);
ad->upsfd = -1;
return S_OOPS;
}
strcpy(ad->old_wakeup_delay, value);
ad->upsfd = upsfd;
return S_OK;
}
/*
* Restore original settings and close the port
*/
void
APC_deinit(struct pluginDevice *ad)
{
APC_enter_smartmode( ad->upsfd );
APC_set_ups_var(ad->upsfd, CMD_SHUTDOWN_DELAY, ad->old_shutdown_delay);
APC_set_ups_var(ad->upsfd, CMD_WAKEUP_DELAY, ad->old_wakeup_delay);
/* close serial port */
if (ad->upsfd >= 0) {
APC_close_serialport(ad->upsdev, ad->upsfd);
ad->upsfd = -1;
}
}
static const char**
apcsmart_get_confignames(StonithPlugin* sp)
{
static const char * names[] = {ST_TTYDEV, ST_HOSTLIST, NULL};
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
return names;
}
/*
* Stash away the config info we've been given...
*/
static int
apcsmart_set_config(StonithPlugin * s, StonithNVpair* list)
{
struct pluginDevice * ad = (struct pluginDevice*)s;
StonithNamesToGet namestocopy [] =
{ {ST_TTYDEV, NULL}
, {ST_HOSTLIST, NULL}
, {NULL, NULL}
};
int rc;
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
ERRIFWRONGDEV(s, S_OOPS);
if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
return rc;
}
ad->upsdev = namestocopy[0].s_value;
ad->hostlist = OurImports->StringToHostList(namestocopy[1].s_value);
FREE(namestocopy[1].s_value);
if (ad->hostlist == NULL) {
LOG(PIL_CRIT,"StringToHostList() failed");
return S_OOPS;
}
for (ad->hostcount = 0; ad->hostlist[ad->hostcount]
; ad->hostcount++) {
g_strdown(ad->hostlist[ad->hostcount]);
}
if (access(ad->upsdev, R_OK|W_OK|F_OK) < 0) {
LOG(PIL_CRIT,"Cannot access tty [%s]", ad->upsdev);
return S_BADCONFIG;
}
return ad->hostcount ? S_OK : S_BADCONFIG;
}
/*
* return the status for this device
*/
static int
apcsmart_status(StonithPlugin * s)
{
struct pluginDevice *ad = (struct pluginDevice *) s;
char resp[MAX_STRING];
int rc;
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
ERRIFNOTCONFIGED(s,S_OOPS);
/* get status */
if (((rc = APC_init( ad )) == S_OK)
&& ((rc = APC_send_cmd(ad->upsfd, CMD_GET_STATUS)) == S_OK)
&& ((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK)) {
return (S_OK); /* everything ok. */
}
if (Debug) {
LOG(PIL_DEBUG, "%s: failed, rc=%d.", __FUNCTION__, rc);
}
return (rc);
}
/*
* return the list of hosts configured for this device
*/
static char **
apcsmart_hostlist(StonithPlugin * s)
{
struct pluginDevice *ad = (struct pluginDevice *) s;
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
ERRIFNOTCONFIGED(s,NULL);
return OurImports->CopyHostList((const char **)ad->hostlist);
}
static gboolean
apcsmart_RegisterBitsSet(struct pluginDevice * ad, int nreg, unsigned bits
, gboolean* waserr)
{
const char* reqregs[4] = {"?", "~", "'", "8"};
unsigned regval;
char resp[MAX_STRING];
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
if (APC_enter_smartmode(ad->upsfd) != S_OK
|| APC_send_cmd(ad->upsfd, reqregs[nreg]) != S_OK
|| APC_recv_rsp(ad->upsfd, resp) != S_OK
|| (sscanf(resp, "%02x", &regval) != 1)) {
if (waserr){
*waserr = TRUE;
}
return FALSE;
}
if (waserr){
*waserr = FALSE;
}
return ((regval & bits) == bits);
}
#define apcsmart_IsPoweredOff(ad, err) apcsmart_RegisterBitsSet(ad,1,0x40,err)
#define apcsmart_ResetHappening(ad,err) apcsmart_RegisterBitsSet(ad,3,0x08,err)
static int
apcsmart_ReqOnOff(struct pluginDevice * ad, int request)
{
const char * cmdstr;
int rc;
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
cmdstr = (request == ST_POWEROFF ? CMD_OFF : CMD_ON);
/* enter smartmode, send on/off command */
if ((rc =APC_enter_smartmode(ad->upsfd)) != S_OK
|| (rc = APC_send_cmd(ad->upsfd, cmdstr)) != S_OK) {
return rc;
}
sleep(2);
if ((rc = APC_send_cmd(ad->upsfd, cmdstr)) == S_OK) {
gboolean ison;
gboolean waserr;
sleep(1);
ison = !apcsmart_IsPoweredOff(ad, &waserr);
if (waserr) {
return S_RESETFAIL;
}
if (request == ST_POWEROFF) {
return ison ? S_RESETFAIL : S_OK;
}else{
return ison ? S_OK : S_RESETFAIL;
}
}
return rc;
}
/*
* reset the host
*/
static int
apcsmart_ReqGenericReset(struct pluginDevice *ad)
{
char resp[MAX_STRING];
int rc = S_RESETFAIL;
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
/* send reset command(s) */
if (((rc = APC_init(ad)) == S_OK)
&& ((rc = APC_send_cmd(ad->upsfd, CMD_RESET)) == S_OK)) {
if (((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK)
&& (strcmp(resp, RSP_RESET) == 0
|| strcmp(resp, RSP_RESET2) == 0)) {
/* first kind of reset command was accepted */
} else if (((rc = APC_send_cmd(ad->upsfd, CMD_RESET2)) == S_OK)
&& ((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK)
&& (strcmp(resp, RSP_RESET) == 0
|| strcmp(resp, RSP_RESET2) == 0)) {
/* second kind of command was accepted */
} else {
if (Debug) {
LOG(PIL_DEBUG, "APC: neither reset command "
"was accepted");
}
rc = S_RESETFAIL;
}
}
if (rc == S_OK) {
/* we wait grace period + up to 10 seconds after shutdown */
int maxdelay = atoi(ad->shutdown_delay)+10;
int j;
for (j=0; j < maxdelay; ++j) {
gboolean err;
if (apcsmart_ResetHappening(ad, &err)) {
return err ? S_RESETFAIL : S_OK;
}
sleep(1);
}
LOG(PIL_CRIT, "%s: timed out waiting for reset to end."
, __FUNCTION__);
return S_RESETFAIL;
}else{
if (strcmp(resp, RSP_NA) == 0){
gboolean iserr;
/* This means it's currently powered off */
/* or busy on a previous command... */
if (apcsmart_IsPoweredOff(ad, &iserr)) {
if (iserr) {
LOG(PIL_DEBUG, "%s: power off "
"detection failed.", __FUNCTION__);
return S_RESETFAIL;
}
if (Debug) {
LOG(PIL_DEBUG, "APC: was powered off, "
"powering back on.");
}
return apcsmart_ReqOnOff(ad, ST_POWERON);
}
}
}
strcpy(resp, "?");
/* reset failed */
return S_RESETFAIL;
}
static int
apcsmart_reset_req(StonithPlugin * s, int request, const char *host)
{
char ** hl;
int b_found=FALSE;
struct pluginDevice * ad = (struct pluginDevice *) s;
int rc;
ERRIFNOTCONFIGED(s, S_OOPS);
if (host == NULL) {
LOG(PIL_CRIT, "%s: invalid hostname argument.", __FUNCTION__);
return (S_INVAL);
}
/* look through the hostlist */
hl = ad->hostlist;
while (*hl && !b_found ) {
if( strcasecmp( *hl, host ) == 0 ) {
b_found = TRUE;
break;
}else{
++hl;
}
}
/* host not found in hostlist */
if( !b_found ) {
LOG(PIL_CRIT, "%s: host '%s' not in hostlist."
, __FUNCTION__, host);
return S_BADHOST;
}
if ((rc = APC_init(ad)) != S_OK) {
return rc;
}
if (request == ST_POWERON || request == ST_POWEROFF) {
return apcsmart_ReqOnOff(ad, request);
}
return apcsmart_ReqGenericReset(ad);
}
/*
* get info about the stonith device
*/
static const char *
apcsmart_get_info(StonithPlugin * s, int reqtype)
{
struct pluginDevice *ad = (struct pluginDevice *) s;
const char *ret;
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
ERRIFWRONGDEV(s,NULL);
switch (reqtype) {
case ST_DEVICEID:
ret = ad->idinfo;
break;
case ST_DEVICENAME:
ret = ad->upsdev;
break;
case ST_DEVICEDESCR:
ret = "APC Smart UPS\n"
" (via serial port - NOT USB!). \n"
" Works with higher-end APC UPSes, like\n"
" Back-UPS Pro, Smart-UPS, Matrix-UPS, etc.\n"
" (Smart-UPS may have to be >= Smart-UPS 700?).\n"
" See http://www.networkupstools.org/protocols/apcsmart.html\n"
" for protocol compatibility details.";
break;
case ST_DEVICEURL:
ret = "http://www.apc.com/";
break;
case ST_CONF_XML: /* XML metadata */
ret = apcsmartXML;
break;
default:
ret = NULL;
break;
}
return (ret);
}
/*
* APC Stonith destructor...
*/
static void
apcsmart_destroy(StonithPlugin * s)
{
struct pluginDevice *ad = (struct pluginDevice *) s;
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
VOIDERRIFWRONGDEV(s);
if (ad->upsfd >= 0 && ad->upsdev) {
APC_deinit( ad );
}
ad->pluginid = NOTpluginID;
if (ad->hostlist) {
stonith_free_hostlist(ad->hostlist);
ad->hostlist = NULL;
}
if (ad->upsdev != NULL) {
FREE(ad->upsdev);
ad->upsdev = NULL;
}
ad->hostcount = -1;
ad->upsfd = -1;
FREE(ad);
}
/*
* Create a new APC Stonith device. Too bad this function can't be
* static
*/
static StonithPlugin *
apcsmart_new(const char *subplugin)
{
struct pluginDevice *ad = ST_MALLOCT(struct pluginDevice);
if (Debug) {
LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
}
if (ad == NULL) {
LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
return (NULL);
}
memset(ad, 0, sizeof(*ad));
ad->pluginid = pluginid;
ad->hostlist = NULL;
ad->hostcount = -1;
ad->upsfd = -1;
ad->idinfo = DEVICE;
ad->sp.s_ops = &apcsmartOps;
if (Debug) {
LOG(PIL_DEBUG, "%s: returning successfully.", __FUNCTION__);
}
return &(ad->sp);
}