diff options
Diffstat (limited to '')
-rw-r--r-- | lib/plugins/stonith/rps10.c | 1070 |
1 files changed, 1070 insertions, 0 deletions
diff --git a/lib/plugins/stonith/rps10.c b/lib/plugins/stonith/rps10.c new file mode 100644 index 0000000..08d9873 --- /dev/null +++ b/lib/plugins/stonith/rps10.c @@ -0,0 +1,1070 @@ +/* + * Stonith module for WTI Remote Power Controllers (RPS-10M device) + * + * Original code from baytech.c by + * Copyright (c) 2000 Alan Robertson <alanr@unix.sh> + * + * Modifications for WTI RPS10 + * Copyright (c) 2000 Computer Generation Incorporated + * Eric Z. Ayers <eric.ayers@compgen.com> + * + * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005 + * + * 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 + * + */ + +#include <lha_internal.h> + +#define DEVICE "WTI RPS10 Power Switch" +#include "stonith_plugin_common.h" + +#include <termios.h> +#define PIL_PLUGIN rps10 +#define PIL_PLUGIN_S "rps10" +#define PIL_PLUGINLICENSE LICENSE_LGPL +#define PIL_PLUGINLICENSEURL URL_LGPL +#define ST_RPS10 "serial_to_targets" +#define MAX_PRSID 256 +#include <pils/plugin.h> + +static StonithPlugin * rps10_new(const char *); +static void rps10_destroy(StonithPlugin *); +static int rps10_set_config(StonithPlugin *, StonithNVpair *); +static const char * const * rps10_get_confignames(StonithPlugin *); +static const char * rps10_getinfo(StonithPlugin * s, int InfoType); +static int rps10_status(StonithPlugin * ); +static int rps10_reset_req(StonithPlugin * s, int request, const char * host); +static char ** rps10_hostlist(StonithPlugin *); + +static struct stonith_ops rps10Ops ={ + rps10_new, /* Create new STONITH object */ + rps10_destroy, /* Destroy STONITH object */ + rps10_getinfo, /* Return STONITH info string */ + rps10_get_confignames, /* Return STONITH info string */ + rps10_set_config, /* Get configuration from NVpairs */ + rps10_status, /* Return STONITH device status */ + rps10_reset_req, /* Request a reset */ + rps10_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; + +#include "stonith_signal.h" +#define DOESNT_USE_STONITHKILLCOMM +#define DOESNT_USE_STONITHSCANLINE +#include "stonith_expect_helpers.h" + +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 + , &rps10Ops + , NULL /*close */ + , &OurInterface + , (void*)&OurImports + , &interfprivate); +} + +/* + * This was written for a Western Telematic Inc. (WTI) + * Remote Power Switch - RPS-10M. + * + * It has a DB9 serial port, a Rotary Address Switch, + * and a pair of RJ-11 jacks for linking multiple switches + * together. The 'M' unit is a master unit which can control + * up to 9 additional slave units. (the master unit also has an + * A/C outlet, so you can control up to 10 devices) + * + * There are a set of dip switches. The default shipping configuration + * is with all dip switches down. I highly recommend that you flip + * switch #3 up, so that when the device is plugged in, the power + * to the unit comes on. + * + * The serial interface is fixed at 9600 BPS (well, you *CAN* + * select 2400 BPS with a dip switch, but why?) 8-N-1 + * + * The ASCII command string is: + * + * ^B^X^X^B^X^Xac^M + * + * ^B^X^X^B^X^X "fixed password" prefix (CTRL-B CTRL-X ... ) + * ^M the carriage return character + * + * a = 0-9 Indicates the address of the module to receive the command + * a = * Sends the command to all modules + * + * c = 0 Switch the AC outlet OFF + * Returns: + * Plug 0 Off + * Complete + * + * c = 1 Switch the AC outlet ON + * Returns: + * Plug 0 On + * Complete + * + * c = T Toggle AC OFF (delay) then back ON + * Returns: + * Plug 0 Off + * Plug 0 On + * Complete + * + * c = ? Read and display status of the selected module + * Returns: + * Plug 0 On # or Plug 0 Off + * Complete + * + * e.g. ^B^X^X^B^X^X0T^M toggles the power on plug 0 OFF and then ON + * + * 21 September 2000 + * Eric Z. Ayers + * Computer Generation, Inc. + */ + +struct cntrlr_str { + char outlet_id; /* value 0-9, '*' */ + char * node; /* name of the node attached to this outlet */ +}; + +struct pluginDevice { + StonithPlugin sp; + const char * pluginid; + const char * idinfo; + + int fd; /* FD open to the serial port */ + + char * device; /* Serial device name to use to communicate + to this RPS10 + */ + +#define WTI_NUM_CONTROLLERS 10 + struct cntrlr_str + controllers[WTI_NUM_CONTROLLERS]; + /* one master switch can address 10 controllers */ + + /* Number of actually configured units */ + int unit_count; + +}; + +/* This string is used to identify this type of object in the config file */ +static const char * pluginid = "WTI_RPS10"; +static const char * NOTwtiid = "OBJECT DESTROYED: (WTI RPS-10)"; + +#include "stonith_config_xml.h" + +#define XML_RPS10_SHORTDESC \ + XML_PARM_SHORTDESC_BEGIN("en") \ + "Value in the format \"serial_device remotenode outlet [remotenode outlet]...\"" \ + XML_PARM_SHORTDESC_END + +#define XML_RPS10_LONGDESC \ + XML_PARM_LONGDESC_BEGIN("en") \ + "The RPS-10 STONITH device configuration information in the format \"serial_device remotenode outlet [remotenode outlet]...\"" \ + XML_PARM_LONGDESC_END + +#define XML_RPS10_PARM \ + XML_PARAMETER_BEGIN(ST_RPS10, "string", "1", "1") \ + XML_RPS10_SHORTDESC \ + XML_RPS10_LONGDESC \ + XML_PARAMETER_END + +static const char *rps10XML = + XML_PARAMETERS_BEGIN + XML_RPS10_PARM + XML_PARAMETERS_END; + +/* WTIpassword - The fixed string ^B^X^X^B^X^X */ +static const char WTIpassword[7] = {2,24,24,2,24,24,0}; + +/* + * Different expect strings that we get from the WTI_RPS10 + * Remote Power Controllers... + */ + +static struct Etoken WTItokReady[] = { {"RPS-10 Ready", 0, 0}, {NULL,0,0}}; +static struct Etoken WTItokComplete[] = { {"Complete", 0, 0} ,{NULL,0,0}}; +static struct Etoken WTItokPlug[] = { {"Plug", 0, 0}, {NULL,0,0}}; +static struct Etoken WTItokOutlet[] = { {"0", 0, 0}, + {"1", 0, 0}, + {"2", 0, 0}, + {"3", 0, 0}, + {"4", 0, 0}, + {"5", 0, 0}, + {"6", 0, 0}, + {"7", 0, 0}, + {"8", 0, 0}, + {"9", 0, 0}, + {NULL,0,0}}; + +static struct Etoken WTItokOff[] = { {"Off", 0, 0}, {NULL,0,0}}; + +/* + * Tokens currently not used because they don't show up on all RPS10 units: + * + */ +static struct Etoken WTItokOn[] = { {"On", 0, 0}, {NULL,0,0}}; + +/* Accept either a CR/NL or an NL/CR */ +static struct Etoken WTItokCRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}}; + +static int RPSConnect(struct pluginDevice * ctx); +static int RPSDisconnect(struct pluginDevice * ctx); + +static int RPSReset(struct pluginDevice*, char unit_id, const char * rebootid); +#if defined(ST_POWERON) +static int RPSOn(struct pluginDevice*, char unit_id, const char * rebootid); +#endif +#if defined(ST_POWEROFF) +static int RPSOff(struct pluginDevice*, char unit_id, const char * rebootid); +#endif +static signed char RPSNametoOutlet ( struct pluginDevice * ctx, const char * host ); + +static int RPS_parse_config_info(struct pluginDevice* ctx, const char * info); + +#define SENDCMD(outlet, cmd, timeout) { \ + int ret_val = RPSSendCommand(ctx, outlet, cmd, timeout);\ + if (ret_val != S_OK) { \ + return ret_val; \ + } \ + } + +/* + * RPSSendCommand - send a command to the specified outlet + */ +static int +RPSSendCommand (struct pluginDevice *ctx, char outlet, char command, int timeout) +{ + char writebuf[10]; /* all commands are 9 chars long! */ + int return_val; /* system call result */ + fd_set rfds, wfds, xfds; + struct timeval tv; /* */ + + /* list of FDs for select() */ + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&xfds); + + snprintf (writebuf, sizeof(writebuf), "%s%c%c\r", + WTIpassword, outlet, command); + + if (Debug) { + LOG(PIL_DEBUG, "Sending %s\n", writebuf); + } + + /* Make sure the serial port won't block on us. use select() */ + FD_SET(ctx->fd, &wfds); + FD_SET(ctx->fd, &xfds); + + tv.tv_sec = timeout; + tv.tv_usec = 0; + + return_val = select(ctx->fd+1, NULL, &wfds,&xfds, &tv); + if (return_val == 0) { + /* timeout waiting on serial port */ + LOG(PIL_CRIT, "%s: Timeout writing to %s", + pluginid, ctx->device); + return S_TIMEOUT; + } else if ((return_val == -1) || FD_ISSET(ctx->fd, &xfds)) { + /* an error occured */ + LOG(PIL_CRIT, "%s: Error before writing to %s: %s", + pluginid, ctx->device, strerror(errno)); + return S_OOPS; + } + + /* send the command */ + if (write(ctx->fd, writebuf, strlen(writebuf)) != + (int)strlen(writebuf)) { + LOG(PIL_CRIT, "%s: Error writing to %s : %s", + pluginid, ctx->device, strerror(errno)); + return S_OOPS; + } + + /* suceeded! */ + return S_OK; + +} /* end RPSSendCommand() */ + +/* + * RPSReset - Reset (power-cycle) the given outlet id + */ +static int +RPSReset(struct pluginDevice* ctx, char unit_id, const char * rebootid) +{ + + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + if (ctx->fd < 0) { + LOG(PIL_CRIT, "%s: device %s is not open!", pluginid, + ctx->device); + return S_OOPS; + } + + /* send the "toggle power" command */ + SENDCMD(unit_id, 'T', 10); + + /* Expect "Plug 0 Off" */ + /* Note: If asked to control "*", the RPS10 will report all units it + * separately; however, we don't know how many, so we can only wait + * for the first unit to report something and then wait until the + * "Complete" */ + EXPECT(ctx->fd, WTItokPlug, 5); + if (Debug) { + LOG(PIL_DEBUG, "Got Plug\n"); + } + EXPECT(ctx->fd, WTItokOutlet, 2); + if (Debug) { + LOG(PIL_DEBUG, "Got Outlet #\n"); + } + EXPECT(ctx->fd, WTItokOff, 2); + if (Debug) { + LOG(PIL_DEBUG, "Got Off\n"); + } + EXPECT(ctx->fd, WTItokCRNL, 2); + LOG(PIL_INFO, "Host is being rebooted: %s", rebootid); + + /* Expect "Complete" */ + EXPECT(ctx->fd, WTItokComplete, 14); + if (Debug) { + LOG(PIL_DEBUG, "Got Complete\n"); + } + EXPECT(ctx->fd, WTItokCRNL, 2); + if (Debug) { + LOG(PIL_DEBUG, "Got NL\n"); + } + + return(S_OK); + +} /* end RPSReset() */ + + +#if defined(ST_POWERON) +/* + * RPSOn - Turn OFF the given outlet id + */ +static int +RPSOn(struct pluginDevice* ctx, char unit_id, const char * host) +{ + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + if (ctx->fd < 0) { + LOG(PIL_CRIT, "%s: device %s is not open!", pluginid, + ctx->device); + return S_OOPS; + } + + /* send the "On" command */ + SENDCMD(unit_id, '1', 10); + + /* Expect "Plug 0 On" */ + EXPECT(ctx->fd, WTItokPlug, 5); + EXPECT(ctx->fd, WTItokOutlet, 2); + EXPECT(ctx->fd, WTItokOn, 2); + EXPECT(ctx->fd, WTItokCRNL, 2); + LOG(PIL_INFO, "Host is being turned on: %s", host); + + /* Expect "Complete" */ + EXPECT(ctx->fd, WTItokComplete, 5); + EXPECT(ctx->fd, WTItokCRNL, 2); + + return(S_OK); + +} /* end RPSOn() */ +#endif + + +#if defined(ST_POWEROFF) +/* + * RPSOff - Turn Off the given outlet id + */ +static int +RPSOff(struct pluginDevice* ctx, char unit_id, const char * host) +{ + + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + if (ctx->fd < 0) { + LOG(PIL_CRIT, "%s: device %s is not open!", pluginid, + ctx->device); + return S_OOPS; + } + + /* send the "Off" command */ + SENDCMD(unit_id, '0', 10); + + /* Expect "Plug 0 Off" */ + EXPECT(ctx->fd, WTItokPlug, 5); + EXPECT(ctx->fd, WTItokOutlet, 2); + EXPECT(ctx->fd, WTItokOff, 2); + EXPECT(ctx->fd, WTItokCRNL, 2); + LOG(PIL_INFO, "Host is being turned on: %s", host); + + /* Expect "Complete" */ + EXPECT(ctx->fd, WTItokComplete, 5); + EXPECT(ctx->fd, WTItokCRNL, 2); + + return(S_OK); + +} /* end RPSOff() */ +#endif + + +/* + * rps10_status - API entry point to probe the status of the stonith device + * (basically just "is it reachable and functional?", not the + * status of the individual outlets) + * + * Returns: + * S_OOPS - some error occured + * S_OK - if the stonith device is reachable and online. + */ +static int +rps10_status(StonithPlugin *s) +{ + struct pluginDevice* ctx; + + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + ERRIFNOTCONFIGED(s,S_OOPS); + + ctx = (struct pluginDevice*) s; + if (RPSConnect(ctx) != S_OK) { + return(S_OOPS); + } + + /* The "connect" really does enough work to see if the + controller is alive... It verifies that it is returning + RPS-10 Ready + */ + + return(RPSDisconnect(ctx)); +} + +/* + * rps10_hostlist - API entry point to return the list of hosts + * for the devices on this WTI_RPS10 unit + * + * This type of device is configured from the config file, + * so we don't actually have to connect to figure this + * out, just peruse the 'ctx' structure. + * Returns: + * NULL on error + * a malloced array, terminated with a NULL, + * of null-terminated malloc'ed strings. + */ +static char ** +rps10_hostlist(StonithPlugin *s) +{ + char ** ret = NULL; /* list to return */ + int i; + int j; + struct pluginDevice* ctx; + + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + ERRIFNOTCONFIGED(s,NULL); + + ctx = (struct pluginDevice*) s; + + if (ctx->unit_count >= 1) { + ret = (char **)MALLOC((ctx->unit_count+1)*sizeof(char*)); + if (ret == NULL) { + LOG(PIL_CRIT, "out of memory"); + return ret; + } + ret[ctx->unit_count]=NULL; /* null terminate the array */ + for (i=0; i < ctx->unit_count; i++) { + ret[i] = STRDUP(ctx->controllers[i].node); + if (ret[i] == NULL) { + for(j=0; j<i; j++) { + FREE(ret[j]); + } + FREE(ret); ret = NULL; + break; + } + } /* end for each possible outlet */ + } /* end if any outlets are configured */ + return(ret); +} /* end si_hostlist() */ + +/* + * Parse the given configuration information, and stash + * it away... + * + * The format of <info> for this module is: + * <serial device> <remotenode> <outlet> [<remotenode> <outlet>] ... + * + * e.g. A machine named 'nodea' can kill a machine named 'nodeb' through + * a device attached to serial port /dev/ttyS0. + * A machine named 'nodeb' can kill machines 'nodea' and 'nodec' + * through a device attached to serial port /dev/ttyS1 (outlets 0 + * and 1 respectively) + * + * <assuming this is the heartbeat configuration syntax:> + * + * stonith nodea rps10 /dev/ttyS0 nodeb 0 + * stonith nodeb rps10 /dev/ttyS0 nodea 0 nodec 1 + * + * Another possible configuration is for 2 stonith devices + * accessible through 2 different serial ports on nodeb: + * + * stonith nodeb rps10 /dev/ttyS0 nodea 0 + * stonith nodeb rps10 /dev/ttyS1 nodec 0 + */ + +/* + * OOPS! + * + * Most of the large block of comments above is incorrect as far as this + * module is concerned. It is somewhat applicable to the heartbeat code, + * but not to this Stonith module. + * + * The format of parameter string for this module is: + * <serial device> <remotenode> <outlet> [<remotenode> <outlet>] ... + */ + +static int +RPS_parse_config_info(struct pluginDevice* ctx, const char * info) +{ + char *copy; + char *token; + char *outlet, *node; + + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + /* strtok() is nice to use to parse a string with + (other than it isn't threadsafe), but it is destructive, so + we're going to alloc our own private little copy for the + duration of this function. + */ + + copy = STRDUP(info); + if (!copy) { + LOG(PIL_CRIT, "out of memory"); + return S_OOPS; + } + + /* Grab the serial device */ + token = strtok (copy, " \t"); + + if (!token) { + LOG(PIL_CRIT, "%s: Can't find serial device on config line '%s'", + pluginid, info); + goto token_error; + } + + ctx->device = STRDUP(token); + if (!ctx->device) { + LOG(PIL_CRIT, "out of memory"); + goto token_error; + } + + /* Loop through the rest of the command line which should consist of */ + /* <nodename> <outlet> pairs */ + while ((node = strtok (NULL, " \t")) + && (outlet = strtok (NULL, " \t\n"))) { + char outlet_id; + + /* validate the outlet token */ + if ((sscanf (outlet, "%c", &outlet_id) != 1) + || !( ((outlet_id >= '0') && (outlet_id <= '9')) + || (outlet_id == '*') || (outlet_id == 'A') ) + ) { + LOG(PIL_CRIT + , "%s: the outlet_id %s must be between" + " 0 and 9 or '*' / 'A'", + pluginid, outlet); + goto token_error; + } + + if (outlet_id == 'A') { + /* Remap 'A' to '*'; in some configurations, + * a '*' can't be configured because it breaks + * scripts -- lmb */ + outlet_id = '*'; + } + + if (ctx->unit_count >= WTI_NUM_CONTROLLERS) { + LOG(PIL_CRIT, + "%s: Tried to configure too many controllers", + pluginid); + goto token_error; + } + + ctx->controllers[ctx->unit_count].node = STRDUP(node); + strdown(ctx->controllers[ctx->unit_count].node); + ctx->controllers[ctx->unit_count].outlet_id = outlet_id; + ctx->unit_count++; + + } + + /* free our private copy of the string we've been destructively + * parsing with strtok() + */ + FREE(copy); + return ((ctx->unit_count > 0) ? S_OK : S_BADCONFIG); + +token_error: + FREE(copy); + if (ctx->device) { + FREE(ctx->device); + ctx->device = NULL; + } + return(S_BADCONFIG); +} + + +/* + * dtrtoggle - toggle DTR on the serial port + * + * snarfed from minicom, sysdep1.c, a well known POSIX trick. + * + */ +static void dtrtoggle(int fd) { + struct termios tty, old; + int sec = 2; + + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + tcgetattr(fd, &tty); + tcgetattr(fd, &old); + cfsetospeed(&tty, B0); + cfsetispeed(&tty, B0); + tcsetattr(fd, TCSANOW, &tty); + if (sec>0) { + sleep(sec); + tcsetattr(fd, TCSANOW, &old); + } + + if (Debug) { + LOG(PIL_DEBUG, "dtrtoggle Complete (%s)\n", pluginid); + } +} + +/* + * RPSConnect - + * + * Connect to the given WTI_RPS10 device. + * Side Effects + * DTR on the serial port is toggled + * ctx->fd now contains a valid file descriptor to the serial port + * ??? LOCK THE SERIAL PORT ??? + * + * Returns + * S_OK on success + * S_OOPS on error + * S_TIMEOUT if the device did not respond + * + */ +static int +RPSConnect(struct pluginDevice * ctx) +{ + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + /* Open the serial port if it isn't already open */ + if (ctx->fd < 0) { + struct termios tio; + + if (OurImports->TtyLock(ctx->device) < 0) { + LOG(PIL_CRIT, "%s: TtyLock failed.", pluginid); + return S_OOPS; + } + + ctx->fd = open (ctx->device, O_RDWR); + if (ctx->fd <0) { + LOG(PIL_CRIT, "%s: Can't open %s : %s", + pluginid, ctx->device, strerror(errno)); + return S_OOPS; + } + + /* set the baudrate to 9600 8 - N - 1 */ + memset (&tio, 0, sizeof(tio)); + + /* ??? ALAN - the -tradtitional flag on gcc causes the + CRTSCTS constant to generate a warning, and warnings + are treated as errors, so I can't set this flag! - EZA ??? + + Hmmm. now that I look at the documentation, RTS + is just wired high on this device! we don't need it. + */ + /* tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD | CRTSCTS ;*/ + tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD ; + tio.c_lflag = ICANON; + + if (tcsetattr (ctx->fd, TCSANOW, &tio) < 0) { + LOG(PIL_CRIT, "%s: Can't set attributes %s : %s", + pluginid, ctx->device, strerror(errno)); + close (ctx->fd); + OurImports->TtyUnlock(ctx->device); + ctx->fd=-1; + return S_OOPS; + } + /* flush all data to and fro the serial port before we start */ + if (tcflush (ctx->fd, TCIOFLUSH) < 0) { + LOG(PIL_CRIT, "%s: Can't flush %s : %s", + pluginid, ctx->device, strerror(errno)); + close (ctx->fd); + OurImports->TtyUnlock(ctx->device); + ctx->fd=-1; + return S_OOPS; + } + + } + + /* Toggle DTR - this 'resets' the controller serial port interface + In minicom, try CTRL-A H to hangup and you can see this behavior. + */ + dtrtoggle(ctx->fd); + + /* Wait for the switch to respond with "RPS-10 Ready". + Emperically, this usually takes 5-10 seconds... + ... If this fails, this may be a hint that you got + a broken serial cable, which doesn't connect hardware + flow control. + */ + if (Debug) { + LOG(PIL_DEBUG, "Waiting for READY\n"); + } + EXPECT(ctx->fd, WTItokReady, 12); + if (Debug) { + LOG(PIL_DEBUG, "Got READY\n"); + } + EXPECT(ctx->fd, WTItokCRNL, 2); + if (Debug) { + LOG(PIL_DEBUG, "Got NL\n"); + } + + return(S_OK); +} + +static int +RPSDisconnect(struct pluginDevice * ctx) +{ + + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + if (ctx->fd >= 0) { + /* Flush the serial port, we don't care what happens to the + * characters and failing to do this can cause close to hang. + */ + tcflush(ctx->fd, TCIOFLUSH); + close (ctx->fd); + if (ctx->device != NULL) { + OurImports->TtyUnlock(ctx->device); + } + } + ctx->fd = -1; + + return S_OK; +} + +/* + * RPSNametoOutlet - Map a hostname to an outlet on this stonith device. + * + * Returns: + * 0-9, * on success ( the outlet id on the RPS10 ) + * -1 on failure (host not found in the config file) + * + */ +static signed char +RPSNametoOutlet ( struct pluginDevice * ctx, const char * host ) +{ + int i=0; + + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + /* scan the controllers[] array to see if this host is there */ + for (i=0;i<ctx->unit_count;i++) { + /* return the outlet id */ + if ( ctx->controllers[i].node + && !strcasecmp(host, ctx->controllers[i].node)) { + /* found it! */ + break; + } + } + + if (i == ctx->unit_count) { + return -1; + } else { + return ctx->controllers[i].outlet_id; + } +} + + +/* + * rps10_reset - API call to Reset (reboot) the given host on + * this Stonith device. This involves toggling the power off + * and then on again, OR just calling the builtin reset command + * on the stonith device. + */ +static int +rps10_reset_req(StonithPlugin * s, int request, const char * host) +{ + int rc = S_OK; + int lorc = S_OK; + signed char outlet_id = -1; + struct pluginDevice* ctx; + + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + ERRIFNOTCONFIGED(s,S_OOPS); + + ctx = (struct pluginDevice*) s; + + if ((rc = RPSConnect(ctx)) != S_OK) { + return(rc); + } + + outlet_id = RPSNametoOutlet(ctx, host); + + if (outlet_id < 0) { + LOG(PIL_WARN, "%s: %s doesn't control host [%s]" + , pluginid, ctx->device, host ); + RPSDisconnect(ctx); + return(S_BADHOST); + } + + switch(request) { + +#if defined(ST_POWERON) + case ST_POWERON: + rc = RPSOn(ctx, outlet_id, host); + break; +#endif +#if defined(ST_POWEROFF) + case ST_POWEROFF: + rc = RPSOff(ctx, outlet_id, host); + break; +#endif + case ST_GENERIC_RESET: + rc = RPSReset(ctx, outlet_id, host); + break; + default: + rc = S_INVAL; + break; + } + + lorc = RPSDisconnect(ctx); + + return(rc != S_OK ? rc : lorc); +} + +/* + * Parse the information in the given string, + * and stash it away... + */ +static int +rps10_set_config(StonithPlugin* s, StonithNVpair* list) +{ + struct pluginDevice* ctx; + StonithNamesToGet namestocopy [] = + { {ST_RPS10, NULL} + , {NULL, NULL} + }; + int rc=0; + + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + ERRIFWRONGDEV(s,S_OOPS); + + if (s->isconfigured) { + /* The module is already configured. */ + return(S_OOPS); + } + + ctx = (struct pluginDevice*) s; + + if((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK){ + LOG(PIL_DEBUG , "get all calues failed"); + return rc; + } + + rc = RPS_parse_config_info(ctx, namestocopy[0].s_value); + FREE(namestocopy[0].s_value); + return rc; +} + +/* + * Return the Stonith plugin configuration parameter + * + */ +static const char * const * +rps10_get_confignames(StonithPlugin* p) +{ + static const char * Rps10Params[] = {ST_RPS10 ,NULL }; + + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + return Rps10Params; +} + +/* + * rps10_getinfo - API entry point to retrieve something from the handle + */ +static const char * +rps10_getinfo(StonithPlugin * s, int reqtype) +{ + struct pluginDevice* ctx; + const char * ret; + + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + ERRIFWRONGDEV(s,NULL); + + /* + * We look in the ST_TEXTDOMAIN catalog for our messages + */ + ctx = (struct pluginDevice *)s; + + switch (reqtype) { + case ST_DEVICEID: + ret = ctx->idinfo; + break; + case ST_DEVICENAME: + ret = ctx->device; + break; + case ST_DEVICEDESCR: + ret = "Western Telematic Inc. (WTI) " + "Remote Power Switch - RPS-10M.\n"; + break; + case ST_DEVICEURL: + ret = "http://www.wti.com/"; + break; + case ST_CONF_XML: /* XML metadata */ + ret = rps10XML; + break; + default: + ret = NULL; + break; + } + return ret; +} + +/* + * rps10_destroy - API entry point to destroy a WTI_RPS10 Stonith object. + */ +static void +rps10_destroy(StonithPlugin *s) +{ + struct pluginDevice* ctx; + int i; + + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + VOIDERRIFWRONGDEV(s); + + ctx = (struct pluginDevice *)s; + + ctx->pluginid = NOTwtiid; + + /* close the fd if open and set ctx->fd to invalid */ + RPSDisconnect(ctx); + + if (ctx->device != NULL) { + FREE(ctx->device); + ctx->device = NULL; + } + if (ctx->unit_count > 0) { + for (i = 0; i < ctx->unit_count; i++) { + if (ctx->controllers[i].node != NULL) { + FREE(ctx->controllers[i].node); + ctx->controllers[i].node = NULL; + } + } + } + FREE(ctx); +} + +/* + * rps10_new - API entry point called to create a new WTI_RPS10 Stonith device + * object. + */ +static StonithPlugin * +rps10_new(const char *subplugin) +{ + struct pluginDevice* ctx = ST_MALLOCT(struct pluginDevice); + + if (Debug) { + LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); + } + + if (ctx == NULL) { + LOG(PIL_CRIT, "out of memory"); + return(NULL); + } + memset(ctx, 0, sizeof(*ctx)); + ctx->pluginid = pluginid; + ctx->fd = -1; + ctx->unit_count = 0; + ctx->device = NULL; + ctx->idinfo = DEVICE; + ctx->sp.s_ops = &rps10Ops; + + return &(ctx->sp); +} |