summaryrefslogtreecommitdiffstats
path: root/lib/plugins/stonith/apcmaster.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/plugins/stonith/apcmaster.c')
-rw-r--r--lib/plugins/stonith/apcmaster.c822
1 files changed, 822 insertions, 0 deletions
diff --git a/lib/plugins/stonith/apcmaster.c b/lib/plugins/stonith/apcmaster.c
new file mode 100644
index 0000000..09a56d3
--- /dev/null
+++ b/lib/plugins/stonith/apcmaster.c
@@ -0,0 +1,822 @@
+/*
+*
+* Copyright 2001 Mission Critical Linux, Inc.
+*
+* All Rights Reserved.
+*/
+/*
+ * Stonith module for APC Master Switch (AP9211)
+ *
+ * Copyright (c) 2001 Mission Critical Linux, Inc.
+ * author: mike ledoux <mwl@mclinux.com>
+ * author: Todd Wheeling <wheeling@mclinux.com>
+ * mangled by Sun Jiang Dong, <sunjd@cn.ibm.com>, IBM, 2005
+ *
+ * Based strongly on original code from baytech.c by Alan Robertson.
+ *
+ * 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
+ *
+ */
+
+/* Observations/Notes
+ *
+ * 1. The APC MasterSwitch, unlike the BayTech network power switch,
+ * accepts only one (telnet) connection/session at a time. When one
+ * session is active, any subsequent attempt to connect to the MasterSwitch
+ * will result in a connection refused/closed failure. In a cluster
+ * environment or other environment utilizing polling/monitoring of the
+ * MasterSwitch (from multiple nodes), this can clearly cause problems.
+ * Obviously the more nodes and the shorter the polling interval, the more
+ * frequently such errors/collisions may occur.
+ *
+ * 2. We observed that on busy networks where there may be high occurances
+ * of broadcasts, the MasterSwitch became unresponsive. In some
+ * configurations this necessitated placing the power switch onto a
+ * private subnet.
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "APC MasterSwitch"
+
+#define DOESNT_USE_STONITHKILLCOMM 1
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN apcmaster
+#define PIL_PLUGIN_S "apcmaster"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * apcmaster_new(const char *);
+static void apcmaster_destroy(StonithPlugin *);
+static const char * const * apcmaster_get_confignames(StonithPlugin *);
+static int apcmaster_set_config(StonithPlugin *, StonithNVpair *);
+static const char * apcmaster_getinfo(StonithPlugin * s, int InfoType);
+static int apcmaster_status(StonithPlugin * );
+static int apcmaster_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** apcmaster_hostlist(StonithPlugin *);
+
+static struct stonith_ops apcmasterOps ={
+ apcmaster_new, /* Create new STONITH object */
+ apcmaster_destroy, /* Destroy STONITH object */
+ apcmaster_getinfo, /* Return STONITH info string */
+ apcmaster_get_confignames, /* Get configuration parameters */
+ apcmaster_set_config, /* Set configuration */
+ apcmaster_status, /* Return STONITH device status */
+ apcmaster_reset_req, /* Request a reset */
+ apcmaster_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_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
+ , &apcmasterOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * I have an AP9211. This code has been tested with this switch.
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ pid_t pid;
+ int rdfd;
+ int wrfd;
+ char * device;
+ char * user;
+ char * passwd;
+};
+
+static const char * pluginid = "APCMS-Stonith";
+static const char * NOTpluginID = "APCMS device has been destroyed";
+
+/*
+ * Different expect strings that we get from the APC MasterSwitch
+ */
+
+#define APCMSSTR "American Power Conversion"
+
+static struct Etoken EscapeChar[] = { {"Escape character is '^]'.", 0, 0}
+ , {NULL,0,0}};
+static struct Etoken login[] = { {"User Name :", 0, 0}, {NULL,0,0}};
+static struct Etoken password[] = { {"Password :", 0, 0} ,{NULL,0,0}};
+static struct Etoken Prompt[] = { {"> ", 0, 0} ,{NULL,0,0}};
+static struct Etoken LoginOK[] = { {APCMSSTR, 0, 0}
+ , {"User Name :", 1, 0} ,{NULL,0,0}};
+static struct Etoken Separator[] = { {"-----", 0, 0} ,{NULL,0,0}};
+
+/* We may get a notice about rebooting, or a request for confirmation */
+static struct Etoken Processing[] = { {"Press <ENTER> to continue", 0, 0}
+ , {"Enter 'YES' to continue", 1, 0}
+ , {NULL,0,0}};
+
+#include "stonith_config_xml.h"
+
+static const char *apcmasterXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+static int MS_connect_device(struct pluginDevice * ms);
+static int MSLogin(struct pluginDevice * ms);
+static int MSRobustLogin(struct pluginDevice * ms);
+static int MSNametoOutlet(struct pluginDevice*, const char * name);
+static int MSReset(struct pluginDevice*, int outletNum, const char * host);
+static int MSLogout(struct pluginDevice * ms);
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+static int apcmaster_onoff(struct pluginDevice*, int outletnum, const char * unitid
+, int request);
+#endif
+
+/* Login to the APC Master Switch */
+
+static int
+MSLogin(struct pluginDevice * ms)
+{
+ EXPECT(ms->rdfd, EscapeChar, 10);
+
+ /*
+ * We should be looking at something like this:
+ * User Name :
+ */
+ EXPECT(ms->rdfd, login, 10);
+ SEND(ms->wrfd, ms->user);
+ SEND(ms->wrfd, "\r");
+
+ /* Expect "Password :" */
+ EXPECT(ms->rdfd, password, 10);
+ SEND(ms->wrfd, ms->passwd);
+ SEND(ms->wrfd, "\r");
+
+ switch (StonithLookFor(ms->rdfd, LoginOK, 30)) {
+
+ case 0: /* Good! */
+ LOG(PIL_INFO, "Successful login to %s.", ms->idinfo);
+ break;
+
+ case 1: /* Uh-oh - bad password */
+ LOG(PIL_CRIT, "Invalid password for %s.", ms->idinfo);
+ return(S_ACCESS);
+
+ default:
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+
+ return(S_OK);
+}
+
+/* Attempt to login up to 20 times... */
+
+static int
+MSRobustLogin(struct pluginDevice * ms)
+{
+ int rc = S_OOPS;
+ int j = 0;
+
+ for ( ; ; ) {
+ if (MS_connect_device(ms) == S_OK) {
+ rc = MSLogin(ms);
+ if( rc == S_OK ) {
+ break;
+ }
+ }
+ if ((++j) == 20) {
+ break;
+ } else {
+ sleep(1);
+ }
+ }
+
+ return rc;
+}
+
+/* Log out of the APC Master Switch */
+
+static
+int MSLogout(struct pluginDevice* ms)
+{
+ int rc;
+
+ /* Make sure we're in the right menu... */
+ /*SEND(ms->wrfd, "\033\033\033\033\033\033\033"); */
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ /* Expect "> " */
+ rc = StonithLookFor(ms->rdfd, Prompt, 5);
+
+ /* "4" is logout */
+ SEND(ms->wrfd, "4\r");
+
+ close(ms->wrfd);
+ close(ms->rdfd);
+ ms->wrfd = ms->rdfd = -1;
+
+ return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS));
+}
+/* Reset (power-cycle) the given outlets */
+static int
+MSReset(struct pluginDevice* ms, int outletNum, const char *host)
+{
+ char unum[32];
+
+ /* Make sure we're in the top level menu */
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ /* Expect ">" */
+ EXPECT(ms->rdfd, Prompt, 5);
+
+ /* Request menu 1 (Device Control) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Select requested outlet */
+ EXPECT(ms->rdfd, Prompt, 5);
+ snprintf(unum, sizeof(unum), "%i\r", outletNum);
+ SEND(ms->wrfd, unum);
+
+ /* Select menu 1 (Control Outlet) */
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "1\r");
+
+ /* Select menu 3 (Immediate Reboot) */
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "3\r");
+
+ /* Expect "Press <ENTER> " or "Enter 'YES'" (if confirmation turned on) */
+ retry:
+ switch (StonithLookFor(ms->rdfd, Processing, 5)) {
+ case 0: /* Got "Press <ENTER>" Do so */
+ SEND(ms->wrfd, "\r");
+ break;
+
+ case 1: /* Got that annoying command confirmation :-( */
+ SEND(ms->wrfd, "YES\r");
+ goto retry;
+
+ default:
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+
+
+ LOG(PIL_INFO, "Host being rebooted: %s", host);
+
+ /* Expect ">" */
+ if (StonithLookFor(ms->rdfd, Prompt, 10) < 0) {
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+
+ /* All Right! Power is back on. Life is Good! */
+
+ LOG(PIL_INFO, "Power restored to host: %s", host);
+
+ /* Return to top level menu */
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ return(S_OK);
+}
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+static int
+apcmaster_onoff(struct pluginDevice* ms, int outletNum, const char * unitid, int req)
+{
+ char unum[32];
+
+ const char * onoff = (req == ST_POWERON ? "1\r" : "2\r");
+ int rc;
+
+ if ((rc = MSRobustLogin(ms) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
+ return(rc);
+ }
+
+ /* Make sure we're in the top level menu */
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ /* Expect ">" */
+ EXPECT(ms->rdfd, Prompt, 5);
+
+ /* Request menu 1 (Device Control) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Select requested outlet */
+ snprintf(unum, sizeof(unum), "%d\r", outletNum);
+ SEND(ms->wrfd, unum);
+
+ /* Select menu 1 (Control Outlet) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Send ON/OFF command for given outlet */
+ SEND(ms->wrfd, onoff);
+
+ /* Expect "Press <ENTER> " or "Enter 'YES'" (if confirmation turned on) */
+ retry:
+ switch (StonithLookFor(ms->rdfd, Processing, 5)) {
+ case 0: /* Got "Press <ENTER>" Do so */
+ SEND(ms->wrfd, "\r");
+ break;
+
+ case 1: /* Got that annoying command confirmation :-( */
+ SEND(ms->wrfd, "YES\r");
+ goto retry;
+
+ default:
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+
+ EXPECT(ms->rdfd, Prompt, 10);
+
+ /* All Right! Command done. Life is Good! */
+ LOG(PIL_INFO, "Power to MS outlet(s) %d turned %s", outletNum, onoff);
+ /* Pop back to main menu */
+ SEND(ms->wrfd, "\033\033\033\033\033\033\033\r");
+ return(S_OK);
+}
+#endif /* defined(ST_POWERON) && defined(ST_POWEROFF) */
+
+/*
+ * Map the given host name into an (AC) Outlet number on the power strip
+ */
+
+static int
+MSNametoOutlet(struct pluginDevice* ms, const char * name)
+{
+ char NameMapping[128];
+ int sockno;
+ char sockname[32];
+ int times = 0;
+ int ret = -1;
+
+ /* Verify that we're in the top-level menu */
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ /* Expect ">" */
+ EXPECT(ms->rdfd, Prompt, 5);
+
+ /* Request menu 1 (Device Control) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Expect: "-----" so we can skip over it... */
+ EXPECT(ms->rdfd, Separator, 5);
+ EXPECT(ms->rdfd, CRNL, 5);
+ EXPECT(ms->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+
+ do {
+ times++;
+ NameMapping[0] = EOS;
+ SNARF(ms->rdfd, NameMapping, 5);
+ if (sscanf(NameMapping
+ , "%d- %23c",&sockno, sockname) == 2) {
+
+ char * last = sockname+23;
+ *last = EOS;
+ --last;
+
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (strcasecmp(name, sockname) == 0) {
+ ret = sockno;
+ }
+ }
+ } while (strlen(NameMapping) > 2 && times < 8);
+
+ /* Pop back out to the top level menu */
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ return(ret);
+}
+
+static int
+apcmaster_status(StonithPlugin *s)
+{
+ struct pluginDevice* ms;
+ int rc;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ms = (struct pluginDevice*) s;
+
+ if ((rc = MSRobustLogin(ms) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
+ return(rc);
+ }
+
+ /* Expect ">" */
+ SEND(ms->wrfd, "\033\r");
+ EXPECT(ms->rdfd, Prompt, 5);
+
+ return(MSLogout(ms));
+}
+
+/*
+ * Return the list of hosts (outlet names) for the devices on this MS unit
+ */
+
+static char **
+apcmaster_hostlist(StonithPlugin *s)
+{
+ char NameMapping[128];
+ char* NameList[64];
+ unsigned int numnames = 0;
+ char ** ret = NULL;
+ struct pluginDevice* ms;
+ unsigned int i;
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ ms = (struct pluginDevice*) s;
+
+ if (MSRobustLogin(ms) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
+ return(NULL);
+ }
+
+ /* Expect ">" */
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+
+ /* Request menu 1 (Device Control) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Expect: "-----" so we can skip over it... */
+ NULLEXPECT(ms->rdfd, Separator, 5);
+ NULLEXPECT(ms->rdfd, CRNL, 5);
+ NULLEXPECT(ms->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+ do {
+ int sockno;
+ char sockname[64];
+ NameMapping[0] = EOS;
+ NULLSNARF(ms->rdfd, NameMapping, 5);
+ if (sscanf(NameMapping
+ , "%d- %23c",&sockno, sockname) == 2) {
+
+ char * last = sockname+23;
+ char * nm;
+ *last = EOS;
+ --last;
+
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (numnames >= DIMOF(NameList)-1) {
+ break;
+ }
+ if ((nm = (char*)STRDUP(sockname)) == NULL) {
+ goto out_of_memory;
+ }
+ strdown(nm);
+ NameList[numnames] = nm;
+ ++numnames;
+ NameList[numnames] = NULL;
+ }
+ } while (strlen(NameMapping) > 2);
+
+ /* Pop back out to the top level menu */
+ SEND(ms->wrfd, "\033");
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+ SEND(ms->wrfd, "\033");
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+ SEND(ms->wrfd, "\033");
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+ SEND(ms->wrfd, "\033");
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+
+
+ if (numnames >= 1) {
+ ret = (char **)MALLOC((numnames+1)*sizeof(char*));
+ if (ret == NULL) {
+ goto out_of_memory;
+ }else{
+ memcpy(ret, NameList, (numnames+1)*sizeof(char*));
+ }
+ }
+ (void)MSLogout(ms);
+ return(ret);
+
+out_of_memory:
+ LOG(PIL_CRIT, "out of memory");
+ for (i=0; i<numnames; i++) {
+ FREE(NameList[i]);
+ }
+ return(NULL);
+}
+
+/*
+ * Connect to the given MS device. We should add serial support here
+ * eventually...
+ */
+static int
+MS_connect_device(struct pluginDevice * ms)
+{
+ int fd = OurImports->OpenStreamSocket(ms->device
+ , TELNET_PORT, TELNET_SERVICE);
+
+ if (fd < 0) {
+ return(S_OOPS);
+ }
+ ms->rdfd = ms->wrfd = fd;
+ return(S_OK);
+}
+
+/*
+ * Reset the given host on this StonithPlugin device.
+ */
+static int
+apcmaster_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = 0;
+ int lorc = 0;
+ struct pluginDevice* ms;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ms = (struct pluginDevice*) s;
+
+ if ((rc = MSRobustLogin(ms)) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
+ return(rc);
+ }else{
+ int noutlet;
+ noutlet = MSNametoOutlet(ms, host);
+ if (noutlet < 1) {
+ LOG(PIL_WARN, "%s doesn't control host [%s]"
+ , ms->device, host);
+ return(S_BADHOST);
+ }
+ switch(request) {
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+ case ST_POWERON:
+ rc = apcmaster_onoff(ms, noutlet, host, request);
+ break;
+ case ST_POWEROFF:
+ rc = apcmaster_onoff(ms, noutlet, host, request);
+ break;
+#endif
+ case ST_GENERIC_RESET:
+ rc = MSReset(ms, noutlet, host);
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+ }
+
+ lorc = MSLogout(ms);
+ return(rc != S_OK ? rc : lorc);
+}
+
+/*
+ * Get the configuration parameters names
+ */
+static const char * const *
+apcmaster_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_LOGIN, ST_PASSWD, NULL};
+ return ret;
+}
+
+/*
+ * Set the configuration parameters
+ */
+static int
+apcmaster_set_config(StonithPlugin * s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->device = namestocopy[0].s_value;
+ sd->user = namestocopy[1].s_value;
+ sd->passwd = namestocopy[2].s_value;
+
+ return(S_OK);
+}
+
+static const char *
+apcmaster_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* ms;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ ms = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ms->idinfo;
+ break;
+
+ case ST_DEVICENAME: /* Which particular device? */
+ ret = ms->device;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "APC MasterSwitch (via telnet)\n"
+ "NOTE: The APC MasterSwitch accepts only one (telnet)\n"
+ "connection/session a time. When one session is active,\n"
+ "subsequent attempts to connect to the MasterSwitch"
+ " will fail.";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.apc.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = apcmasterXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * APC MasterSwitch StonithPlugin destructor...
+ */
+static void
+apcmaster_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* ms;
+
+ VOIDERRIFWRONGDEV(s);
+
+ ms = (struct pluginDevice *)s;
+
+ ms->pluginid = NOTpluginID;
+ if (ms->rdfd >= 0) {
+ close(ms->rdfd);
+ ms->rdfd = -1;
+ }
+ if (ms->wrfd >= 0) {
+ close(ms->wrfd);
+ ms->wrfd = -1;
+ }
+ if (ms->device != NULL) {
+ FREE(ms->device);
+ ms->device = NULL;
+ }
+ if (ms->user != NULL) {
+ FREE(ms->user);
+ ms->user = NULL;
+ }
+ if (ms->passwd != NULL) {
+ FREE(ms->passwd);
+ ms->passwd = NULL;
+ }
+ FREE(ms);
+}
+
+/* Create a new APC Master Switch StonithPlugin device. */
+
+static StonithPlugin *
+apcmaster_new(const char *subplugin)
+{
+ struct pluginDevice* ms = ST_MALLOCT(struct pluginDevice);
+
+ if (ms == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(ms, 0, sizeof(*ms));
+ ms->pluginid = pluginid;
+ ms->pid = -1;
+ ms->rdfd = -1;
+ ms->wrfd = -1;
+ ms->user = NULL;
+ ms->device = NULL;
+ ms->passwd = NULL;
+ ms->idinfo = DEVICE;
+ ms->sp.s_ops = &apcmasterOps;
+
+ return(&(ms->sp));
+}