summaryrefslogtreecommitdiffstats
path: root/lib/plugins/stonith/baytech.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/plugins/stonith/baytech.c')
-rw-r--r--lib/plugins/stonith/baytech.c924
1 files changed, 924 insertions, 0 deletions
diff --git a/lib/plugins/stonith/baytech.c b/lib/plugins/stonith/baytech.c
new file mode 100644
index 0000000..33093ad
--- /dev/null
+++ b/lib/plugins/stonith/baytech.c
@@ -0,0 +1,924 @@
+/*
+ * Stonith module for BayTech Remote Power Controllers (RPC-x devices)
+ *
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * 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 "BayTech power switch"
+
+#define DOESNT_USE_STONITHKILLCOMM 1
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN baytech
+#define PIL_PLUGIN_S "baytech"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * baytech_new(const char *);
+static void baytech_destroy(StonithPlugin *);
+static int baytech_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * baytech_get_confignames(StonithPlugin * s);
+static const char * baytech_get_info(StonithPlugin * s, int InfoType);
+static int baytech_status(StonithPlugin *);
+static int baytech_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** baytech_hostlist(StonithPlugin *);
+
+static struct stonith_ops baytechOps ={
+ baytech_new, /* Create new STONITH object */
+ baytech_destroy, /* Destroy STONITH object */
+ baytech_get_info, /* Return STONITH info string */
+ baytech_get_confignames, /* Return STONITH config vars */
+ baytech_set_config, /* set configuration from vars */
+ baytech_status, /* Return STONITH device status */
+ baytech_reset_req, /* Request a reset */
+ baytech_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"
+
+#define MAXOUTLET 32
+
+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
+ , &baytechOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * I have an RPC-5. This code has been tested with this switch.
+ *
+ * The BayTech switches are quite nice, but the dialogues are a bit of a
+ * pain for mechanical parsing.
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ char * idinfo;
+ char * unitid;
+ const struct BayTechModelInfo* modelinfo;
+ pid_t pid;
+ int rdfd;
+ int wrfd;
+ char * device;
+ char * user;
+ char * passwd;
+};
+
+struct BayTechModelInfo {
+ const char * type; /* Baytech model info */
+ size_t socklen; /* Length of socket name string */
+ struct Etoken * expect; /* Expect string before outlet list */
+};
+
+static int parse_socket_line(struct pluginDevice*,const char *
+, int *, char *);
+
+static const char * pluginid = "BayTech-Stonith";
+static const char * NOTpluginID = "BayTech device has been destroyed";
+
+/*
+ * Different expect strings that we get from the Baytech
+ * Remote Power Controllers...
+ */
+
+#define BAYTECHASSOC "Bay Technical Associates"
+
+static struct Etoken BayTechAssoc[] = { {BAYTECHASSOC, 0, 0}, {NULL,0,0}};
+static struct Etoken UnitId[] = { {"Unit ID: ", 0, 0}, {NULL,0,0}};
+static struct Etoken login[] = { {"username>", 0, 0} ,{NULL,0,0}};
+static struct Etoken password[] = { {"password>", 0, 0}
+ , {"username>", 0, 0} ,{NULL,0,0}};
+static struct Etoken Selection[] = { {"election>", 0, 0} ,{NULL,0,0}};
+static struct Etoken RPC[] = { {"RPC", 0, 0} ,{NULL,0,0}};
+static struct Etoken LoginOK[] = { {"RPC", 0, 0}, {"Invalid password", 1, 0}
+ , {NULL,0,0}};
+static struct Etoken GTSign[] = { {">", 0, 0} ,{NULL,0,0}};
+static struct Etoken Menu[] = { {"Menu:", 0, 0} ,{NULL,0,0}};
+static struct Etoken Temp[] = { {"emperature: ", 0, 0}
+ , {NULL,0,0}};
+static struct Etoken Break[] = { {"Status", 0, 0}
+ , {NULL,0,0}};
+static struct Etoken PowerApplied[] = { {"ower applied to outlet", 0, 0}
+ , {NULL,0,0}};
+
+/* We may get a notice about rebooting, or a request for confirmation */
+static struct Etoken Rebooting[] = { {"ebooting selected outlet", 0, 0}
+ , {"(Y/N)>", 1, 0}
+ , {"already off.", 2, 0}
+ , {NULL,0,0}};
+
+static struct Etoken TurningOnOff[] = { {"RPC", 0, 0}
+ , {"(Y/N)>", 1, 0}
+ , {"already ", 2, 0}
+ , {NULL,0,0}};
+
+
+static struct BayTechModelInfo ModelInfo [] = {
+ {"BayTech RPC-5", 18, Temp},/* This first model will be the default */
+ {"BayTech RPC-3", 10, Break},
+ {"BayTech RPC-3A", 10, Break},
+ {NULL, 0, NULL},
+};
+
+#include "stonith_config_xml.h"
+
+static const char *baytechXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+static int RPC_connect_device(struct pluginDevice * bt);
+static int RPCLogin(struct pluginDevice * bt);
+static int RPCRobustLogin(struct pluginDevice * bt);
+static int RPCNametoOutletList(struct pluginDevice*, const char * name
+, int outletlist[]);
+static int RPCReset(struct pluginDevice*, int unitnum, const char * rebootid);
+static int RPCLogout(struct pluginDevice * bt);
+
+
+static int RPC_onoff(struct pluginDevice*, int unitnum, const char * unitid
+, int request);
+
+/* Login to the Baytech Remote Power Controller (RPC) */
+
+static int
+RPCLogin(struct pluginDevice * bt)
+{
+ char IDinfo[128];
+ static char IDbuf[128];
+ char * idptr = IDinfo;
+ char * delim;
+ int j;
+
+ EXPECT(bt->rdfd, RPC, 10);
+
+ /* Look for the unit type info */
+ if (EXPECT_TOK(bt->rdfd, BayTechAssoc, 2, IDinfo
+ , sizeof(IDinfo), Debug) < 0) {
+ LOG(PIL_CRIT, "No initial response from %s.", bt->idinfo);
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+ idptr += strspn(idptr, WHITESPACE);
+ /*
+ * We should be looking at something like this:
+ * RPC-5 Telnet Host
+ * Revision F 4.22, (C) 1999
+ * Bay Technical Associates
+ */
+
+ /* Truncate the result after the RPC-5 part */
+ if ((delim = strchr(idptr, ' ')) != NULL) {
+ *delim = EOS;
+ }
+ snprintf(IDbuf, sizeof(IDbuf), "BayTech RPC%s", idptr);
+ REPLSTR(bt->idinfo, IDbuf);
+ if (bt->idinfo == NULL) {
+ return(S_OOPS);
+ }
+
+ bt->modelinfo = &ModelInfo[0];
+
+ for (j=0; ModelInfo[j].type != NULL; ++j) {
+ /*
+ * TIMXXX -
+ * Look at device ID as this really describes the model.
+ */
+ if (strcasecmp(ModelInfo[j].type, IDbuf) == 0) {
+ bt->modelinfo = &ModelInfo[j];
+ break;
+ }
+ }
+
+ /* Look for the unit id info */
+ EXPECT(bt->rdfd, UnitId, 10);
+ SNARF(bt->rdfd, IDbuf, 2);
+ delim = IDbuf + strcspn(IDbuf, WHITESPACE);
+ *delim = EOS;
+ REPLSTR(bt->unitid, IDbuf);
+ if (bt->unitid == NULL) {
+ return(S_OOPS);
+ }
+
+ /* Expect "username>" */
+ EXPECT(bt->rdfd, login, 2);
+
+ SEND(bt->wrfd, bt->user);
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "password>" */
+
+ switch (StonithLookFor(bt->rdfd, password, 5)) {
+ case 0: /* Good! */
+ break;
+
+ case 1: /* OOPS! got another username prompt */
+ LOG(PIL_CRIT, "Invalid username for %s.", bt->idinfo);
+ return(S_ACCESS);
+
+ default:
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+
+ SEND(bt->wrfd, bt->passwd);
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "RPC-x Menu" */
+
+ switch (StonithLookFor(bt->rdfd, LoginOK, 5)) {
+
+ case 0: /* Good! */
+ break;
+
+ case 1: /* Uh-oh - bad password */
+ LOG(PIL_CRIT, "Invalid password for %s.", bt->idinfo);
+ return(S_ACCESS);
+
+ default:
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+ EXPECT(bt->rdfd, Menu, 2);
+
+ return(S_OK);
+}
+
+static int
+RPCRobustLogin(struct pluginDevice * bt)
+{
+ int rc=S_OOPS;
+ int j;
+
+ for (j=0; j < 20 && rc != S_OK; ++j) {
+
+
+ if (RPC_connect_device(bt) != S_OK) {
+ continue;
+ }
+
+ rc = RPCLogin(bt);
+ }
+ return rc;
+}
+
+/* Log out of the Baytech RPC */
+
+static int
+RPCLogout(struct pluginDevice* bt)
+{
+ int rc;
+
+ /* Make sure we're in the right menu... */
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "Selection>" */
+ rc = StonithLookFor(bt->rdfd, Selection, 5);
+
+ /* Option 6 is Logout */
+ SEND(bt->wrfd, "6\r");
+
+ close(bt->wrfd);
+ close(bt->rdfd);
+ bt->wrfd = bt->rdfd = -1;
+ return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS));
+}
+
+/* Reset (power-cycle) the given outlet number */
+static int
+RPCReset(struct pluginDevice* bt, int unitnum, const char * rebootid)
+{
+ char unum[32];
+
+
+ SEND(bt->wrfd, "\r");
+
+ /* Make sure we're in the top level menu */
+
+ /* Expect "RPC-x Menu" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, Menu, 5);
+
+ /* OK. Request sub-menu 1 (Outlet Control) */
+ SEND(bt->wrfd, "1\r");
+
+ /* Verify that we're in the sub-menu */
+
+ /* Expect: "RPC-x>" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, GTSign, 5);
+
+
+ /* Send REBOOT command for given outlet */
+ snprintf(unum, sizeof(unum), "REBOOT %d\r", unitnum);
+ SEND(bt->wrfd, unum);
+
+ /* Expect "ebooting "... or "(Y/N)" (if confirmation turned on) */
+
+ retry:
+ switch (StonithLookFor(bt->rdfd, Rebooting, 5)) {
+ case 0: /* Got "Rebooting" Do nothing */
+ break;
+
+ case 1: /* Got that annoying command confirmation :-( */
+ SEND(bt->wrfd, "Y\r");
+ goto retry;
+
+ case 2: /* Outlet is turned off */
+ LOG(PIL_CRIT, "Host is OFF: %s.", rebootid);
+ return(S_ISOFF);
+
+ default:
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+ LOG(PIL_INFO, "Host %s (outlet %d) being rebooted."
+ , rebootid, unitnum);
+
+ /* Expect "ower applied to outlet" */
+ if (StonithLookFor(bt->rdfd, PowerApplied, 30) < 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 (outlet %d)."
+ , rebootid, unitnum);
+
+ /* Expect: "RPC-x>" */
+ EXPECT(bt->rdfd, RPC,5);
+ EXPECT(bt->rdfd, GTSign, 5);
+
+ /* Pop back to main menu */
+ SEND(bt->wrfd, "MENU\r");
+ return(S_OK);
+}
+
+static int
+RPC_onoff(struct pluginDevice* bt, int unitnum, const char * unitid, int req)
+{
+ char unum[32];
+
+ const char * onoff = (req == ST_POWERON ? "on" : "off");
+ int rc;
+
+
+ if ((rc = RPCRobustLogin(bt) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s."
+ , bt->idinfo ? bt->idinfo : DEVICE);
+ return(rc);
+ }
+ SEND(bt->wrfd, "\r");
+
+ /* Make sure we're in the top level menu */
+
+ /* Expect "RPC-x Menu" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, Menu, 5);
+
+ /* OK. Request sub-menu 1 (Outlet Control) */
+ SEND(bt->wrfd, "1\r");
+
+ /* Verify that we're in the sub-menu */
+
+ /* Expect: "RPC-x>" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, GTSign, 5);
+
+
+ /* Send ON/OFF command for given outlet */
+ snprintf(unum, sizeof(unum), "%s %d\r"
+ , onoff, unitnum);
+ SEND(bt->wrfd, unum);
+
+ /* Expect "RPC->x "... or "(Y/N)" (if confirmation turned on) */
+
+ if (StonithLookFor(bt->rdfd, TurningOnOff, 10) == 1) {
+ /* They've turned on that annoying command confirmation :-( */
+ SEND(bt->wrfd, "Y\r");
+ EXPECT(bt->rdfd, TurningOnOff, 10);
+ }
+
+ EXPECT(bt->rdfd, GTSign, 10);
+
+ /* All Right! Command done. Life is Good! */
+ LOG(PIL_INFO, "Power to host %s (outlet %d) turned %s."
+ , unitid, unitnum, onoff);
+ /* Pop back to main menu */
+ SEND(bt->wrfd, "MENU\r");
+ return(S_OK);
+}
+
+/*
+ * Map the given host name into an (AC) Outlet number on the power strip
+ */
+
+static int
+RPCNametoOutletList(struct pluginDevice* bt, const char * name
+, int outletlist[])
+{
+ char NameMapping[128];
+ int sockno;
+ char sockname[32];
+ int maxfound = 0;
+
+
+
+ /* Verify that we're in the top-level menu */
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "RPC-x Menu" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, Menu, 5);
+
+
+ /* OK. Request sub-menu 1 (Outlet Control) */
+ SEND(bt->wrfd, "1\r");
+
+ /* Verify that we're in the sub-menu */
+
+ /* Expect: "RPC-x>" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, GTSign, 5);
+
+ /* The status command output contains mapping of hosts to outlets */
+ SEND(bt->wrfd, "STATUS\r");
+
+ /* Expect: "emperature:" so we can skip over it... */
+ EXPECT(bt->rdfd, bt->modelinfo->expect, 5);
+ EXPECT(bt->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+
+ do {
+ char * last;
+ NameMapping[0] = EOS;
+ SNARF(bt->rdfd, NameMapping, 5);
+
+ if (!parse_socket_line(bt, NameMapping, &sockno, sockname)) {
+ continue;
+ }
+
+ last = sockname+bt->modelinfo->socklen;
+ *last = EOS;
+ --last;
+
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (strcasecmp(name, sockname) == 0) {
+ outletlist[maxfound] = sockno;
+ ++maxfound;
+ }
+ } while (strlen(NameMapping) > 2 && maxfound < MAXOUTLET);
+
+ /* Pop back out to the top level menu */
+ SEND(bt->wrfd, "MENU\r");
+ return(maxfound);
+}
+
+static int
+baytech_status(StonithPlugin *s)
+{
+ struct pluginDevice* bt;
+ int rc;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ bt = (struct pluginDevice*) s;
+
+ if ((rc = RPCRobustLogin(bt) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s."
+ , bt->idinfo ? bt->idinfo : DEVICE);
+ return(rc);
+ }
+
+ /* Verify that we're in the top-level menu */
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "RPC-x Menu" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, Menu, 5);
+
+ return(RPCLogout(bt));
+}
+/*
+ * Return the list of hosts (outlet names) for the devices on this BayTech unit
+ */
+
+static char **
+baytech_hostlist(StonithPlugin *s)
+{
+ char NameMapping[128];
+ char* NameList[64];
+ unsigned int numnames = 0;
+ char ** ret = NULL;
+ struct pluginDevice* bt;
+ unsigned int i;
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ bt = (struct pluginDevice*) s;
+
+ if (RPCRobustLogin(bt) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s."
+ , bt->idinfo ? bt->idinfo : DEVICE);
+ return(NULL);
+ }
+
+ /* Verify that we're in the top-level menu */
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "RPC-x Menu" */
+ NULLEXPECT(bt->rdfd, RPC, 5);
+ NULLEXPECT(bt->rdfd, Menu, 5);
+
+ /* OK. Request sub-menu 1 (Outlet Control) */
+ SEND(bt->wrfd, "1\r");
+
+ /* Verify that we're in the sub-menu */
+
+ /* Expect: "RPC-x>" */
+ NULLEXPECT(bt->rdfd, RPC, 5);
+ NULLEXPECT(bt->rdfd, GTSign, 5);
+
+ /* The status command output contains mapping of hosts to outlets */
+ SEND(bt->wrfd, "STATUS\r");
+
+ /* Expect: "emperature:" so we can skip over it... */
+ NULLEXPECT(bt->rdfd, bt->modelinfo->expect, 5);
+ NULLEXPECT(bt->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+
+ do {
+ int sockno;
+ char sockname[64];
+ char * last;
+ char * nm;
+
+ NameMapping[0] = EOS;
+
+ NULLSNARF(bt->rdfd, NameMapping, 5);
+
+ if (!parse_socket_line(bt, NameMapping, &sockno, sockname)) {
+ continue;
+ }
+
+ last = sockname+bt->modelinfo->socklen;
+ *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(bt->wrfd, "MENU\r");
+ 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)RPCLogout(bt);
+ 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 BayTech device.
+ * We should add serial support here eventually...
+ */
+static int
+RPC_connect_device(struct pluginDevice * bt)
+{
+ int fd = OurImports->OpenStreamSocket(bt->device
+ , TELNET_PORT, TELNET_SERVICE);
+
+ if (fd < 0) {
+ return(S_OOPS);
+ }
+ bt->rdfd = bt->wrfd = fd;
+ return(S_OK);
+}
+
+/*
+ * Reset the given host on this Stonith device.
+ */
+static int
+baytech_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = S_OK;
+ int lorc = 0;
+ struct pluginDevice* bt;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ bt = (struct pluginDevice*) s;
+
+ if ((rc = RPCRobustLogin(bt)) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s."
+ , bt->idinfo ? bt->idinfo : DEVICE);
+ }else{
+ int noutlets;
+ int outlets[MAXOUTLET];
+ int j;
+ noutlets = RPCNametoOutletList(bt, host, outlets);
+
+ if (noutlets < 1) {
+ LOG(PIL_CRIT, "%s %s doesn't control host [%s]"
+ , bt->idinfo, bt->unitid, host);
+ return(S_BADHOST);
+ }
+ switch(request) {
+
+ case ST_POWERON:
+ case ST_POWEROFF:
+ for (j=0; rc == S_OK && j < noutlets;++j) {
+ rc = RPC_onoff(bt, outlets[j], host, request);
+ }
+ break;
+ case ST_GENERIC_RESET:
+ /*
+ * Our strategy here:
+ * 1. Power off all outlets except the last one
+ * 2. reset the last outlet
+ * 3. power the other outlets back on
+ */
+
+ for (j=0; rc == S_OK && j < noutlets-1; ++j) {
+ rc = RPC_onoff(bt,outlets[j],host
+ , ST_POWEROFF);
+ }
+ if (rc == S_OK) {
+ rc = RPCReset(bt, outlets[j], host);
+ }
+ for (j=0; rc == S_OK && j < noutlets-1; ++j) {
+ rc = RPC_onoff(bt, outlets[j], host
+ , ST_POWERON);
+ }
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+ }
+
+ lorc = RPCLogout(bt);
+
+ return(rc != S_OK ? rc : lorc);
+}
+
+static const char * const *
+baytech_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_LOGIN, ST_PASSWD, NULL};
+ return ret;
+}
+
+
+/*
+ * Parse the config information in the given string, and stash it away...
+ */
+static int
+baytech_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ struct pluginDevice* bt = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (bt->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc =OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ bt->device = namestocopy[0].s_value;
+ bt->user = namestocopy[1].s_value;
+ bt->passwd = namestocopy[2].s_value;
+
+ return(S_OK);
+}
+
+static const char *
+baytech_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* bt;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ bt = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+
+ case ST_DEVICEID: /* What type of device? */
+ ret = bt->idinfo;
+ break;
+
+ case ST_DEVICENAME: /* Which particular device? */
+ ret = bt->device;
+ break;
+
+ case ST_DEVICEDESCR: /* Description of dev type */
+ ret = "Bay Technical Associates (Baytech) RPC "
+ "series power switches (via telnet).\n"
+ "The RPC-5, RPC-3 and RPC-3A switches are well tested"
+ ".";
+ break;
+
+ case ST_DEVICEURL: /* Manufacturer's web site */
+ ret = "http://www.baytech.net/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = baytechXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Baytech Stonith destructor...
+ */
+static void
+baytech_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* bt;
+
+ VOIDERRIFWRONGDEV(s);
+
+ bt = (struct pluginDevice *)s;
+
+ bt->pluginid = NOTpluginID;
+ if (bt->rdfd >= 0) {
+ close(bt->rdfd);
+ bt->rdfd = -1;
+ }
+ if (bt->wrfd >= 0) {
+ close(bt->wrfd);
+ bt->wrfd = -1;
+ }
+ if (bt->device != NULL) {
+ FREE(bt->device);
+ bt->device = NULL;
+ }
+ if (bt->user != NULL) {
+ FREE(bt->user);
+ bt->user = NULL;
+ }
+ if (bt->passwd != NULL) {
+ FREE(bt->passwd);
+ bt->passwd = NULL;
+ }
+ if (bt->idinfo != NULL) {
+ FREE(bt->idinfo);
+ bt->idinfo = NULL;
+ }
+ if (bt->unitid != NULL) {
+ FREE(bt->unitid);
+ bt->unitid = NULL;
+ }
+ FREE(bt);
+}
+
+/* Create a new BayTech Stonith device. */
+
+static StonithPlugin *
+baytech_new(const char *subplugin)
+{
+ struct pluginDevice* bt = ST_MALLOCT(struct pluginDevice);
+
+ if (bt == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(bt, 0, sizeof(*bt));
+ bt->pluginid = pluginid;
+ bt->pid = -1;
+ bt->rdfd = -1;
+ bt->wrfd = -1;
+ REPLSTR(bt->idinfo, DEVICE);
+ if (bt->idinfo == NULL) {
+ FREE(bt);
+ return(NULL);
+ }
+ bt->modelinfo = &ModelInfo[0];
+ bt->sp.s_ops = &baytechOps;
+
+ return &(bt->sp); /* same as "bt" */
+}
+
+static int
+parse_socket_line(struct pluginDevice * bt, const char *NameMapping
+, int *sockno, char *sockname)
+{
+#if 0
+ char format[64];
+ snprintf(format, sizeof(format), "%%7d %%%dc"
+ , bt->modelinfo->socklen);
+ /* 7 digits, 7 blanks, then 'socklen' characters */
+ /* [0-6]: digits, NameMapping[13] begins the sockname */
+ /* NameMapping strlen must be >= socklen + 14 */
+
+ if (sscanf(NameMapping, format, sockno, sockname) != 2) {
+ return FALSE;
+ }
+#else
+# define OFFSET 14
+
+ if (sscanf(NameMapping, "%7d", sockno) != 1
+ || strlen(NameMapping) < OFFSET+bt->modelinfo->socklen) {
+ return FALSE;
+ }
+ strncpy(sockname, NameMapping+OFFSET, bt->modelinfo->socklen);
+ sockname[bt->modelinfo->socklen] = EOS;
+#endif
+ return TRUE;
+}