/* $Id: apcsmart.c,v 1.12.2.4 2005/04/22 13:53:26 blaschke Exp $ */
/*
* 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
*
* UPS code is taken from:
* 'Network UPS Tools' by Russell Kroll <rkroll@exploits.org>
* homepage: http://www.exploits.org/nut/
*/
#include <portability.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <libintl.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <stonith/stonith.h>
#include <glib.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 DEVICE "APCSmart-Stonith"
#define CFG_FILE "/etc/ha.d/apcsmart.cfg"
#define MAX_DEVICES 1
#define SERIAL_TIMEOUT 3 /* timeout in sec */
#define SEND_DELAY 50000 /* in µs */
#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 CMD_SHUTDOWN_DELAY "p"
#define CMD_WAKEUP_DELAY "r"
#define CR 13
struct APCDevice {
const char *APCid; /* of object */
char **hostlist; /* served by the device (only 1) */
int hostcount; /* of hosts (1) */
char *upsdev; /* */
int upsfd; /* for serial port */
char shutdown_delay[MAX_DELAY_STRING]; /* smallest shutdown delay */
char old_shutdown_delay[MAX_DELAY_STRING]; /* old shutdown delay */
char wakeup_delay[MAX_DELAY_STRING]; /* smallest wakeup delay */
char old_wakeup_delay[MAX_DELAY_STRING]; /* old wakeup delay */
};
/* saving old settings */
static struct termios old_tio;
static int f_serialtimeout; /* flag for timeout */
static const char *APCid = DEVICE;
static const char *NOTapcID = "destroyed (APCSmart)";
#define ISAPCDEV(i) (((i)!= NULL && (i)->pinfo != NULL) && \
((struct APCDevice *)(i->pinfo))->APCid == APCid)
#define ISCONFIGED(i) (((struct APCDevice *)(i->pinfo))->upsdev != NULL)
#define _(text) dgettext(ST_TEXTDOMAIN, text)
/*
* stonith prototypes
*/
#define PIL_PLUGINTYPE STONITH_TYPE
#define PIL_PLUGINTYPE_S STONITH_TYPE_S
#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"
/*
* apcsmartclose is called as part of unloading the apcsmart STONITH plugin.
* If there was any global data allocated, or file descriptors opened, etc.
* which is associated with the plugin, and not a single interface
* in particular, here's our chance to clean it up.
*/
static void
apcsmartclosepi(PILPlugin*pi)
{
}
/*
* apcsmartcloseintf called as part of shutting down the apcsmart STONITH
* interface. If there was any global data allocated, or file descriptors
* opened, etc. which is associated with the apcsmart implementation,
* here's our chance to clean it up.
*/
static PIL_rc
apcsmartcloseintf(PILInterface* pi, void* pd)
{
return PIL_OK;
}
static void * apcsmart_new(void);
static void apcsmart_destroy(Stonith *);
static int apcsmart_set_config_file(Stonith *, const char * cfgname);
static int apcsmart_set_config_info(Stonith *, const char * info);
static const char * apcsmart_getinfo(Stonith * s, int InfoType);
static int apcsmart_status(Stonith * );
static int apcsmart_reset_req(Stonith * s, int request, const char * host);
static char ** apcsmart_hostlist(Stonith *);
static void apcsmart_free_hostlist(char **);
static struct stonith_ops apcsmartOps ={
apcsmart_new, /* Create new STONITH object */
apcsmart_destroy, /* Destroy STONITH object */
apcsmart_set_config_file, /* set configuration from file */
apcsmart_set_config_info, /* Get configuration from file */
apcsmart_getinfo, /* Return STONITH info string */
apcsmart_status, /* Return STONITH device status */
apcsmart_reset_req, /* Request a reset */
apcsmart_hostlist, /* Return list of supported hosts */
apcsmart_free_hostlist /* free above list */
};
PIL_PLUGIN_BOILERPLATE("1.0", Debug, apcsmartclosepi);
static const PILPluginImports* PluginImports;
static PILPlugin* OurPlugin;
static PILInterface* OurInterface;
static StonithImports* OurImports;
static void* interfprivate;
#define LOG PluginImports->log
#define MALLOC PluginImports->alloc
#define STRDUP PluginImports->mstrdup
#define FREE PluginImports->mfree
#define EXPECT_TOK OurImports->ExpectToken
#define STARTPROC OurImports->StartProcess
#undef MALLOCT
#define MALLOCT(t) ((t *)(MALLOC(sizeof(t))))
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
, apcsmartcloseintf /*close */
, &OurInterface
, (void*)&OurImports
, &interfprivate);
}
/*
* own prototypes
*/
int APC_open_serialport(const char *port, speed_t speed);
void APC_close_serialport(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_parse_config_info(struct APCDevice *ad, const char *info );
int APC_init( struct APCDevice *ad );
void APC_deinit( struct APCDevice *ad );
/*
*
* Portable locking (non-blocking)
*
* This is a candidate for including in a general portability library.
*/
static int
file_lock(int fd)
{
int ret;
#ifdef HAVE_FCNTL
struct flock l;
l.l_type = F_WRLCK;
l.l_whence = 0;
l.l_start = 0;
l.l_len = 0;
ret = fcntl(fd, F_SETLK, &l);
return((ret == -1) ? -1 : 0);
#else
# ifdef HAVE_FLOCK
ret = flock(fd, LOCK_EX | LOCK_NB);
return(ret);
# else
# error "No locking method (flock, fcntl) is available"
return(-1);
# endif /* HAVE_FLOCK */
#endif /* HAVE_FCNTL */
}
static int
file_unlock(int fd)
{
int ret;
#ifdef HAVE_FCNTL
struct flock l;
l.l_type = F_UNLCK;
l.l_whence = 0;
l.l_start = 0;
l.l_len = 0;
ret = fcntl(fd, F_SETLK, &l);
return((ret == -1) ? -1 : 0);
#else
# ifdef HAVE_FLOCK
ret = flock(fd, LOCK_UN);
return(ret);
# else
# error "No unlocking method (flock, fcntl) is available"
return(-1);
# endif /* HAVE_FLOCK */
#endif /* HAVE_FCNTL */
}
/*
* Signal handler for serial port timeouts
*/
void
APC_sh_serial_timeout(int sig)
{
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
STONITH_IGNORE_SIG(SIGALRM);
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: serial port timed out.", __FUNCTION__);
#endif
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;
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout);
alarm(SERIAL_TIMEOUT);
f_serialtimeout = FALSE;
fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK | O_EXCL);
alarm(0);
STONITH_IGNORE_SIG(SIGALRM);
if (fd < 0) {
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: 1st open failed.", __FUNCTION__);
#endif
return -1;
}
if (file_lock(fd) != 0) {
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: 1st lock failed.", __FUNCTION__);
#endif
close(fd);
return -1;
}
tcgetattr(fd, &old_tio);
memcpy(&tio, &old_tio, sizeof(struct termios));
tio.c_lflag = 0 | ECHOE | ECHOKE | ECHOCTL | PENDIN;
tio.c_iflag = 0 | IXANY | IMAXBEL | IXOFF;
tio.c_oflag = 0 | ONLCR;
tio.c_cflag = 0 | CREAD | CS8 | HUPCL | CLOCAL;
cfsetispeed(&tio, speed);
cfsetospeed(&tio, speed);
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &tio);
close(fd);
STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout);
alarm(SERIAL_TIMEOUT);
fd = open(port, O_RDWR | O_NOCTTY | O_EXCL);
alarm(0);
STONITH_IGNORE_SIG(SIGALRM);
if (fd < 0) {
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: 2nd open failed.", __FUNCTION__);
#endif
return -1;
}
if (file_lock(fd) != 0) {
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: 2nd lock failed.", __FUNCTION__);
#endif
close(fd);
return -1;
}
tcgetattr(fd, &tio);
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, TCIFLUSH);
tcsetattr(fd, TCSANOW, &tio);
return (fd);
}
/*
* Close serial port and restore old settings
*/
void
APC_close_serialport(int upsfd)
{
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
file_unlock(upsfd);
tcflush(upsfd, TCIFLUSH);
tcsetattr(upsfd, TCSANOW, &old_tio);
close(upsfd);
}
/*
* Send a command to the ups
*/
int
APC_send_cmd(int upsfd, const char *cmd)
{
int i;
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
for (i = strlen(cmd); i > 0; i--) {
tcflush(upsfd, TCIFLUSH);
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;
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
*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';
return (S_OK);
}
if (inp != CR) {
*p++ = inp;
num++;
}
} else {
alarm(0);
STONITH_IGNORE_SIG(SIGALRM);
*p = '\0';
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];
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
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;
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
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);
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: var '%s' original val %s", __FUNCTION__, cmd, orig);
#endif
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) {
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: var '%s' set to %s", __FUNCTION__,
cmd, newval);
#endif
strcpy(newval, orig); /* return the old value */
return (S_OK); /* got it */
}
}
syslog(LOG_ERR, "%s(): variable '%s' wrapped!", __FUNCTION__, cmd);
syslog(LOG_ERR, "%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 APCDevice *ad )
{
int upsfd;
char value[MAX_DELAY_STRING];
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
/* 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 != -1) {
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(upsfd);
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) {
APC_close_serialport(upsfd);
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) {
APC_close_serialport(upsfd);
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) {
APC_close_serialport(upsfd);
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 APCDevice *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 */
APC_close_serialport(ad->upsfd);
}
/*
* Parse config
*/
int
APC_parse_config_info(struct APCDevice *ad, const char *info )
{
char hostname[MAX_STRING];
static char devicename[MAX_STRING];
char **hl;
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
if (ad->hostcount >= 0) {
return(S_OOPS);
}
if ((hl = (char **)MALLOC((MAX_DEVICES+1)*sizeof(char*))) == NULL) {
syslog(LOG_ERR, "%s: out of memory!", __FUNCTION__);
return S_OOPS;
}
memset(hl, 0, (MAX_DEVICES+1)*sizeof(char*));
if (sscanf(info, "%s %s", devicename, hostname) == 2) {
g_strdown(hostname);
if(( hl[0] = STRDUP(hostname)) == NULL ) {
apcsmart_free_hostlist(hl);
hl = NULL;
return( S_OOPS );
}
ad->hostlist = hl;
ad->hostcount = MAX_DEVICES+1;
ad->upsdev = devicename;
return(S_OK);
}
return(S_BADCONFIG);
}
/*
* return the status for this device
*/
static int
apcsmart_status(Stonith * s)
{
struct APCDevice *ad;
char resp[MAX_STRING];
int rc;
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
if (!ISAPCDEV(s)) {
syslog(LOG_ERR, "%s: invalid argument.", __FUNCTION__);
return (S_INVAL);
}
if( !ISCONFIGED(s)) {
syslog(LOG_ERR, "%s: device is UNCONFIGURED!", __FUNCTION__ );
return( S_OOPS );
}
ad = (struct APCDevice *) s->pinfo;
rc = APC_init(ad);
/* 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. */
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: failed, rc=%d.", __FUNCTION__, rc);
#endif
return (rc);
}
/*
* return the list of hosts configured for this device
*/
static char **
apcsmart_hostlist(Stonith * s)
{
int numhosts;
char **hl;
struct APCDevice *ad;
int j;
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
if (!ISAPCDEV(s)) {
syslog(LOG_ERR, "%s: invalid argument.", __FUNCTION__);
return (NULL);
}
if( !ISCONFIGED(s)) {
syslog(LOG_ERR, "%s: device is UNCONFIGURED!", __FUNCTION__ );
return( NULL );
}
ad = (struct APCDevice *) s->pinfo;
numhosts = ad->hostcount;
if (( hl = (char **)MALLOC(numhosts * sizeof(char *))) == NULL) {
syslog(LOG_ERR, "%s: out of memory.", __FUNCTION__);
return (hl);
}
memset(hl, 0, numhosts * sizeof(char *));
for (j = 0; j < numhosts -1; ++j) {
if ((hl[j] = STRDUP(ad->hostlist[j])) == NULL) {
apcsmart_free_hostlist(hl);
hl = NULL;
return (hl);
}
}
return (hl);
}
/*
* free the hostlist
*/
static void
apcsmart_free_hostlist(char **hlist)
{
char **hl = hlist;
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
if (hl == NULL)
return;
while (*hl) {
FREE(*hl);
*hl = NULL;
++hl;
}
FREE(hlist);
hlist = NULL;
}
static gboolean
apcsmart_RegisterBitsSet(struct APCDevice * 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", ®val) != 1)) {
if (waserr){
*waserr = TRUE;
}
return FALSE;
}
if (waserr){
*waserr = FALSE;
}
return ((regval & bits) == bits);
}
#define apcsmart_ResetHappening(ad,err) apcsmart_RegisterBitsSet(ad,3,0x08,err)
/*
* reset the host
*/
static int
apcsmart_reset_req(Stonith * s, int request, const char *host)
{
struct APCDevice *ad;
char resp[MAX_STRING];
int rc;
int i;
char **hl;
char *shost;
int b_found=FALSE;
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
if (!ISAPCDEV(s)) {
syslog(LOG_ERR, "%s: invalid argument.", __FUNCTION__);
return (S_INVAL);
}
if( !ISCONFIGED(s)) {
syslog(LOG_ERR, "%s: device is UNCONFIGURED!", __FUNCTION__ );
return( S_OOPS );
}
if (host == NULL) {
syslog(LOG_ERR, "%s: invalid hostname argument.", __FUNCTION__);
return (S_INVAL);
}
shost = strdup(host);
if (shost == NULL) {
syslog(LOG_ERR, "%s: strdup failed.", __FUNCTION__);
return (S_INVAL);
}
g_strdown(shost);
ad = (struct APCDevice *) s->pinfo;
/* look through the hostlist */
hl = ad->hostlist;
while (*hl && !b_found ) {
if( strcmp( *hl, shost ) == 0 ) {
b_found = TRUE;
break;
} else
++hl;
}
/* host not found in hostlist */
if( !b_found ) {
syslog(LOG_ERR, "%s: host '%s' not in hostlist.", __FUNCTION__, host);
rc = S_BADHOST;
goto out;
}
/* 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 {
rc = S_RESETFAIL;
}
}
if (rc == S_OK) {
/* we wait grace period + up to 10 seconds after shutdown */
int maxdelay = atoi(ad->shutdown_delay)+10;
for (i=0; i < maxdelay; ++i) {
gboolean err;
if (apcsmart_ResetHappening(ad, &err)) {
if (err) {
break;
}else{
rc = S_OK;
goto out;
}
}
sleep(1);
}
rc = S_RESETFAIL;
}
/* reset failed */
syslog(LOG_ERR, "%s: resetting host '%s' failed.", __FUNCTION__, host);
rc = S_RESETFAIL;
out:
free(shost);
return rc;
}
/*
* parse the information in the given configuration file,
* and stash it away...
*/
static int
apcsmart_set_config_file(Stonith * s, const char *configname)
{
FILE *cfgfile;
char confline[MAX_STRING];
struct APCDevice *ad;
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
if (!ISAPCDEV(s)) {
syslog(LOG_ERR, "%s: invalid argument.", __FUNCTION__);
return (S_INVAL);
}
ad = (struct APCDevice *) s->pinfo;
if ((cfgfile = fopen(configname, "r")) == NULL) {
syslog(LOG_ERR, "Cannot open %s", configname);
return(S_BADCONFIG);
}
while (fgets(confline, sizeof(confline), cfgfile) != NULL) {
if (*confline == '#' || *confline == '\n' || *confline == EOS)
continue;
return(APC_parse_config_info(ad, confline));
}
return(S_BADCONFIG);
}
/*
* Parse the config information in the given string, and stash it away...
*/
static int
apcsmart_set_config_info(Stonith * s, const char *info)
{
struct APCDevice *ad;
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: info: '%s'.", __FUNCTION__, info );
#endif
if (!ISAPCDEV(s)) {
syslog(LOG_ERR, "%s: invalid argument.", __FUNCTION__);
return (S_INVAL);
}
ad = (struct APCDevice *) s->pinfo;
return(APC_parse_config_info(ad, info));
}
/*
* get info about the stonith device
*/
static const char *
apcsmart_getinfo(Stonith * s, int reqtype)
{
struct APCDevice *ad;
const char *ret;
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
if (!ISAPCDEV(s)) {
syslog(LOG_ERR, "%s: invalid argument.", __FUNCTION__);
return NULL;
}
ad = (struct APCDevice *) s->pinfo;
switch (reqtype) {
case ST_DEVICEID:
ret = ad->APCid;
break;
case ST_CONF_INFO_SYNTAX:
ret = _("devicename hostname\n"
"The hostname and devicename are white-space delimited.");
break;
case ST_CONF_FILE_SYNTAX:
ret = _("devicename hostname\n"
"The hostname and devicename are white-space delimited.\n"
"Both items must be on one line.\n"
"Blank lines and lines beginning with # are ignored.");
break;
case ST_DEVICEDESCR:
ret = _("APC Smart UPS (via serial port - model must be >= Smart-UPS 750)");
break;
case ST_DEVICEURL:
ret = "http://www.apc.com/";
break;
default:
ret = NULL;
break;
}
return (ret);
}
/*
* APC Stonith destructor...
*/
static void
apcsmart_destroy(Stonith * s)
{
struct APCDevice *ad;
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
if (!ISAPCDEV(s)) {
syslog(LOG_ERR, "%s: invalid argument.", __FUNCTION__);
return;
}
ad = (struct APCDevice *) s->pinfo;
APC_deinit( ad );
ad->APCid = NOTapcID;
if (ad->hostlist) {
apcsmart_free_hostlist(ad->hostlist);
ad->hostlist = NULL;
}
ad->hostcount = -1;
ad->upsfd = -1;
FREE(ad);
}
/*
* Create a new APC Stonith device. Too bad this function can't be
* static
*/
static void *
apcsmart_new(void)
{
struct APCDevice *ad = MALLOCT(struct APCDevice);
#ifdef APC_DEBUG
syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);
#endif
if (ad == NULL) {
syslog(LOG_ERR, "%s: out of memory.", __FUNCTION__);
return (NULL);
}
memset(ad, 0, sizeof(*ad));
ad->APCid = APCid;
ad->hostlist = NULL;
ad->hostcount = -1;
ad->upsfd = -1;
return ((void *) ad);
}
syntax highlighted by Code2HTML, v. 0.9.1