summaryrefslogtreecommitdiffstats
path: root/lib/plugins/stonith
diff options
context:
space:
mode:
Diffstat (limited to 'lib/plugins/stonith')
-rw-r--r--lib/plugins/stonith/Makefile.am216
-rw-r--r--lib/plugins/stonith/apcmaster.c822
-rw-r--r--lib/plugins/stonith/apcmastersnmp.c890
-rw-r--r--lib/plugins/stonith/apcmastersnmp.cfg.example39
-rw-r--r--lib/plugins/stonith/apcsmart.c1028
-rw-r--r--lib/plugins/stonith/apcsmart.cfg.example1
-rw-r--r--lib/plugins/stonith/baytech.c924
-rw-r--r--lib/plugins/stonith/bladehpi.c1101
-rw-r--r--lib/plugins/stonith/cyclades.c650
-rw-r--r--lib/plugins/stonith/drac3.c359
-rw-r--r--lib/plugins/stonith/drac3_command.c342
-rw-r--r--lib/plugins/stonith/drac3_command.h29
-rw-r--r--lib/plugins/stonith/drac3_hash.c106
-rw-r--r--lib/plugins/stonith/drac3_hash.h28
-rw-r--r--lib/plugins/stonith/external.c868
-rw-r--r--lib/plugins/stonith/external/Makefile.am33
-rw-r--r--lib/plugins/stonith/external/drac5.in113
-rw-r--r--lib/plugins/stonith/external/dracmc-telnet377
-rwxr-xr-xlib/plugins/stonith/external/hetzner139
-rw-r--r--lib/plugins/stonith/external/hmchttp218
-rw-r--r--lib/plugins/stonith/external/ibmrsa157
-rw-r--r--lib/plugins/stonith/external/ibmrsa-telnet320
-rw-r--r--lib/plugins/stonith/external/ipmi276
-rwxr-xr-xlib/plugins/stonith/external/ippower9258.in316
-rw-r--r--lib/plugins/stonith/external/kdumpcheck.in274
-rw-r--r--lib/plugins/stonith/external/libvirt298
-rw-r--r--lib/plugins/stonith/external/nut302
-rw-r--r--lib/plugins/stonith/external/rackpdu280
-rw-r--r--lib/plugins/stonith/external/riloe530
-rw-r--r--lib/plugins/stonith/external/ssh.in176
-rwxr-xr-xlib/plugins/stonith/external/vcenter280
-rw-r--r--lib/plugins/stonith/external/vmware216
-rw-r--r--lib/plugins/stonith/external/xen0253
-rwxr-xr-xlib/plugins/stonith/external/xen0-ha-dom0-stonith-helper72
-rwxr-xr-xlib/plugins/stonith/external/xen0-ha.in96
-rw-r--r--lib/plugins/stonith/ibmhmc.c1261
-rw-r--r--lib/plugins/stonith/ipmi_os_handler.c257
-rw-r--r--lib/plugins/stonith/ipmilan.c587
-rw-r--r--lib/plugins/stonith/ipmilan.h41
-rw-r--r--lib/plugins/stonith/ipmilan_command.c399
-rw-r--r--lib/plugins/stonith/ipmilan_test.c63
-rw-r--r--lib/plugins/stonith/meatware.c351
-rw-r--r--lib/plugins/stonith/null.c260
-rw-r--r--lib/plugins/stonith/nw_rpc100s.c779
-rw-r--r--lib/plugins/stonith/rcd_serial.c602
-rw-r--r--lib/plugins/stonith/rhcs.c1035
-rw-r--r--lib/plugins/stonith/ribcl.py.in101
-rw-r--r--lib/plugins/stonith/riloe.c338
-rw-r--r--lib/plugins/stonith/rps10.c1070
-rw-r--r--lib/plugins/stonith/ssh.c351
-rw-r--r--lib/plugins/stonith/stonith_config_xml.h157
-rw-r--r--lib/plugins/stonith/stonith_expect_helpers.h120
-rw-r--r--lib/plugins/stonith/stonith_plugin_common.h127
-rw-r--r--lib/plugins/stonith/stonith_signal.h68
-rw-r--r--lib/plugins/stonith/suicide.c274
-rw-r--r--lib/plugins/stonith/vacm.c485
-rw-r--r--lib/plugins/stonith/wti_mpc.c856
-rw-r--r--lib/plugins/stonith/wti_nps.c813
58 files changed, 22524 insertions, 0 deletions
diff --git a/lib/plugins/stonith/Makefile.am b/lib/plugins/stonith/Makefile.am
new file mode 100644
index 0000000..01f2f4a
--- /dev/null
+++ b/lib/plugins/stonith/Makefile.am
@@ -0,0 +1,216 @@
+#
+# stonith: STONITH plugins for Linux-HA
+#
+# Copyright (C) 2001 Alan Robertson
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = external
+
+INCFILES = stonith_expect_helpers.h \
+ stonith_plugin_common.h \
+ stonith_signal.h \
+ stonith_config_xml.h
+
+idir=$(includedir)/stonith
+
+i_HEADERS = $(INCFILES)
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+
+AM_CFLAGS = @NON_FATAL_CFLAGS@
+
+#
+# We need "vacmclient_api.h" and -lvacmclient to make the VACM
+# plugin work.
+#
+if USE_VACM
+vacm_LIB = vacm.la
+else
+vacm_LIB =
+endif
+
+#
+# We need <ucd-snmp/asn1.h>, <ucd-snmp/snmp_api.h>, <ucd-snmp/snmp.h>
+# <ucd-snmp/snmp_client.h>, <ucd-snmp/mib.h>, -lsnmp and -lcrypto
+# for the apcmastersnmp plugin to work
+#
+
+if USE_APC_SNMP
+apcmastersnmp_LIB = apcmastersnmp.la wti_mpc.la
+else
+apcmastersnmp_LIB =
+endif
+if IPMILAN_BUILD
+OPENIPMI_LIB = -lOpenIPMI -lOpenIPMIposix -lOpenIPMIutils
+libipmilan_LIB = libipmilan.la
+ipmilan_LIB = ipmilan.la
+ipmilan_TEST = ipmilantest
+else
+OPENIPMI_LIB =
+libipmilan_LIB =
+ipmilan_LIB =
+endif
+#
+# We need <curl/curl.h>, <libxml/xmlmemory.h>,
+# <libxml/parser.h>, <libxml/xpath.h>,
+# -lcurl and -lxml2 for the drac3 plugin to work
+#
+if USE_DRAC3
+drac3_LIB = drac3.la
+else
+drac3_LIB =
+endif
+
+#
+# We need OpenHPI to make the IBM BladeCenter plugin work.
+#
+if USE_OPENHPI
+bladehpi_LIB = bladehpi.la
+else
+bladehpi_LIB =
+endif
+
+noinst_PROGRAMS = $(ipmilan_TEST)
+ipmilantest_SOURCES = ipmilan_test.c
+ipmilantest_LDADD = $(libipmilan_LIB) \
+ $(top_builddir)/lib/pils/libpils.la \
+ $(top_builddir)/lib/stonith/libstonith.la \
+ $(OPENIPMI_LIB)
+
+## libraries
+
+plugindir = $(stonith_plugindir)/stonith2
+
+plugin_LTLIBRARIES = apcmaster.la \
+ $(apcmastersnmp_LIB) \
+ apcsmart.la \
+ baytech.la \
+ $(bladehpi_LIB) \
+ cyclades.la \
+ $(drac3_LIB) \
+ external.la \
+ rhcs.la \
+ ibmhmc.la \
+ $(ipmilan_LIB) \
+ meatware.la \
+ null.la \
+ nw_rpc100s.la \
+ rcd_serial.la \
+ rps10.la \
+ ssh.la \
+ suicide.la \
+ $(vacm_LIB) \
+ wti_nps.la
+noinst_LTLIBRARIES = $(libipmilan_LIB)
+
+apcmaster_la_SOURCES = apcmaster.c $(INCFILES)
+apcmaster_la_LDFLAGS = -export-dynamic -module -avoid-version
+apcmaster_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+apcmastersnmp_la_SOURCES= apcmastersnmp.c $(INCFILES)
+apcmastersnmp_la_LDFLAGS= -export-dynamic -module -avoid-version @SNMPLIB@ \
+ @CRYPTOLIB@
+apcmastersnmp_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+apcsmart_la_SOURCES = apcsmart.c $(INCFILES)
+apcsmart_la_LDFLAGS = -export-dynamic -module -avoid-version
+apcsmart_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+baytech_la_SOURCES = baytech.c $(INCFILES)
+baytech_la_LDFLAGS = -export-dynamic -module -avoid-version
+baytech_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+bladehpi_la_SOURCES = bladehpi.c $(INCFILES)
+bladehpi_la_LDFLAGS = -export-dynamic -module -avoid-version
+bladehpi_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB) -lopenhpi
+
+cyclades_la_SOURCES = cyclades.c $(INCFILES)
+cyclades_la_LDFLAGS = -export-dynamic -module -avoid-version
+cyclades_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+drac3_la_SOURCES = drac3.c drac3_command.c drac3_command.h drac3_hash.c drac3_hash.h $(INCFILES)
+drac3_la_LDFLAGS = -export-dynamic -module -avoid-version
+drac3_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la -lcurl -lxml2 $(GLIBLIB)
+
+external_la_SOURCES = external.c $(INCFILES)
+external_la_LDFLAGS = -export-dynamic -module -avoid-version
+external_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la
+
+rhcs_la_SOURCES = rhcs.c $(INCFILES)
+rhcs_la_LDFLAGS = -export-dynamic -module -avoid-version
+rhcs_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la
+
+ibmhmc_la_SOURCES = ibmhmc.c $(INCFILES)
+ibmhmc_la_LDFLAGS = -export-dynamic -module -avoid-version
+ibmhmc_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(GLIBLIB)
+
+ipmilan_la_SOURCES = ipmilan.c ipmilan.h ipmilan_command.c $(INCFILES)
+ipmilan_la_LDFLAGS = -export-dynamic -module -avoid-version
+ipmilan_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(OPENIPMI_LIB) $(GLIBLIB)
+
+libipmilan_la_SOURCES = ipmilan.c ipmilan.h ipmilan_command.c $(INCFILES)
+libipmilan_la_LDFLAGS = -version-info 1:0:0
+libipmilan_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(OPENIPMI_LIB) $(GLIBLIB)
+
+meatware_la_SOURCES = meatware.c $(INCFILES)
+meatware_la_LDFLAGS = -export-dynamic -module -avoid-version
+meatware_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+null_la_SOURCES = null.c $(INCFILES)
+null_la_LDFLAGS = -export-dynamic -module -avoid-version
+null_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+nw_rpc100s_la_SOURCES = nw_rpc100s.c $(INCFILES)
+nw_rpc100s_la_LDFLAGS = -export-dynamic -module -avoid-version
+nw_rpc100s_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(GLIBLIB)
+
+rcd_serial_la_SOURCES = rcd_serial.c $(INCFILES)
+rcd_serial_la_LDFLAGS = -export-dynamic -module -avoid-version
+rcd_serial_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+rps10_la_SOURCES = rps10.c $(INCFILES)
+rps10_la_LDFLAGS = -export-dynamic -module -avoid-version
+rps10_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+ssh_la_SOURCES = ssh.c $(INCFILES)
+ssh_la_LDFLAGS = -export-dynamic -module -avoid-version
+ssh_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la
+
+vacm_la_SOURCES = vacm.c $(INCFILES)
+vacm_la_LDFLAGS = -export-dynamic -module -avoid-version
+vacm_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la
+
+wti_nps_la_SOURCES = wti_nps.c $(INCFILES)
+wti_nps_la_LDFLAGS = -export-dynamic -module -avoid-version
+wti_nps_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(GLIBLIB)
+
+wti_mpc_la_SOURCES= wti_mpc.c $(INCFILES)
+wti_mpc_la_LDFLAGS= -export-dynamic -module -avoid-version @SNMPLIB@ \
+ @CRYPTOLIB@
+wti_mpc_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+suicide_la_SOURCES = suicide.c $(INCFILES)
+suicide_la_LDFLAGS = -export-dynamic -module -avoid-version
+suicide_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la
+
+stonithscriptdir = $(stonith_plugindir)/stonith2
+
+stonithscript_SCRIPTS = ribcl.py
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));
+}
diff --git a/lib/plugins/stonith/apcmastersnmp.c b/lib/plugins/stonith/apcmastersnmp.c
new file mode 100644
index 0000000..a9eeaeb
--- /dev/null
+++ b/lib/plugins/stonith/apcmastersnmp.c
@@ -0,0 +1,890 @@
+/*
+ * Stonith module for APC Masterswitch (SNMP)
+ * Copyright (c) 2001 Andreas Piesk <a.piesk@gmx.net>
+ * Mangled by Sun Jiang Dong <sunjd@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>
+
+/* device ID */
+#define DEVICE "APC MasterSwitch (SNMP)"
+
+#include "stonith_plugin_common.h"
+#undef FREE /* defined by snmp stuff */
+
+#ifdef PACKAGE_BUGREPORT
+#undef PACKAGE_BUGREPORT
+#endif
+#ifdef PACKAGE_NAME
+#undef PACKAGE_NAME
+#endif
+#ifdef PACKAGE_STRING
+#undef PACKAGE_STRING
+#endif
+#ifdef PACKAGE_TARNAME
+#undef PACKAGE_TARNAME
+#endif
+#ifdef PACKAGE_VERSION
+#undef PACKAGE_VERSION
+#endif
+
+#ifdef HAVE_NET_SNMP_NET_SNMP_CONFIG_H
+# include <net-snmp/net-snmp-config.h>
+# include <net-snmp/net-snmp-includes.h>
+# include <net-snmp/agent/net-snmp-agent-includes.h>
+# define INIT_AGENT() init_master_agent()
+#else
+# include <ucd-snmp/ucd-snmp-config.h>
+# include <ucd-snmp/ucd-snmp-includes.h>
+# include <ucd-snmp/ucd-snmp-agent-includes.h>
+# ifndef NETSNMP_DS_APPLICATION_ID
+# define NETSNMP_DS_APPLICATION_ID DS_APPLICATION_ID
+# endif
+# ifndef NETSNMP_DS_AGENT_ROLE
+# define NETSNMP_DS_AGENT_ROLE DS_AGENT_ROLE
+# endif
+# define netsnmp_ds_set_boolean ds_set_boolean
+# define INIT_AGENT() init_master_agent(161, NULL, NULL)
+#endif
+
+#define PIL_PLUGIN apcmastersnmp
+#define PIL_PLUGIN_S "apcmastersnmp"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#define DEBUGCALL \
+ if (Debug) { \
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); \
+ }
+
+static StonithPlugin * apcmastersnmp_new(const char *);
+static void apcmastersnmp_destroy(StonithPlugin *);
+static const char * const * apcmastersnmp_get_confignames(StonithPlugin *);
+static int apcmastersnmp_set_config(StonithPlugin *, StonithNVpair *);
+static const char * apcmastersnmp_getinfo(StonithPlugin * s, int InfoType);
+static int apcmastersnmp_status(StonithPlugin * );
+static int apcmastersnmp_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** apcmastersnmp_hostlist(StonithPlugin *);
+
+static struct stonith_ops apcmastersnmpOps ={
+ apcmastersnmp_new, /* Create new STONITH object */
+ apcmastersnmp_destroy, /* Destroy STONITH object */
+ apcmastersnmp_getinfo, /* Return STONITH info string */
+ apcmastersnmp_get_confignames, /* Get configuration parameters */
+ apcmastersnmp_set_config, /* Set configuration */
+ apcmastersnmp_status, /* Return STONITH device status */
+ apcmastersnmp_reset_req, /* Request a reset */
+ apcmastersnmp_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+ DEBUGCALL;
+
+ 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
+ , &apcmastersnmpOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * APCMaster tested with APC Masterswitch 9212
+ */
+
+/* outlet commands / status codes */
+#define OUTLET_ON 1
+#define OUTLET_OFF 2
+#define OUTLET_REBOOT 3
+#define OUTLET_NO_CMD_PEND 2
+
+/* oids */
+#define OID_IDENT ".1.3.6.1.4.1.318.1.1.12.1.5.0"
+#define OID_NUM_OUTLETS ".1.3.6.1.4.1.318.1.1.12.1.8.0"
+#define OID_OUTLET_NAMES ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.%i"
+#define OID_OUTLET_STATE ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.%i"
+#define OID_OUTLET_COMMAND_PENDING ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.%i"
+#define OID_OUTLET_REBOOT_DURATION ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.%i"
+
+/*
+ snmpset -c private -v1 172.16.0.32:161
+ ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.1" i 1
+ The last octet in the OID is the plug number. The value can
+ be 1 thru 8 because there are 8 power plugs on this device.
+ The integer that can be set is as follows: 1=on, 2=off, and
+ 3=reset
+*/
+
+/* own defines */
+#define MAX_STRING 128
+#define ST_PORT "port"
+
+/* structur of stonith object */
+struct pluginDevice {
+ StonithPlugin sp; /* StonithPlugin object */
+ const char* pluginid; /* id of object */
+ const char* idinfo; /* type of device */
+ struct snmp_session* sptr; /* != NULL->session created */
+ char * hostname; /* masterswitch's hostname */
+ /* or ip addr */
+ int port; /* snmp port */
+ char * community; /* snmp community (r/w) */
+ int num_outlets; /* number of outlets */
+};
+
+/* for checking hardware (issue a warning if mismatch) */
+static const char* APC_tested_ident[] = {"AP9606","AP7920","AP7921","AP7900","AP_other_well_tested"};
+
+/* constant strings */
+static const char *pluginid = "APCMS-SNMP-Stonith";
+static const char *NOTpluginID = "APCMS SNMP device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_PORT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_PORT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PORT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The port number on which the SNMP server is running on the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PORT_PARM \
+ XML_PARAMETER_BEGIN(ST_PORT, "string", "1", "0") \
+ XML_PORT_SHORTDESC \
+ XML_PORT_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *apcmastersnmpXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_PORT_PARM
+ XML_COMMUNITY_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * own prototypes
+ */
+
+static void APC_error(struct snmp_session *sptr, const char *fn
+, const char *msg);
+static struct snmp_session *APC_open(char *hostname, int port
+, char *community);
+static void *APC_read(struct snmp_session *sptr, const char *objname
+, int type);
+static int APC_write(struct snmp_session *sptr, const char *objname
+, char type, char *value);
+
+static void
+APC_error(struct snmp_session *sptr, const char *fn, const char *msg)
+{
+ int snmperr = 0;
+ int cliberr = 0;
+ char *errstr;
+
+ snmp_error(sptr, &cliberr, &snmperr, &errstr);
+ LOG(PIL_CRIT
+ , "%s: %s (cliberr: %i / snmperr: %i / error: %s)."
+ , fn, msg, cliberr, snmperr, errstr);
+ free(errstr);
+}
+
+
+/*
+ * creates a snmp session
+ */
+static struct snmp_session *
+APC_open(char *hostname, int port, char *community)
+{
+ static struct snmp_session session;
+ struct snmp_session *sptr;
+
+ DEBUGCALL;
+
+ /* create session */
+ snmp_sess_init(&session);
+
+ /* fill session */
+ session.peername = hostname;
+ session.version = SNMP_VERSION_1;
+ session.remote_port = port;
+ session.community = (u_char *)community;
+ session.community_len = strlen(community);
+ session.retries = 5;
+ session.timeout = 1000000;
+
+ /* open session */
+ sptr = snmp_open(&session);
+
+ if (sptr == NULL) {
+ APC_error(&session, __FUNCTION__, "cannot open snmp session");
+ }
+
+ /* return pointer to opened session */
+ return (sptr);
+}
+
+/*
+ * parse config
+ */
+
+/*
+ * read value of given oid and return it as string
+ */
+static void *
+APC_read(struct snmp_session *sptr, const char *objname, int type)
+{
+ oid name[MAX_OID_LEN];
+ size_t namelen = MAX_OID_LEN;
+ struct variable_list *vars;
+ struct snmp_pdu *pdu;
+ struct snmp_pdu *resp;
+ static char response_str[MAX_STRING];
+ static int response_int;
+
+ DEBUGCALL;
+
+ /* convert objname into oid; return NULL if invalid */
+ if (!read_objid(objname, name, &namelen)) {
+ LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname);
+ return (NULL);
+ }
+
+ /* create pdu */
+ if ((pdu = snmp_pdu_create(SNMP_MSG_GET)) != NULL) {
+
+ /* get-request have no values */
+ snmp_add_null_var(pdu, name, namelen);
+
+ /* send pdu and get response; return NULL if error */
+ if (snmp_synch_response(sptr, pdu, &resp) == SNMPERR_SUCCESS) {
+
+ /* request succeed, got valid response ? */
+ if (resp->errstat == SNMP_ERR_NOERROR) {
+
+ /* go through the returned vars */
+ for (vars = resp->variables; vars;
+ vars = vars->next_variable) {
+
+ /* return response as string */
+ if ((vars->type == type) && (type == ASN_OCTET_STR)) {
+ memset(response_str, 0, MAX_STRING);
+ strncpy(response_str, (char *)vars->val.string,
+ MIN(vars->val_len, MAX_STRING));
+ snmp_free_pdu(resp);
+ return ((void *) response_str);
+ }
+ /* return response as integer */
+ if ((vars->type == type) && (type == ASN_INTEGER)) {
+ response_int = *vars->val.integer;
+ snmp_free_pdu(resp);
+ return ((void *) &response_int);
+ }
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]."
+ , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat));
+ }
+ }else{
+ APC_error(sptr, __FUNCTION__, "error sending/receiving pdu");
+ }
+ /* free repsonse pdu (necessary?) */
+ snmp_free_pdu(resp);
+ }else{
+ APC_error(sptr, __FUNCTION__, "cannot create pdu");
+ }
+ /* error: return nothing */
+ return (NULL);
+}
+
+/*
+ * write value of given oid
+ */
+static int
+APC_write(struct snmp_session *sptr, const char *objname, char type,
+ char *value)
+{
+ oid name[MAX_OID_LEN];
+ size_t namelen = MAX_OID_LEN;
+ struct snmp_pdu *pdu;
+ struct snmp_pdu *resp;
+
+ DEBUGCALL;
+
+ /* convert objname into oid; return FALSE if invalid */
+ if (!read_objid(objname, name, &namelen)) {
+ LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname);
+ return (FALSE);
+ }
+
+ /* create pdu */
+ if ((pdu = snmp_pdu_create(SNMP_MSG_SET)) != NULL) {
+
+ /* add to be written value to pdu */
+ snmp_add_var(pdu, name, namelen, type, value);
+
+ /* send pdu and get response; return NULL if error */
+ if (snmp_synch_response(sptr, pdu, &resp) == STAT_SUCCESS) {
+
+ /* go through the returned vars */
+ if (resp->errstat == SNMP_ERR_NOERROR) {
+
+ /* request successful done */
+ snmp_free_pdu(resp);
+ return (TRUE);
+
+ }else{
+ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]."
+ , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat));
+ }
+ }else{
+ APC_error(sptr, __FUNCTION__, "error sending/receiving pdu");
+ }
+ /* free pdu (again: necessary?) */
+ snmp_free_pdu(resp);
+ }else{
+ APC_error(sptr, __FUNCTION__, "cannot create pdu");
+ }
+ /* error */
+ return (FALSE);
+}
+
+/*
+ * return the status for this device
+ */
+
+static int
+apcmastersnmp_status(StonithPlugin * s)
+{
+ struct pluginDevice *ad;
+ char *ident;
+ int i;
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ ad = (struct pluginDevice *) s;
+
+ if ((ident = APC_read(ad->sptr, OID_IDENT, ASN_OCTET_STR)) == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read ident.", __FUNCTION__);
+ return (S_ACCESS);
+ }
+
+ /* issue a warning if ident mismatches */
+ for(i=DIMOF(APC_tested_ident) -1; i >=0 ; i--) {
+ if (strcmp(ident, APC_tested_ident[i]) == 0) {
+ break;
+ }
+ }
+
+ if (i<0) {
+ LOG(PIL_WARN
+ , "%s: module not tested with this hardware '%s'."
+ , __FUNCTION__, ident);
+ }
+ /* status ok */
+ return (S_OK);
+}
+
+/*
+ * return the list of hosts configured for this device
+ */
+
+static char **
+apcmastersnmp_hostlist(StonithPlugin * s)
+{
+ char **hl;
+ struct pluginDevice *ad;
+ int j, h, num_outlets;
+ char *outlet_name;
+ char objname[MAX_STRING];
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, NULL);
+
+ ad = (struct pluginDevice *) s;
+
+ /* allocate memory for array of up to NUM_OUTLETS strings */
+ if ((hl = (char **)MALLOC((ad->num_outlets+1) * sizeof(char *))) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+ /* clear hostlist array */
+ memset(hl, 0, (ad->num_outlets + 1) * sizeof(char *));
+ num_outlets = 0;
+
+ /* read NUM_OUTLETS values and put them into hostlist array */
+ for (j = 0; j < ad->num_outlets; ++j) {
+
+ /* prepare objname */
+ snprintf(objname, MAX_STRING, OID_OUTLET_NAMES, j + 1);
+
+ /* read outlet name */
+ if ((outlet_name = APC_read(ad->sptr, objname, ASN_OCTET_STR)) ==
+ NULL) {
+ LOG(PIL_CRIT, "%s: cannot read name for outlet %d."
+ , __FUNCTION__, j+1);
+ stonith_free_hostlist(hl);
+ hl = NULL;
+ return (hl);
+ }
+
+ /* Check whether the host is already listed */
+ for (h = 0; h < num_outlets; ++h) {
+ if (strcasecmp(hl[h],outlet_name) == 0)
+ break;
+ }
+
+ if (h >= num_outlets) {
+ /* put outletname in hostlist */
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: added %s to hostlist."
+ , __FUNCTION__, outlet_name);
+ }
+
+ if ((hl[num_outlets] = STRDUP(outlet_name)) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ stonith_free_hostlist(hl);
+ hl = NULL;
+ return (hl);
+ }
+ strdown(hl[num_outlets]);
+ num_outlets++;
+ }
+ }
+
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %d unique hosts connected to %d outlets."
+ , __FUNCTION__, num_outlets, j);
+ }
+ /* return list */
+ return (hl);
+}
+
+/*
+ * reset the host
+ */
+
+static int
+apcmastersnmp_reset_req(StonithPlugin * s, int request, const char *host)
+{
+ struct pluginDevice *ad;
+ char objname[MAX_STRING];
+ char value[MAX_STRING];
+ char *outlet_name;
+ int req_oid = OUTLET_REBOOT;
+ int expect_state = OUTLET_ON;
+ int i, h, num_outlets, outlet, reboot_duration, *state, bad_outlets;
+ int outlets[8]; /* Assume that one node is connected to a
+ maximum of 8 outlets */
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ ad = (struct pluginDevice *) s;
+
+ num_outlets = 0;
+ reboot_duration = 0;
+ bad_outlets = 0;
+
+ /* read max. as->num_outlets values */
+ for (outlet = 1; outlet <= ad->num_outlets; outlet++) {
+
+ /* prepare objname */
+ snprintf(objname, MAX_STRING, OID_OUTLET_NAMES, outlet);
+
+ /* read outlet name */
+ if ((outlet_name = APC_read(ad->sptr, objname, ASN_OCTET_STR))
+ == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read name for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: found outlet: %s.", __FUNCTION__, outlet_name);
+ }
+
+ /* found one */
+ if (strcasecmp(outlet_name, host) == 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: found %s at outlet %d."
+ , __FUNCTION__, host, outlet);
+ }
+ /* Check that the outlet is not administratively down */
+
+ /* prepare objname */
+ snprintf(objname, MAX_STRING, OID_OUTLET_STATE, outlet);
+
+ /* get outlet's state */
+ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER)) == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read state for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+
+ /* prepare oid */
+ snprintf(objname, MAX_STRING, OID_OUTLET_REBOOT_DURATION
+ , outlet);
+
+ /* read reboot duration of the port */
+ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER))
+ == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read reboot duration for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+ if (num_outlets == 0) {
+ /* save the inital value of the first port */
+ reboot_duration = *state;
+ } else if (reboot_duration != *state) {
+ LOG(PIL_WARN, "%s: outlet %d has a different reboot duration!"
+ , __FUNCTION__, outlet);
+ if (reboot_duration < *state)
+ reboot_duration = *state;
+ }
+
+ /* Ok, add it to the list of outlets to control */
+ outlets[num_outlets]=outlet;
+ num_outlets++;
+ }
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: outlet: %i.", __FUNCTION__, outlet);
+ }
+
+ /* host not found in outlet names */
+ if (num_outlets < 1) {
+ LOG(PIL_CRIT, "%s: no active outlet for '%s'.", __FUNCTION__, host);
+ return (S_BADHOST);
+ }
+
+
+ /* choose the OID for the stonith request */
+ switch (request) {
+ case ST_POWERON:
+ req_oid = OUTLET_ON;
+ expect_state = OUTLET_ON;
+ break;
+ case ST_POWEROFF:
+ req_oid = OUTLET_OFF;
+ expect_state = OUTLET_OFF;
+ break;
+ case ST_GENERIC_RESET:
+ req_oid = OUTLET_REBOOT;
+ expect_state = OUTLET_ON;
+ break;
+ default: break;
+ }
+
+ /* Turn them all off */
+
+ for (outlet=outlets[0], i=0 ; i < num_outlets; i++, outlet = outlets[i]) {
+ /* prepare objname */
+ snprintf(objname, MAX_STRING, OID_OUTLET_COMMAND_PENDING, outlet);
+
+ /* are there pending commands ? */
+ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER)) == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read pending commands for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+
+ if (*state != OUTLET_NO_CMD_PEND) {
+ LOG(PIL_CRIT, "%s: command pending.", __FUNCTION__);
+ return (S_RESETFAIL);
+ }
+
+ /* prepare objnames */
+ snprintf(objname, MAX_STRING, OID_OUTLET_STATE, outlet);
+ snprintf(value, MAX_STRING, "%i", req_oid);
+
+ /* send reboot cmd */
+ if (!APC_write(ad->sptr, objname, 'i', value)) {
+ LOG(PIL_CRIT
+ , "%s: cannot send reboot command for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+ }
+
+ /* wait max. 2*reboot_duration for all outlets to go back on */
+ for (i = 0; i < reboot_duration << 1; i++) {
+
+ sleep(1);
+
+ bad_outlets = 0;
+ for (outlet=outlets[0], h=0 ; h < num_outlets; h++,
+ outlet = outlets[h]) {
+
+ /* prepare objname of the first outlet */
+ snprintf(objname, MAX_STRING, OID_OUTLET_STATE, outlet);
+ /* get outlet's state */
+
+ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER))
+ == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read state for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+
+ if (*state != expect_state)
+ bad_outlets++;
+ }
+
+ if (bad_outlets == 0)
+ return (S_OK);
+ }
+
+ if (bad_outlets == num_outlets) {
+ /* reset failed */
+ LOG(PIL_CRIT, "%s: stonith operation for '%s' failed."
+ , __FUNCTION__, host);
+ return (S_RESETFAIL);
+ } else {
+ /* Not all outlets back on, but at least one; implies node was */
+ /* rebooted correctly */
+ LOG(PIL_WARN,"%s: Not all outlets in the expected state!"
+ , __FUNCTION__);
+ return (S_OK);
+ }
+}
+
+/*
+ * Get the configuration parameter names.
+ */
+
+static const char * const *
+apcmastersnmp_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_PORT, ST_COMMUNITY, NULL};
+ return ret;
+}
+
+/*
+ * Set the configuration parameters.
+ */
+
+static int
+apcmastersnmp_set_config(StonithPlugin * s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ int * i;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_PORT, NULL}
+ , {ST_COMMUNITY, NULL}
+ , {NULL, NULL}
+ };
+
+ DEBUGCALL;
+ ERRIFWRONGDEV(s,S_INVAL);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->hostname = namestocopy[0].s_value;
+ sd->port = atoi(namestocopy[1].s_value);
+ PluginImports->mfree(namestocopy[1].s_value);
+ sd->community = namestocopy[2].s_value;
+
+ /* try to resolve the hostname/ip-address */
+ if (gethostbyname(sd->hostname) != NULL) {
+ /* init snmp library */
+ init_snmp("apcmastersnmp");
+
+ /* now try to get a snmp session */
+ if ((sd->sptr = APC_open(sd->hostname, sd->port, sd->community)) != NULL) {
+
+ /* ok, get the number of outlets from the masterswitch */
+ if ((i = APC_read(sd->sptr, OID_NUM_OUTLETS, ASN_INTEGER))
+ == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read number of outlets."
+ , __FUNCTION__);
+ return (S_ACCESS);
+ }
+ /* store the number of outlets */
+ sd->num_outlets = *i;
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: number of outlets: %i."
+ , __FUNCTION__, sd->num_outlets );
+ }
+
+ /* Everything went well */
+ return (S_OK);
+ }else{
+ LOG(PIL_CRIT, "%s: cannot create snmp session."
+ , __FUNCTION__);
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: cannot resolve hostname '%s', h_errno %d."
+ , __FUNCTION__, sd->hostname, h_errno);
+ }
+
+ /* not a valid config */
+ return (S_BADCONFIG);
+}
+
+/*
+ * get info about the stonith device
+ */
+
+static const char *
+apcmastersnmp_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice *ad;
+ const char *ret = NULL;
+
+ DEBUGCALL;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ ad = (struct pluginDevice *) s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ad->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = ad->hostname;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "APC MasterSwitch (via SNMP)\n"
+ "The APC MasterSwitch can accept multiple simultaneous SNMP clients";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.apc.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = apcmastersnmpXML;
+ break;
+
+ }
+ return ret;
+}
+
+
+/*
+ * APC StonithPlugin destructor...
+ */
+
+static void
+apcmastersnmp_destroy(StonithPlugin * s)
+{
+ struct pluginDevice *ad;
+
+ DEBUGCALL;
+
+ VOIDERRIFWRONGDEV(s);
+
+ ad = (struct pluginDevice *) s;
+
+ ad->pluginid = NOTpluginID;
+
+ /* release snmp session */
+ if (ad->sptr != NULL) {
+ snmp_close(ad->sptr);
+ ad->sptr = NULL;
+ }
+
+ /* reset defaults */
+ if (ad->hostname != NULL) {
+ PluginImports->mfree(ad->hostname);
+ ad->hostname = NULL;
+ }
+ if (ad->community != NULL) {
+ PluginImports->mfree(ad->community);
+ ad->community = NULL;
+ }
+ ad->num_outlets = 0;
+
+ PluginImports->mfree(ad);
+}
+
+/*
+ * Create a new APC StonithPlugin device. Too bad this function can't be
+ * static
+ */
+
+static StonithPlugin *
+apcmastersnmp_new(const char *subplugin)
+{
+ struct pluginDevice *ad = ST_MALLOCT(struct pluginDevice);
+
+ DEBUGCALL;
+
+ /* no memory for stonith-object */
+ if (ad == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+
+ /* clear stonith-object */
+ memset(ad, 0, sizeof(*ad));
+
+ /* set defaults */
+ ad->pluginid = pluginid;
+ ad->sptr = NULL;
+ ad->hostname = NULL;
+ ad->community = NULL;
+ ad->idinfo = DEVICE;
+ ad->sp.s_ops = &apcmastersnmpOps;
+
+ /* return the object */
+ return (&(ad->sp));
+}
diff --git a/lib/plugins/stonith/apcmastersnmp.cfg.example b/lib/plugins/stonith/apcmastersnmp.cfg.example
new file mode 100644
index 0000000..76fea08
--- /dev/null
+++ b/lib/plugins/stonith/apcmastersnmp.cfg.example
@@ -0,0 +1,39 @@
+#
+# this is an example config for the stonith module apcmastersnmp
+#
+# 1. what does the fields on the line mean ?
+#
+# all parameters must be given on a single line. blank lines and lines
+# starting with '#' are ignored. only the first not ignored line will
+# be processed. all subsequent lines will be ignored. the different
+# fields must be seperated by white-spaces (blanks and/or tabs).
+#
+# the first field is the either the hostname or the ip address. the
+# hostname must be resolvable. the second fields specifies the snmp port
+# the masterswitch is listening. for snmp the default is 161. the last
+# field contains the so called 'community' string. this must be the same
+# as the one in the masterswitch configuration.
+#
+#
+# 2. how must the masterswitch be configured ?
+#
+# as said above, the community string must be set to the same value entered
+# in this config. the different outlets must be named after the connected
+# hosts. that means, the outlet names must be the same as the node names
+# in /etc/ha.d/ha.cf. the reset values should be set to reasonable values.
+#
+# the module DON'T configure the module in any way!
+#
+#
+# 3. how does the module work ?
+#
+# in case of a stonith the module receives the nodename of the host, which
+# should be reset. the module looks up this nodename in the list of outlet
+# names. that's why the names must be identical (see 2.). if it finds the
+# name, it'll reset the appropriate outlet using the configured values
+# (eg. delay, duration). then the module waits for the outlet to coming
+# up. if it comes up, a successful stonith will be reported back. otherwise
+# the stonith failed and a failure code will be returned.
+#
+
+192.168.1.110 161 private
diff --git a/lib/plugins/stonith/apcsmart.c b/lib/plugins/stonith/apcsmart.c
new file mode 100644
index 0000000..18d1612
--- /dev/null
+++ b/lib/plugins/stonith/apcsmart.c
@@ -0,0 +1,1028 @@
+/*
+ * Stonith module for APCSmart Stonith device
+ * Copyright (c) 2000 Andreas Piesk <a.piesk@gmx.net>
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.*
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Original version of this UPS code was taken from:
+ * 'Network UPS Tools' by Russell Kroll <rkroll@exploits.org>
+ * homepage: http://www.networkupstools.org/
+ *
+ * Significantly mangled by Alan Robertson <alanr@unix.sh>
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "APCSmart"
+
+#include "stonith_plugin_common.h"
+
+/*
+ * APCSmart (tested with old 900XLI, APC SmartUPS 700 and SmartUPS-1000)
+ *
+ * The reset is a combined reset: "S" and "@000"
+ * The "S" command tells the ups that if it is on-battery, it should
+ * remain offline until the power is back.
+ * If that command is not accepted, the "@000" command will be sent
+ * to tell the ups to turn off and back on right away.
+ * In both cases, if the UPS supports a 20 second shutdown grace
+ * period (such as on the 900XLI), the shutdown will delay that long,
+ * otherwise the shutdown will happen immediately (the code searches
+ * for the smallest possible delay).
+ */
+
+#define CFG_FILE "/etc/ha.d/apcsmart.cfg"
+
+#define MAX_DEVICES 1
+
+#define SERIAL_TIMEOUT 3 /* timeout in sec */
+#define SEND_DELAY 50000 /* in microseconds */
+#define ENDCHAR 10 /* use LF */
+#define MAX_STRING 512
+#define MAX_DELAY_STRING 16
+#define SWITCH_TO_NEXT_VAL "-" /* APC cmd for cycling through
+ * the values
+ */
+
+#define CMD_SMART_MODE "Y"
+#define RSP_SMART_MODE "SM"
+#define CMD_GET_STATUS "Q"
+#define RSP_GET_STATUS NULL
+#define CMD_RESET "S" /* turn off & stay off if on battery */
+#define CMD_RESET2 "@000" /* turn off & immediately turn on */
+#define RSP_RESET "*" /* RESET response from older models */
+#define RSP_RESET2 "OK" /* RESET response from newer models */
+#define RSP_NA "NA"
+#define CMD_READREG1 "~"
+#define CMD_OFF "Z"
+#define CMD_ON "\016" /* (control-n) */
+#define CMD_SHUTDOWN_DELAY "p"
+#define CMD_WAKEUP_DELAY "r"
+
+#define CR 13
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid; /* of object */
+ const char * idinfo; /* type of device */
+ char ** hostlist; /* served by the device (only 1) */
+ int hostcount;/* of hosts (1) */
+ char * upsdev; /* */
+ int upsfd; /* for serial port */
+ int retries;
+ char shutdown_delay[MAX_DELAY_STRING];
+ char old_shutdown_delay[MAX_DELAY_STRING];
+ char wakeup_delay[MAX_DELAY_STRING];
+ char old_wakeup_delay[MAX_DELAY_STRING];
+};
+
+/* saving old settings */
+/* FIXME! These should be part of pluginDevice struct above */
+static struct termios old_tio;
+
+static int f_serialtimeout; /* flag for timeout */
+static const char *pluginid = "APCSmart-Stonith";
+static const char *NOTpluginID = "APCSmart device has been destroyed";
+
+/*
+ * stonith prototypes
+ */
+
+#define PIL_PLUGIN apcsmart
+#define PIL_PLUGIN_S "apcsmart"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * apcsmart_new(const char *);
+static void apcsmart_destroy(StonithPlugin *);
+static const char * const * apcsmart_get_confignames(StonithPlugin*);
+static int apcsmart_set_config(StonithPlugin *, StonithNVpair*);
+static const char * apcsmart_get_info(StonithPlugin * s, int InfoType);
+static int apcsmart_status(StonithPlugin * );
+static int apcsmart_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** apcsmart_hostlist(StonithPlugin *);
+
+static struct stonith_ops apcsmartOps ={
+ apcsmart_new, /* Create new STONITH object */
+ apcsmart_destroy, /* Destroy STONITH object */
+ apcsmart_get_info, /* Return STONITH info string */
+ apcsmart_get_confignames, /* Return STONITH info string */
+ apcsmart_set_config, /* Get configuration from NVpairs */
+ apcsmart_status, /* Return STONITH device status */
+ apcsmart_reset_req, /* Request a reset */
+ apcsmart_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &apcsmartOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+#include "stonith_config_xml.h"
+
+static const char *apcsmartXML =
+ XML_PARAMETERS_BEGIN
+ XML_TTYDEV_PARM
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * own prototypes
+ */
+
+int APC_open_serialport(const char *port, speed_t speed);
+void APC_close_serialport(const char *port, int upsfd);
+void APC_sh_serial_timeout(int sig);
+int APC_send_cmd(int upsfd, const char *cmd);
+int APC_recv_rsp(int upsfd, char *rsp);
+int APC_enter_smartmode(int upsfd);
+int APC_set_ups_var(int upsfd, const char *cmd, char *newval);
+int APC_get_smallest_delay(int upsfd, const char *cmd, char *smdelay);
+int APC_init( struct pluginDevice *ad );
+void APC_deinit( struct pluginDevice *ad );
+
+/*
+ * Signal handler for serial port timeouts
+ */
+
+void
+APC_sh_serial_timeout(int sig)
+{
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ STONITH_IGNORE_SIG(SIGALRM);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: serial port timed out.", __FUNCTION__);
+ }
+
+ f_serialtimeout = TRUE;
+
+ return;
+}
+
+/*
+ * Open serial port and set it to b2400
+ */
+
+int
+APC_open_serialport(const char *port, speed_t speed)
+{
+ struct termios tio;
+ int fd;
+ int rc;
+ int errno_save;
+ int fflags;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if ((rc = OurImports->TtyLock(port)) < 0) {
+ LOG(PIL_CRIT, "%s: Could not lock tty %s [rc=%d]."
+ , __FUNCTION__, port, rc);
+ return -1;
+ }
+
+ STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout);
+ alarm(SERIAL_TIMEOUT);
+ f_serialtimeout = FALSE;
+
+ fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK | O_EXCL);
+ errno_save = errno;
+
+ alarm(0);
+ STONITH_IGNORE_SIG(SIGALRM);
+
+ if (fd < 0) {
+ LOG(PIL_CRIT, "%s: Open of %s %s [%s].", __FUNCTION__
+ , port
+ , f_serialtimeout ? "timed out" : "failed"
+ , strerror(errno_save));
+ OurImports->TtyUnlock(port);
+ return -1;
+ }
+
+ if ((fflags = fcntl(fd, F_GETFL)) < 0
+ || fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
+ LOG(PIL_CRIT, "%s: Setting flags on %s failed [%s]."
+ , __FUNCTION__
+ , port
+ , strerror(errno_save));
+ close(fd);
+ OurImports->TtyUnlock(port);
+ return -1;
+ }
+
+ if (tcgetattr(fd, &old_tio) < 0) {
+ LOG(PIL_CRIT, "%s: tcgetattr of %s failed [%s].", __FUNCTION__
+ , port
+ , strerror(errno));
+ close(fd);
+ OurImports->TtyUnlock(port);
+ return -1;
+ }
+
+ memcpy(&tio, &old_tio, sizeof(struct termios));
+ tio.c_cflag = CS8 | CLOCAL | CREAD;
+ tio.c_iflag = IGNPAR;
+ tio.c_oflag = 0;
+ tio.c_lflag = 0;
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
+
+ cfsetispeed(&tio, speed);
+ cfsetospeed(&tio, speed);
+
+ tcflush(fd, TCIOFLUSH);
+ tcsetattr(fd, TCSANOW, &tio);
+
+ return (fd);
+}
+
+/*
+ * Close serial port and restore old settings
+ */
+
+void
+APC_close_serialport(const char *port, int upsfd)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ if (upsfd < 0) {
+ return;
+ }
+
+ tcflush(upsfd, TCIFLUSH);
+ tcsetattr(upsfd, TCSANOW, &old_tio);
+ close(upsfd);
+ if (port != NULL) {
+ OurImports->TtyUnlock(port);
+ }
+}
+
+/*
+ * Send a command to the ups
+ */
+
+int
+APC_send_cmd(int upsfd, const char *cmd)
+{
+ int i;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s(\"%s\")", __FUNCTION__, cmd);
+ }
+
+ tcflush(upsfd, TCIFLUSH);
+ for (i = strlen(cmd); i > 0; i--) {
+ if (write(upsfd, cmd++, 1) != 1) {
+ return (S_ACCESS);
+ }
+
+ usleep(SEND_DELAY);
+ }
+ return (S_OK);
+}
+
+/*
+ * Get the response from the ups
+ */
+
+int
+APC_recv_rsp(int upsfd, char *rsp)
+{
+ char *p = rsp;
+ char inp;
+ int num = 0;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ *p = '\0';
+
+ STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout);
+
+ alarm(SERIAL_TIMEOUT);
+
+ while (num < MAX_STRING) {
+
+ if (read(upsfd, &inp, 1) == 1) {
+
+ /* shutdown sends only a '*' without LF */
+ if ((inp == '*') && (num == 0)) {
+ *p++ = inp;
+ num++;
+ inp = ENDCHAR;
+ }
+
+ if (inp == ENDCHAR) {
+ alarm(0);
+ STONITH_IGNORE_SIG(SIGALRM);
+
+ *p = '\0';
+ if (Debug) {
+ LOG(PIL_DEBUG, "return(\"%s\")/*%s*/;"
+ , rsp, __FUNCTION__);
+ }
+ return (S_OK);
+ }
+
+ if (inp != CR) {
+ *p++ = inp;
+ num++;
+ }
+ }else{
+ alarm(0);
+ STONITH_IGNORE_SIG(SIGALRM);
+ *p = '\0';
+ LOG(PIL_DEBUG, "%s: %s.", __FUNCTION__,
+ f_serialtimeout ? "timeout" :
+ "can't access device" );
+ return (f_serialtimeout ? S_TIMEOUT : S_ACCESS);
+ }
+ }
+ return (S_ACCESS);
+}
+
+/*
+ * Enter smart mode
+ */
+
+int
+APC_enter_smartmode(int upsfd)
+{
+ int rc;
+ char resp[MAX_STRING];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ strcpy(resp, RSP_SMART_MODE);
+
+ if (((rc = APC_send_cmd(upsfd, CMD_SMART_MODE)) == S_OK)
+ && ((rc = APC_recv_rsp(upsfd, resp)) == S_OK)
+ && (strcmp(RSP_SMART_MODE, resp) == 0)) {
+ return (S_OK);
+ }
+
+ return (S_ACCESS);
+}
+
+/*
+ * Set a value in the hardware using the <cmdchar> '-' (repeat) approach
+ */
+
+int
+APC_set_ups_var(int upsfd, const char *cmd, char *newval)
+{
+ char resp[MAX_STRING];
+ char orig[MAX_STRING];
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
+ || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, orig)) != S_OK)) {
+ return (rc);
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: var '%s' original val %s"
+ , __FUNCTION__, cmd, orig);
+ }
+
+ if (strcmp(orig, newval) == 0) {
+ return (S_OK); /* already set */
+ }
+
+ *resp = '\0';
+
+ while (strcmp(resp, orig) != 0) {
+ if (((rc = APC_send_cmd(upsfd, SWITCH_TO_NEXT_VAL)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
+ return (rc);
+ }
+
+ if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
+ || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
+ return (rc);
+ }
+
+ if (strcmp(resp, newval) == 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: var '%s' set to %s"
+ , __FUNCTION__, cmd, newval);
+ }
+
+ strcpy(newval, orig); /* return the old value */
+ return (S_OK); /* got it */
+ }
+ }
+
+ LOG(PIL_CRIT, "%s(): Could not set variable '%s' to %s!"
+ , __FUNCTION__, cmd, newval);
+ LOG(PIL_CRIT, "%s(): This UPS may not support STONITH :-("
+ , __FUNCTION__);
+
+ return (S_OOPS);
+}
+
+/*
+ * Query the smallest delay supported by the hardware using the
+ * <cmdchar> '-' (repeat) approach and looping through all possible values,
+ * saving the smallest
+ */
+
+int
+APC_get_smallest_delay(int upsfd, const char *cmd, char *smdelay)
+{
+ char resp[MAX_DELAY_STRING];
+ char orig[MAX_DELAY_STRING];
+ int delay, smallest;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
+ || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, orig)) != S_OK)) {
+ return (rc);
+ }
+
+ smallest = atoi(orig);
+ strcpy(smdelay, orig);
+
+ *resp = '\0';
+
+ /* search for smallest delay; need to loop through all possible
+ * values so that we leave delay the way we found it */
+ while (strcmp(resp, orig) != 0) {
+ if (((rc = APC_send_cmd(upsfd, SWITCH_TO_NEXT_VAL)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
+ return (rc);
+ }
+
+ if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
+ || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
+ return (rc);
+ }
+
+ if ((delay = atoi(resp)) < smallest) {
+ smallest = delay;
+ strcpy(smdelay, resp);
+ }
+ }
+
+ return (S_OK);
+}
+
+/*
+ * Initialize the ups
+ */
+
+int
+APC_init(struct pluginDevice *ad)
+{
+ int upsfd;
+ char value[MAX_DELAY_STRING];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ /* if ad->upsfd != -1 device has already been configured. */
+ /* Just enter smart mode again because otherwise a SmartUPS-1000 */
+ /* has been observed to sometimes not respond. */
+ if(ad->upsfd >= 0) {
+ if(APC_enter_smartmode(ad->upsfd) != S_OK) {
+ return(S_OOPS);
+ }
+ return S_OK;
+ }
+
+ /* open serial port and store the fd in ad->upsfd */
+ if ((upsfd = APC_open_serialport(ad->upsdev, B2400)) == -1) {
+ return S_OOPS;
+ }
+
+ /* switch into smart mode */
+ if (APC_enter_smartmode(upsfd) != S_OK) {
+ APC_close_serialport(ad->upsdev, upsfd);
+ ad->upsfd = -1;
+ return S_OOPS;
+ }
+
+ /* get the smallest possible delays for this particular hardware */
+ if (APC_get_smallest_delay(upsfd, CMD_SHUTDOWN_DELAY
+ , ad->shutdown_delay) != S_OK
+ || APC_get_smallest_delay(upsfd, CMD_WAKEUP_DELAY
+ , ad->wakeup_delay) != S_OK) {
+ LOG(PIL_CRIT, "%s: couldn't retrieve smallest delay from UPS"
+ , __FUNCTION__);
+ APC_close_serialport(ad->upsdev, upsfd);
+ ad->upsfd = -1;
+ return S_OOPS;
+ }
+
+ /* get the old settings and store them */
+ strcpy(value, ad->shutdown_delay);
+ if (APC_set_ups_var(upsfd, CMD_SHUTDOWN_DELAY, value) != S_OK) {
+ LOG(PIL_CRIT, "%s: couldn't set shutdown delay to %s"
+ , __FUNCTION__, ad->shutdown_delay);
+ APC_close_serialport(ad->upsdev, upsfd);
+ ad->upsfd = -1;
+ return S_OOPS;
+ }
+ strcpy(ad->old_shutdown_delay, value);
+ strcpy(value, ad->wakeup_delay);
+ if (APC_set_ups_var(upsfd, CMD_WAKEUP_DELAY, value) != S_OK) {
+ LOG(PIL_CRIT, "%s: couldn't set wakeup delay to %s"
+ , __FUNCTION__, ad->wakeup_delay);
+ APC_close_serialport(ad->upsdev, upsfd);
+ ad->upsfd = -1;
+ return S_OOPS;
+ }
+ strcpy(ad->old_wakeup_delay, value);
+
+ ad->upsfd = upsfd;
+ return S_OK;
+}
+
+/*
+ * Restore original settings and close the port
+ */
+
+void
+APC_deinit(struct pluginDevice *ad)
+{
+ APC_enter_smartmode( ad->upsfd );
+
+ APC_set_ups_var(ad->upsfd, CMD_SHUTDOWN_DELAY, ad->old_shutdown_delay);
+ APC_set_ups_var(ad->upsfd, CMD_WAKEUP_DELAY, ad->old_wakeup_delay);
+
+ /* close serial port */
+ if (ad->upsfd >= 0) {
+ APC_close_serialport(ad->upsdev, ad->upsfd);
+ ad->upsfd = -1;
+ }
+}
+static const char * const *
+apcsmart_get_confignames(StonithPlugin* sp)
+{
+ static const char * names[] = {ST_TTYDEV, ST_HOSTLIST, NULL};
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ return names;
+}
+
+/*
+ * Stash away the config info we've been given...
+ */
+
+static int
+apcsmart_set_config(StonithPlugin * s, StonithNVpair* list)
+{
+ struct pluginDevice * ad = (struct pluginDevice*)s;
+ StonithNamesToGet namestocopy [] =
+ { {ST_TTYDEV, NULL}
+ , {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ ad->upsdev = namestocopy[0].s_value;
+ ad->hostlist = OurImports->StringToHostList(namestocopy[1].s_value);
+ FREE(namestocopy[1].s_value);
+
+ if (ad->hostlist == NULL) {
+ LOG(PIL_CRIT,"StringToHostList() failed");
+ return S_OOPS;
+ }
+ for (ad->hostcount = 0; ad->hostlist[ad->hostcount]
+ ; ad->hostcount++) {
+ strdown(ad->hostlist[ad->hostcount]);
+ }
+ if (access(ad->upsdev, R_OK|W_OK|F_OK) < 0) {
+ LOG(PIL_CRIT,"Cannot access tty [%s]", ad->upsdev);
+ return S_BADCONFIG;
+ }
+
+ return ad->hostcount ? S_OK : S_BADCONFIG;
+}
+
+/*
+ * return the status for this device
+ */
+
+static int
+apcsmart_status(StonithPlugin * s)
+{
+ struct pluginDevice *ad = (struct pluginDevice *) s;
+ char resp[MAX_STRING];
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+
+ /* get status */
+ if (((rc = APC_init( ad )) == S_OK)
+ && ((rc = APC_send_cmd(ad->upsfd, CMD_GET_STATUS)) == S_OK)
+ && ((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK)) {
+ return (S_OK); /* everything ok. */
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: failed, rc=%d.", __FUNCTION__, rc);
+ }
+ return (rc);
+}
+
+
+/*
+ * return the list of hosts configured for this device
+ */
+
+static char **
+apcsmart_hostlist(StonithPlugin * s)
+{
+ struct pluginDevice *ad = (struct pluginDevice *) s;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ ERRIFNOTCONFIGED(s,NULL);
+
+ return OurImports->CopyHostList((const char **)(void*)ad->hostlist);
+}
+
+static gboolean
+apcsmart_RegisterBitsSet(struct pluginDevice * ad, int nreg, unsigned bits
+, gboolean* waserr)
+{
+ const char* reqregs[4] = {"?", "~", "'", "8"};
+ unsigned regval;
+ char resp[MAX_STRING];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+
+ if (APC_enter_smartmode(ad->upsfd) != S_OK
+ || APC_send_cmd(ad->upsfd, reqregs[nreg]) != S_OK
+ || APC_recv_rsp(ad->upsfd, resp) != S_OK
+ || (sscanf(resp, "%02x", &regval) != 1)) {
+ if (waserr){
+ *waserr = TRUE;
+ }
+ return FALSE;
+ }
+ if (waserr){
+ *waserr = FALSE;
+ }
+ return ((regval & bits) == bits);
+}
+
+#define apcsmart_IsPoweredOff(ad, err) apcsmart_RegisterBitsSet(ad,1,0x40,err)
+#define apcsmart_ResetHappening(ad,err) apcsmart_RegisterBitsSet(ad,3,0x08,err)
+
+
+static int
+apcsmart_ReqOnOff(struct pluginDevice * ad, int request)
+{
+ const char * cmdstr;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ cmdstr = (request == ST_POWEROFF ? CMD_OFF : CMD_ON);
+ /* enter smartmode, send on/off command */
+ if ((rc =APC_enter_smartmode(ad->upsfd)) != S_OK
+ || (rc = APC_send_cmd(ad->upsfd, cmdstr)) != S_OK) {
+ return rc;
+ }
+ sleep(2);
+ if ((rc = APC_send_cmd(ad->upsfd, cmdstr)) == S_OK) {
+ gboolean ison;
+ gboolean waserr;
+ sleep(1);
+ ison = !apcsmart_IsPoweredOff(ad, &waserr);
+ if (waserr) {
+ return S_RESETFAIL;
+ }
+ if (request == ST_POWEROFF) {
+ return ison ? S_RESETFAIL : S_OK;
+ }else{
+ return ison ? S_OK : S_RESETFAIL;
+ }
+ }
+ return rc;
+}
+
+/*
+ * reset the host
+ */
+
+static int
+apcsmart_ReqGenericReset(struct pluginDevice *ad)
+{
+ char resp[MAX_STRING];
+ int rc = S_RESETFAIL;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ /* send reset command(s) */
+ if (((rc = APC_init(ad)) == S_OK)
+ && ((rc = APC_send_cmd(ad->upsfd, CMD_RESET)) == S_OK)) {
+ if (((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK)
+ && (strcmp(resp, RSP_RESET) == 0
+ || strcmp(resp, RSP_RESET2) == 0)) {
+ /* first kind of reset command was accepted */
+ } else if (((rc = APC_send_cmd(ad->upsfd, CMD_RESET2)) == S_OK)
+ && ((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK)
+ && (strcmp(resp, RSP_RESET) == 0
+ || strcmp(resp, RSP_RESET2) == 0)) {
+ /* second kind of command was accepted */
+ } else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "APC: neither reset command "
+ "was accepted");
+ }
+ rc = S_RESETFAIL;
+ }
+ }
+ if (rc == S_OK) {
+ /* we wait grace period + up to 10 seconds after shutdown */
+ int maxdelay = atoi(ad->shutdown_delay)+10;
+ int j;
+
+ for (j=0; j < maxdelay; ++j) {
+ gboolean err;
+ if (apcsmart_ResetHappening(ad, &err)) {
+ return err ? S_RESETFAIL : S_OK;
+ }
+ sleep(1);
+ }
+ LOG(PIL_CRIT, "%s: timed out waiting for reset to end."
+ , __FUNCTION__);
+ return S_RESETFAIL;
+
+ }else{
+ if (strcmp(resp, RSP_NA) == 0){
+ gboolean iserr;
+ /* This means it's currently powered off */
+ /* or busy on a previous command... */
+ if (apcsmart_IsPoweredOff(ad, &iserr)) {
+ if (iserr) {
+ LOG(PIL_DEBUG, "%s: power off "
+ "detection failed.", __FUNCTION__);
+ return S_RESETFAIL;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "APC: was powered off, "
+ "powering back on.");
+ }
+ return apcsmart_ReqOnOff(ad, ST_POWERON);
+ }
+ }
+ }
+ strcpy(resp, "?");
+
+ /* reset failed */
+
+ return S_RESETFAIL;
+}
+
+static int
+apcsmart_reset_req(StonithPlugin * s, int request, const char *host)
+{
+ char ** hl;
+ int b_found=FALSE;
+ struct pluginDevice * ad = (struct pluginDevice *) s;
+ int rc;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ if (host == NULL) {
+ LOG(PIL_CRIT, "%s: invalid hostname argument.", __FUNCTION__);
+ return (S_INVAL);
+ }
+
+ /* look through the hostlist */
+ hl = ad->hostlist;
+
+ while (*hl && !b_found ) {
+ if( strcasecmp( *hl, host ) == 0 ) {
+ b_found = TRUE;
+ break;
+ }else{
+ ++hl;
+ }
+ }
+
+ /* host not found in hostlist */
+ if( !b_found ) {
+ LOG(PIL_CRIT, "%s: host '%s' not in hostlist."
+ , __FUNCTION__, host);
+ return S_BADHOST;
+ }
+ if ((rc = APC_init(ad)) != S_OK) {
+ return rc;
+ }
+
+ if (request == ST_POWERON || request == ST_POWEROFF) {
+ return apcsmart_ReqOnOff(ad, request);
+ }
+ return apcsmart_ReqGenericReset(ad);
+}
+
+
+/*
+ * get info about the stonith device
+ */
+
+static const char *
+apcsmart_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice *ad = (struct pluginDevice *) s;
+ const char *ret;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ad->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = ad->upsdev;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "APC Smart UPS\n"
+ " (via serial port - NOT USB!). \n"
+ " Works with higher-end APC UPSes, like\n"
+ " Back-UPS Pro, Smart-UPS, Matrix-UPS, etc.\n"
+ " (Smart-UPS may have to be >= Smart-UPS 700?).\n"
+ " See http://www.networkupstools.org/protocols/apcsmart.html\n"
+ " for protocol compatibility details.";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.apc.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = apcsmartXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return (ret);
+}
+
+/*
+ * APC Stonith destructor...
+ */
+
+static void
+apcsmart_destroy(StonithPlugin * s)
+{
+ struct pluginDevice *ad = (struct pluginDevice *) s;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ VOIDERRIFWRONGDEV(s);
+
+ if (ad->upsfd >= 0 && ad->upsdev) {
+ APC_deinit( ad );
+ }
+
+ ad->pluginid = NOTpluginID;
+
+ if (ad->hostlist) {
+ stonith_free_hostlist(ad->hostlist);
+ ad->hostlist = NULL;
+ }
+ if (ad->upsdev != NULL) {
+ FREE(ad->upsdev);
+ ad->upsdev = NULL;
+ }
+
+ ad->hostcount = -1;
+ ad->upsfd = -1;
+
+ FREE(ad);
+
+}
+
+/*
+ * Create a new APC Stonith device. Too bad this function can't be
+ * static
+ */
+
+static StonithPlugin *
+apcsmart_new(const char *subplugin)
+{
+ struct pluginDevice *ad = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ if (ad == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+
+ memset(ad, 0, sizeof(*ad));
+
+ ad->pluginid = pluginid;
+ ad->hostlist = NULL;
+ ad->hostcount = -1;
+ ad->upsfd = -1;
+ ad->idinfo = DEVICE;
+ ad->sp.s_ops = &apcsmartOps;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: returning successfully.", __FUNCTION__);
+ }
+ return &(ad->sp);
+}
diff --git a/lib/plugins/stonith/apcsmart.cfg.example b/lib/plugins/stonith/apcsmart.cfg.example
new file mode 100644
index 0000000..278f925
--- /dev/null
+++ b/lib/plugins/stonith/apcsmart.cfg.example
@@ -0,0 +1 @@
+/dev/ups hostname
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;
+}
diff --git a/lib/plugins/stonith/bladehpi.c b/lib/plugins/stonith/bladehpi.c
new file mode 100644
index 0000000..ae9a4cf
--- /dev/null
+++ b/lib/plugins/stonith/bladehpi.c
@@ -0,0 +1,1101 @@
+/*
+ * Stonith module for BladeCenter via OpenHPI, an implementation of Service
+ * Availability Forum's Hardware Platfrom Interface
+ *
+ * Author: Dave Blaschke <debltc@us.ibm.com>
+ *
+ * Copyright (c) 2005 International Business Machines
+ *
+ * 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 "IBM BladeCenter (OpenHPI)"
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN bladehpi
+#define PIL_PLUGIN_S "bladehpi"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include <openhpi/SaHpi.h>
+
+/* Maximum number of seconds to wait for host to power off */
+#define MAX_POWEROFF_WAIT 60
+
+/* entity_root, the one required plugin parameter */
+#define ST_ENTITYROOT "entity_root"
+
+/* String format of entity_root */
+#define SYSTEM_CHASSIS_FMT "{SYSTEM_CHASSIS,%d}"
+
+/* soft_reset, the one optional plugin parameter */
+#define ST_SOFTRESET "soft_reset"
+
+#define OPENHPIURL "http://www.openhpi.org/"
+
+/* OpenHPI resource types of interest to this plugin */
+#define OHRES_NONE 0
+#define OHRES_BLADECENT 1
+#define OHRES_MGMTMOD 2
+#define OHRES_BLADE 3
+
+/* IBMBC_WAIT_FOR_OFF - This constant has to do with the problem that
+ saHpiResourcePowerStateSet can return before the desired state has been
+ achieved by the blade. In the SAHPI_POWER_OFF case this is not good,
+ as whoever calls this plugin assumes that the power is actually off
+ when the plugin returns with a successful return code. Define this
+ constant to build code that loops in one second intervals after calling
+ saHpiResourcePowerStateSet(SAHPI_POWER_OFF) to make sure the power is
+ really off.
+#define IBMBC_WAIT_FOR_OFF */
+
+static StonithPlugin * bladehpi_new(const char *);
+static void bladehpi_destroy(StonithPlugin *);
+static const char * bladehpi_getinfo(StonithPlugin *, int);
+static const char * const * bladehpi_get_confignames(StonithPlugin *);
+static int bladehpi_status(StonithPlugin *);
+static int bladehpi_reset_req(StonithPlugin *, int, const char *);
+static char ** bladehpi_hostlist(StonithPlugin *);
+static int bladehpi_set_config(StonithPlugin *, StonithNVpair *);
+
+static struct stonith_ops bladehpiOps = {
+ bladehpi_new, /* Create new STONITH object */
+ bladehpi_destroy, /* Destroy STONITH object */
+ bladehpi_getinfo, /* Return STONITH info string */
+ bladehpi_get_confignames, /* Return configuration parameters */
+ bladehpi_set_config, /* Set configuration */
+ bladehpi_status, /* Return STONITH device status */
+ bladehpi_reset_req, /* Request a reset */
+ bladehpi_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports * PluginImports;
+static PILPlugin * OurPlugin;
+static PILInterface * OurInterface;
+static StonithImports * OurImports;
+static void * interfprivate;
+
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin *us, const PILPluginImports *imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin *us, const PILPluginImports *imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us
+ , PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &bladehpiOps
+ , NULL /* close */
+ , &OurInterface
+ , (void *)&OurImports
+ , &interfprivate);
+}
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ char * idinfo;
+ char * device;
+ int softreset;
+ GList * hostlist;
+ SaHpiVersionT ohver; /* OpenHPI interface version */
+ SaHpiSessionIdT ohsession; /* session ID */
+ SaHpiUint32T ohrptcnt; /* RPT count for hostlist */
+ SaHpiResourceIdT ohdevid; /* device resource ID */
+ SaHpiResourceIdT ohsensid; /* sensor resource ID */
+ SaHpiSensorNumT ohsensnum; /* sensor number */
+};
+
+static int open_hpi_session(struct pluginDevice *dev);
+static void close_hpi_session(struct pluginDevice *dev);
+
+static const char *pluginid = "BladeCenterDevice-Stonith";
+static const char *NOTpluginID = "IBM BladeCenter device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_ENTITYROOT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_ENTITYROOT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_ENTITYROOT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The entity_root of the STONITH device from the OpenHPI config file" \
+ XML_PARM_LONGDESC_END
+
+#define XML_ENTITYROOT_PARM \
+ XML_PARAMETER_BEGIN(ST_ENTITYROOT, "string", "1", "0") \
+ XML_ENTITYROOT_SHORTDESC \
+ XML_ENTITYROOT_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_SOFTRESET_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_SOFTRESET \
+ XML_PARM_SHORTDESC_END
+
+#define XML_SOFTRESET_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "Soft reset indicator, true|1 if STONITH device should use soft reset (power cycle) to reset nodes, false|0 if device should use hard reset (power off, wait, power on); default is false" \
+ XML_PARM_LONGDESC_END
+
+#define XML_SOFTRESET_PARM \
+ XML_PARAMETER_BEGIN(ST_SOFTRESET, "string", "0", "0") \
+ XML_SOFTRESET_SHORTDESC \
+ XML_SOFTRESET_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *bladehpiXML =
+ XML_PARAMETERS_BEGIN
+ XML_ENTITYROOT_PARM
+ XML_SOFTRESET_PARM
+ XML_PARAMETERS_END;
+
+static int get_resource_type(char *, SaHpiRptEntryT *);
+static int get_sensor_num(SaHpiSessionIdT, SaHpiResourceIdT);
+static int get_bladehpi_hostlist(struct pluginDevice *);
+static void free_bladehpi_hostlist(struct pluginDevice *);
+static int get_num_tokens(char *str);
+
+struct blade_info {
+ char * name; /* blade name */
+ SaHpiResourceIdT resourceId; /* blade resource ID */
+ SaHpiCapabilitiesT resourceCaps; /* blade capabilities */
+};
+
+
+static int
+bladehpi_status(StonithPlugin *s)
+{
+ struct pluginDevice * dev;
+ SaErrorT ohrc;
+ SaHpiDomainInfoT ohdi;
+ int rc = S_OK;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ dev = (struct pluginDevice *)s;
+ rc = open_hpi_session(dev);
+ if( rc != S_OK )
+ return rc;
+
+ /* Refresh the hostlist only if RPTs updated */
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ rc = S_BADCONFIG;
+ goto done;
+ }
+ if (dev->ohrptcnt != ohdi.RptUpdateCount) {
+ free_bladehpi_hostlist(dev);
+ if (get_bladehpi_hostlist(dev) != S_OK) {
+ LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
+ , __FUNCTION__);
+ rc = S_BADCONFIG;
+ goto done;
+ }
+ }
+
+ /* At this point, hostlist is up to date */
+ if (dev->ohsensid && dev->ohsensnum) {
+ /*
+ * For accurate status, need to make a call that goes out to
+ * BladeCenter MM because the calls made so far by this
+ * function (and perhaps get_bladehpi_hostlist) only retrieve
+ * information from memory cached by OpenHPI
+ */
+ ohrc = saHpiSensorReadingGet(dev->ohsession
+ , dev->ohsensid, dev->ohsensnum, NULL, NULL);
+ if (ohrc == SA_ERR_HPI_BUSY || ohrc == SA_ERR_HPI_NO_RESPONSE) {
+ LOG(PIL_CRIT, "Unable to connect to BladeCenter in %s"
+ , __FUNCTION__);
+ rc = S_OOPS;
+ goto done;
+ }
+ }
+
+done:
+ close_hpi_session(dev);
+ return (rc == S_OK) ? (dev->ohdevid ? S_OK : S_OOPS) : rc;
+}
+
+
+/*
+ * Return the list of hosts configured for this HMC device
+ */
+
+static char **
+bladehpi_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice * dev;
+ int numnames = 0, j;
+ char ** ret = NULL;
+ GList * node = NULL;
+ SaErrorT ohrc;
+ SaHpiDomainInfoT ohdi;
+ int rc = S_OK;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s, NULL);
+
+ dev = (struct pluginDevice *)s;
+ rc = open_hpi_session(dev);
+ if( rc != S_OK )
+ return NULL;
+
+ /* Refresh the hostlist only if RPTs updated */
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ goto done;
+ }
+ if (dev->ohrptcnt != ohdi.RptUpdateCount) {
+ free_bladehpi_hostlist(dev);
+ if (get_bladehpi_hostlist(dev) != S_OK) {
+ LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
+ , __FUNCTION__);
+ goto done;
+ }
+ }
+
+ /* At this point, hostlist is up to date */
+ numnames = g_list_length(dev->hostlist);
+ if (numnames < 0) {
+ LOG(PIL_CRIT, "Unconfigured stonith object in %s"
+ , __FUNCTION__);
+ goto done;
+ }
+
+ ret = (char **)MALLOC((numnames+1) * sizeof(char *));
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "Out of memory for malloc in %s", __FUNCTION__);
+ goto done;
+ }
+
+ memset(ret, 0, (numnames+1) * sizeof(char *));
+ for (node = g_list_first(dev->hostlist), j = 0
+ ; NULL != node
+ ; j++, node = g_list_next(node)) {
+ ret[j] = STRDUP(((struct blade_info *)node->data)->name);
+ if (ret[j] == NULL) {
+ LOG(PIL_CRIT, "Out of memory for strdup in %s"
+ , __FUNCTION__);
+ stonith_free_hostlist(ret);
+ ret = NULL;
+ goto done;
+ }
+ strdown(ret[j]);
+ }
+
+done:
+ close_hpi_session(dev);
+ return ret;
+}
+
+
+static const char * const *
+bladehpi_get_confignames(StonithPlugin *s)
+{
+ static const char * names[] = {ST_ENTITYROOT, NULL};
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ return names;
+}
+
+
+/*
+ * Reset the given host, and obey the request type.
+ */
+
+static int
+bladehpi_reset_req(StonithPlugin *s, int request, const char *host)
+{
+ GList * node = NULL;
+ struct pluginDevice * dev = NULL;
+ struct blade_info * bi = NULL;
+ SaHpiPowerStateT ohcurstate, ohnewstate;
+ SaHpiDomainInfoT ohdi;
+ SaErrorT ohrc;
+ int rc = S_OK;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called, request=%d, host=%s"
+ , __FUNCTION__, request, host);
+ }
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if (host == NULL) {
+ LOG(PIL_CRIT, "Invalid host argument to %s", __FUNCTION__);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ dev = (struct pluginDevice *)s;
+ rc = open_hpi_session(dev);
+ if( rc != S_OK )
+ return rc;
+
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ rc = S_BADCONFIG;
+ goto done;
+ }
+ if (dev->ohrptcnt != ohdi.RptUpdateCount) {
+ free_bladehpi_hostlist(dev);
+ if (get_bladehpi_hostlist(dev) != S_OK) {
+ LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
+ , __FUNCTION__);
+ rc = S_OOPS;
+ goto done;
+ }
+ }
+
+ for (node = g_list_first(dev->hostlist)
+ ; node != NULL
+ ; node = g_list_next(node)) {
+ bi = ((struct blade_info *)node->data);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Found host %s in hostlist", bi->name);
+ }
+
+ if (!strcasecmp(bi->name, host)) {
+ break;
+ }
+ }
+
+ if (!node || !bi) {
+ LOG(PIL_CRIT
+ , "Host %s is not configured in this STONITH module, "
+ "please check your configuration information", host);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ /* Make sure host has proper capabilities for get */
+ if (!(bi->resourceCaps & SAHPI_CAPABILITY_POWER)) {
+ LOG(PIL_CRIT
+ , "Host %s does not have power capability", host);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ ohrc = saHpiResourcePowerStateGet(dev->ohsession, bi->resourceId
+ , &ohcurstate);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get host %s power state (%d)"
+ , host, ohrc);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ switch (request) {
+ case ST_POWERON:
+ if (ohcurstate == SAHPI_POWER_ON) {
+ LOG(PIL_INFO, "Host %s already on", host);
+ goto done;
+ }
+ ohnewstate = SAHPI_POWER_ON;
+
+ break;
+
+ case ST_POWEROFF:
+ if (ohcurstate == SAHPI_POWER_OFF) {
+ LOG(PIL_INFO, "Host %s already off", host);
+ goto done;
+ }
+ ohnewstate = SAHPI_POWER_OFF;
+
+ break;
+
+ case ST_GENERIC_RESET:
+ if (ohcurstate == SAHPI_POWER_OFF) {
+ ohnewstate = SAHPI_POWER_ON;
+ } else {
+ ohnewstate = SAHPI_POWER_CYCLE;
+ }
+
+ break;
+
+ default:
+ LOG(PIL_CRIT, "Invalid request argument to %s"
+ , __FUNCTION__);
+ rc = S_INVAL;
+ goto done;
+ }
+
+ if (!dev->softreset && (ohnewstate == SAHPI_POWER_CYCLE)) {
+ int maxwait;
+
+ ohrc = saHpiResourcePowerStateSet(dev->ohsession
+ , bi->resourceId, SAHPI_POWER_OFF);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to set host %s power state to"
+ " OFF (%d)", host, ohrc);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ /*
+ * Must wait for power off here or subsequent power on request
+ * may take place while power is still on and thus ignored
+ */
+ maxwait = MAX_POWEROFF_WAIT;
+ do {
+ maxwait--;
+ sleep(1);
+ ohrc = saHpiResourcePowerStateGet(dev->ohsession
+ , bi->resourceId, &ohcurstate);
+ } while ((ohrc == SA_OK)
+ && (ohcurstate != SAHPI_POWER_OFF)
+ && (maxwait > 0));
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Waited %d seconds for power off"
+ , MAX_POWEROFF_WAIT - maxwait);
+ }
+
+ ohrc = saHpiResourcePowerStateSet(dev->ohsession
+ , bi->resourceId, SAHPI_POWER_ON);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to set host %s power state to"
+ " ON (%d)", host, ohrc);
+ rc = S_OOPS;
+ goto done;
+ }
+ } else {
+ /* Make sure host has proper capabilities to reset */
+ if ((ohnewstate == SAHPI_POWER_CYCLE) &&
+ (!(bi->resourceCaps & SAHPI_CAPABILITY_RESET))) {
+ LOG(PIL_CRIT
+ , "Host %s does not have reset capability"
+ , host);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ if ((ohrc = saHpiResourcePowerStateSet(dev->ohsession
+ , bi->resourceId, ohnewstate)) != SA_OK) {
+ LOG(PIL_CRIT, "Unable to set host %s power state (%d)"
+ , host, ohrc);
+ rc = S_OOPS;
+ goto done;
+ }
+ }
+
+#ifdef IBMBC_WAIT_FOR_OFF
+ if (ohnewstate == SAHPI_POWER_OFF) {
+ int maxwait = MAX_POWEROFF_WAIT;
+
+ do {
+ maxwait--;
+ sleep(1);
+ ohrc = saHpiResourcePowerStateGet(dev->ohsession
+ , bi->resourceId, &ohcurstate);
+ } while ((ohrc == SA_OK)
+ && (ohcurstate != SAHPI_POWER_OFF)
+ && (maxwait > 0));
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Waited %d seconds for power off"
+ , MAX_POWEROFF_WAIT - maxwait);
+ }
+ }
+#endif
+
+ LOG(PIL_INFO, "Host %s %s %d.", host, __FUNCTION__, request);
+
+done:
+ close_hpi_session(dev);
+ return rc;
+}
+
+
+/*
+ * Parse the information in the given configuration file,
+ * and stash it away...
+ */
+
+static int
+bladehpi_set_config(StonithPlugin *s, StonithNVpair *list)
+{
+ struct pluginDevice * dev = NULL;
+ StonithNamesToGet namestocopy [] =
+ { {ST_ENTITYROOT, NULL}
+ , {NULL, NULL}
+ };
+ int rc, i;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ dev = (struct pluginDevice *)s;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s conditionally compiled with:"
+#ifdef IBMBC_WAIT_FOR_OFF
+ " IBMBC_WAIT_FOR_OFF"
+#endif
+ , dev->pluginid);
+ }
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s = %s", ST_ENTITYROOT
+ , namestocopy[0].s_value);
+ }
+
+ if (get_num_tokens(namestocopy[0].s_value) == 1) {
+ /* name=value pairs on command line, look for soft_reset */
+ const char *softreset =
+ OurImports->GetValue(list, ST_SOFTRESET);
+ if (softreset != NULL) {
+ if (!strcasecmp(softreset, "true") ||
+ !strcmp(softreset, "1")) {
+ dev->softreset = 1;
+ } else if (!strcasecmp(softreset, "false") ||
+ !strcmp(softreset, "0")) {
+ dev->softreset = 0;
+ } else {
+ LOG(PIL_CRIT, "Invalid %s %s, must be "
+ "true, 1, false or 0"
+ , ST_SOFTRESET, softreset);
+ FREE(namestocopy[0].s_value);
+ return S_OOPS;
+ }
+ }
+ } else {
+ /* -p or -F option with args "entity_root [soft_reset]..." */
+ char *pch = namestocopy[0].s_value;
+
+ /* skip over entity_root and null-terminate */
+ pch += strcspn(pch, WHITESPACE);
+ *pch = EOS;
+
+ /* skip over white-space up to next token */
+ pch++;
+ pch += strspn(pch, WHITESPACE);
+ if (!strcasecmp(pch, "true") || !strcmp(pch, "1")) {
+ dev->softreset = 1;
+ } else if (!strcasecmp(pch, "false") || !strcmp(pch, "0")) {
+ dev->softreset = 0;
+ } else {
+ LOG(PIL_CRIT, "Invalid %s %s, must be "
+ "true, 1, false or 0"
+ , ST_SOFTRESET, pch);
+ FREE(namestocopy[0].s_value);
+ return S_OOPS;
+ }
+ }
+
+ dev->device = STRDUP(namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ if (dev->device == NULL) {
+ LOG(PIL_CRIT, "Out of memory for strdup in %s", __FUNCTION__);
+ return S_OOPS;
+ }
+
+ if (strcspn(dev->device, WHITESPACE) != strlen(dev->device) ||
+ sscanf(dev->device, SYSTEM_CHASSIS_FMT, &i) != 1 || i < 0) {
+ LOG(PIL_CRIT, "Invalid %s %s, must be of format %s"
+ , ST_ENTITYROOT, dev->device, SYSTEM_CHASSIS_FMT);
+ return S_BADCONFIG;
+ }
+
+ dev->ohver = saHpiVersionGet();
+ if (dev->ohver > SAHPI_INTERFACE_VERSION) {
+ LOG(PIL_CRIT, "Installed OpenHPI interface (%x) greater than "
+ "one used by plugin (%x), incompatibilites may exist"
+ , dev->ohver, SAHPI_INTERFACE_VERSION);
+ return S_BADCONFIG;
+ }
+ return S_OK;
+}
+
+static int
+open_hpi_session(struct pluginDevice *dev)
+{
+ SaErrorT ohrc;
+
+ ohrc = saHpiSessionOpen(SAHPI_UNSPECIFIED_DOMAIN_ID
+ , &dev->ohsession, NULL);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to open HPI session (%d)", ohrc);
+ return S_BADCONFIG;
+ }
+
+ ohrc = saHpiDiscover(dev->ohsession);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to discover resources (%d)", ohrc);
+ return S_BADCONFIG;
+ }
+
+ return S_OK;
+}
+static void
+close_hpi_session(struct pluginDevice *dev)
+{
+ if (dev && dev->ohsession) {
+ saHpiSessionClose(dev->ohsession);
+ dev->ohsession = 0;
+ }
+}
+
+static const char *
+bladehpi_getinfo(StonithPlugin *s, int reqtype)
+{
+ struct pluginDevice * dev;
+ const char * ret;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called, reqtype=%d"
+ , __FUNCTION__, reqtype);
+ }
+
+ ERRIFWRONGDEV(s, NULL);
+
+ dev = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = dev->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = dev->device;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "IBM BladeCenter via OpenHPI\n"
+ "Use for IBM xSeries systems managed by BladeCenter\n"
+ " Required parameter name " ST_ENTITYROOT " is "
+ "a string (no white-space) of\n"
+ "the format \""SYSTEM_CHASSIS_FMT"\" "
+ "which is entity_root of BladeCenter\n"
+ "from OpenHPI config file, where %d is a positive "
+ "integer\n"
+ " Optional parameter name " ST_SOFTRESET " is "
+ "true|1 if STONITH device should\n"
+ "use soft reset (power cycle) to reset nodes or "
+ "false|0 if device should\n"
+ "use hard reset (power off, wait, power on); "
+ "default is false";
+ break;
+
+ case ST_DEVICEURL:
+ ret = OPENHPIURL;
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = bladehpiXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+
+ return ret;
+}
+
+
+/*
+ * HMC Stonith destructor...
+ */
+
+static void
+bladehpi_destroy(StonithPlugin *s)
+{
+ struct pluginDevice * dev;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ dev = (struct pluginDevice *)s;
+
+ dev->pluginid = NOTpluginID;
+ if (dev->device) {
+ FREE(dev->device);
+ dev->device = NULL;
+ }
+ if (dev->idinfo) {
+ FREE(dev->idinfo);
+ dev->idinfo = NULL;
+ }
+ free_bladehpi_hostlist(dev);
+
+ if (dev->ohsession) {
+ saHpiSessionClose(dev->ohsession);
+ dev->ohsession = 0;
+ }
+
+ FREE(dev);
+}
+
+
+static StonithPlugin *
+bladehpi_new(const char *subplugin)
+{
+ struct pluginDevice * dev = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ if (dev == NULL) {
+ LOG(PIL_CRIT, "Out of memory in %s", __FUNCTION__);
+ return NULL;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ dev->pluginid = pluginid;
+ dev->device = NULL;
+ dev->hostlist = NULL;
+ REPLSTR(dev->idinfo, DEVICE);
+ if (dev->idinfo == NULL) {
+ FREE(dev);
+ return NULL;
+ }
+ dev->sp.s_ops = &bladehpiOps;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: returning successfully", __FUNCTION__);
+ }
+
+ return ((void *)dev);
+}
+
+
+static int
+get_resource_type(char *entityRoot, SaHpiRptEntryT *ohRPT)
+{
+ int i, rc = OHRES_NONE;
+ int foundBlade = 0, foundExp = 0, foundMgmt = 0;
+ int foundRoot = 0, foundOther = 0;
+ char rootName[64];
+ SaHpiEntityPathT * ohep = &ohRPT->ResourceEntity;
+
+ if (ohep == NULL || entityRoot == NULL) {
+ return 0;
+ }
+
+ /* First find root of entity path, which is last entity in entry */
+ for (i = 0; i < SAHPI_MAX_ENTITY_PATH; i++) {
+ if (ohep->Entry[i].EntityType == SAHPI_ENT_ROOT) {
+ break;
+ }
+ }
+
+ /* Then back up through entries looking for specific entity */
+ for (i--; i >= 0; i--) {
+ switch (ohep->Entry[i].EntityType) {
+ case SAHPI_ENT_SBC_BLADE:
+ foundBlade = 1;
+ break;
+
+ case SAHPI_ENT_SYS_EXPANSION_BOARD:
+ foundExp = 1;
+ break;
+
+ case SAHPI_ENT_SYS_MGMNT_MODULE:
+ if (ohep->Entry[i].EntityLocation == 0) {
+ foundMgmt = 1;
+ }
+ break;
+
+ case SAHPI_ENT_SYSTEM_CHASSIS:
+ snprintf(rootName, sizeof(rootName)
+ , SYSTEM_CHASSIS_FMT
+ , ohep->Entry[i].EntityLocation);
+ if (!strcmp(entityRoot, rootName)) {
+ foundRoot = 1;
+ }
+ break;
+
+ default:
+ foundOther = 1;
+ break;
+ }
+ }
+
+ /* We are only interested in specific entities on specific device */
+ if (foundRoot) {
+ if (foundMgmt && !(foundBlade||foundExp||foundOther)) {
+ rc = OHRES_MGMTMOD;
+ } else if (!(foundMgmt||foundBlade||foundExp||foundOther)) {
+ rc = OHRES_BLADECENT;
+ } else if (foundBlade && !foundExp) {
+ rc = OHRES_BLADE;
+ }
+ }
+
+ return rc;
+}
+
+
+static int
+get_sensor_num(SaHpiSessionIdT ohsession, SaHpiResourceIdT ohresid)
+{
+ SaErrorT ohrc = SA_OK;
+ SaHpiEntryIdT ohnextid;
+ SaHpiRdrT ohRDR;
+
+ ohnextid = SAHPI_FIRST_ENTRY;
+ do {
+ ohrc = saHpiRdrGet(ohsession, ohresid, ohnextid
+ , &ohnextid, &ohRDR);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get RDR entry in %s (%d)"
+ , __FUNCTION__, ohrc);
+ } else if (ohRDR.RdrType == SAHPI_SENSOR_RDR) {
+ return ohRDR.RdrTypeUnion.SensorRec.Num;
+ }
+ } while (ohrc == SA_OK && ohnextid != SAHPI_LAST_ENTRY);
+
+ return 0;
+}
+
+
+/*
+ * Get RPT update count
+ * Loop through all RPT entries
+ * If entry is BladeCenter, save resource ID in dev->ohdevid
+ * If entry is MgmtMod and has sensor, save resource ID in dev->ohsensid
+ * and sensor number in dev->ohsensnum
+ * If entry is blade, save blade_info and add to dev->hostlist
+ * Get RPT update count
+ * If RPT update count changed since start of loop, repeat loop
+ * Save RPT update count in dev->ohrptcnt
+ *
+ * Note that not only does this function update hostlist, it also
+ * updates ohrptcnt, ohdevid, ohsensid and ohsensnum. However, with
+ * this logic it does not need to be called again until the RPT update
+ * count changes.
+ */
+
+static int
+get_bladehpi_hostlist(struct pluginDevice *dev)
+{
+ struct blade_info * bi;
+ SaErrorT ohrc;
+ SaHpiEntryIdT ohnextid;
+ SaHpiRptEntryT ohRPT;
+ SaHpiDomainInfoT ohdi;
+ SaHpiUint32T ohupdate;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called, dev->device=%s"
+ , __FUNCTION__, dev->device);
+ }
+
+ if (dev->device == NULL || *dev->device == 0) {
+ LOG(PIL_CRIT, "Unconfigured stonith object in %s"
+ , __FUNCTION__);
+ return S_BADCONFIG;
+ }
+
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ return S_BADCONFIG;
+ }
+
+try_again:
+ ohupdate = ohdi.RptUpdateCount;
+ dev->ohdevid = dev->ohsensid = dev->ohsensnum = 0;
+ ohnextid = SAHPI_FIRST_ENTRY;
+ do {
+ char blname[SAHPI_MAX_TEXT_BUFFER_LENGTH];
+ int blnum;
+
+ ohrc = saHpiRptEntryGet(dev->ohsession, ohnextid
+ , &ohnextid, &ohRPT);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get RPT entry in %s (%d)"
+ , __FUNCTION__, ohrc);
+ free_bladehpi_hostlist(dev);
+ return S_BADCONFIG;
+ }
+
+ switch (get_resource_type(dev->device, &ohRPT)) {
+ case OHRES_BLADECENT:
+ dev->ohdevid = ohRPT.ResourceId;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "BladeCenter '%s' has id %d"
+ , (char*)ohRPT.ResourceTag.Data
+ , dev->ohdevid);
+ }
+ break;
+
+ case OHRES_MGMTMOD:
+ if (ohRPT.ResourceCapabilities&SAHPI_CAPABILITY_SENSOR){
+ dev->ohsensnum = get_sensor_num(dev->ohsession
+ , ohRPT.ResourceId);
+
+ if (dev->ohsensnum) {
+ dev->ohsensid = ohRPT.ResourceId;
+
+ if (Debug) {
+ LOG(PIL_DEBUG
+ , "MgmtModule '%s' has id %d "
+ "with sensor #%d"
+ , (char*)ohRPT.ResourceTag.Data
+ , dev->ohsensid
+ , dev->ohsensnum);
+ }
+ }
+ }
+ break;
+
+ case OHRES_BLADE:
+ if ((bi = (struct blade_info *)
+ MALLOC(sizeof(struct blade_info))) == NULL) {
+ LOG(PIL_CRIT, "Out of memory in %s"
+ , __FUNCTION__);
+ free_bladehpi_hostlist(dev);
+ return S_OOPS;
+ }
+
+ /*
+ * New format consists of "Blade N - name" while older
+ * format consists only of "name"; we only need to
+ * stash name because ResourceID is the important info
+ */
+ if (sscanf((char*)ohRPT.ResourceTag.Data, "Blade %d - %s"
+ , &blnum, blname) == 2) {
+ bi->name = STRDUP(blname);
+ } else {
+ bi->name = STRDUP((char*)ohRPT.ResourceTag.Data);
+ }
+ if (bi->name == NULL) {
+ LOG(PIL_CRIT, "Out of memory for strdup in %s"
+ , __FUNCTION__);
+ free_bladehpi_hostlist(dev);
+ return S_OOPS;
+ }
+
+ bi->resourceId = ohRPT.ResourceId;
+ bi->resourceCaps = ohRPT.ResourceCapabilities;
+ dev->hostlist = g_list_append(dev->hostlist, bi);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Blade '%s' has id %d, caps %x"
+ , bi->name, bi->resourceId, bi->resourceCaps);
+ }
+ break;
+ }
+ } while (ohrc == SA_OK && ohnextid != SAHPI_LAST_ENTRY);
+
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ free_bladehpi_hostlist(dev);
+ return S_BADCONFIG;
+ }
+
+ if (ohupdate != ohdi.RptUpdateCount) {
+ free_bladehpi_hostlist(dev);
+ if(Debug){
+ LOG(PIL_DEBUG, "Looping through entries again,"
+ " count changed from %d to %d"
+ , ohupdate, ohdi.RptUpdateCount);
+ }
+ goto try_again;
+ }
+
+ dev->ohrptcnt = ohupdate;
+
+ return S_OK;
+}
+
+
+static void
+free_bladehpi_hostlist(struct pluginDevice *dev)
+{
+ if (dev->hostlist) {
+ GList *node;
+ while (NULL != (node = g_list_first(dev->hostlist))) {
+ dev->hostlist =
+ g_list_remove_link(dev->hostlist, node);
+ FREE(((struct blade_info *)node->data)->name);
+ FREE(node->data);
+ g_list_free(node);
+ }
+ dev->hostlist = NULL;
+ }
+ dev->ohdevid = dev->ohsensid = dev->ohsensnum = 0;
+}
+
+
+static int
+get_num_tokens(char *str)
+{
+ int namecount = 0;
+
+ while (*str != EOS) {
+ str += strspn(str, WHITESPACE);
+ if (*str == EOS)
+ break;
+ str += strcspn(str, WHITESPACE);
+ namecount++;
+ }
+ return namecount;
+}
diff --git a/lib/plugins/stonith/cyclades.c b/lib/plugins/stonith/cyclades.c
new file mode 100644
index 0000000..6744cd4
--- /dev/null
+++ b/lib/plugins/stonith/cyclades.c
@@ -0,0 +1,650 @@
+/*
+ * Stonith module for Cyclades AlterPath PM
+ * Bases off the SSH plugin
+ *
+ * Copyright (c) 2004 Cyclades corp.
+ *
+ * Author: Jon Taylor <jon.taylor@cyclades.com>
+ *
+ * Rewritten from scratch using baytech.c structure and code
+ * and currently maintained by
+ * Marcelo Tosatti <marcelo.tosatti@cyclades.com>
+ *
+ * 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 "Cyclades AlterPath PM"
+
+#define DOESNT_USE_STONITHSCANLINE
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN cyclades
+#define PIL_PLUGIN_S "cyclades"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * cyclades_new(const char *);
+static void cyclades_destroy(StonithPlugin *);
+static int cyclades_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * cyclades_get_confignames(StonithPlugin * s);
+static const char * cyclades_get_info(StonithPlugin * s, int InfoType);
+static int cyclades_status(StonithPlugin *);
+static int cyclades_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** cyclades_hostlist(StonithPlugin *);
+
+
+
+static struct stonith_ops cycladesOps ={
+ cyclades_new, /* Create new STONITH object */
+ cyclades_destroy, /* Destroy STONITH object */
+ cyclades_get_info, /* Return STONITH info string */
+ cyclades_get_confignames, /* Return STONITH config vars */
+ cyclades_set_config, /* set configuration from vars */
+ cyclades_status, /* Return STONITH device status */
+ cyclades_reset_req, /* Request a reset */
+ cyclades_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
+ , &cycladesOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * Cyclades STONITH device
+ *
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char * device;
+ char * user;
+
+ int serial_port;
+
+ /* pid of ssh client process and its in/out file descriptors */
+ pid_t pid;
+ int rdfd, wrfd;
+};
+
+static struct Etoken StatusOutput[] = {
+ { "Outlet\t\tName\t\tStatus\t\tUsers\t\tInterval (s)", 1, 0},
+ { "Outlet\tName\t\t\tStatus\t\tInterval (s)\tUsers", 2, 0},
+ { "Outlet Name Status Post-on Delay(s)", 3, 0},
+ { NULL, 0, 0}
+};
+
+static struct Etoken CRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};
+
+
+/* Commands of PM devices */
+static char status_all[] = "status all";
+static char cycle[] = "cycle";
+
+static int CYC_robust_cmd(struct pluginDevice *, char *);
+
+static const char * pluginid = "CycladesDevice-Stonith";
+static const char * NOTpluginID = "Cyclades device has been destroyed";
+
+#define MAX_OUTLETS 128
+
+#define ST_SERIALPORT "serialport"
+
+#define ZEROEXPECT(fd,p,t) { \
+ if (StonithLookFor(fd, p, t) < 0) \
+ return(0); \
+ }
+
+#define RESETEXPECT(fd,p,t) { \
+ if (StonithLookFor(fd, p, t) < 0) { \
+ FREE(outletstr); \
+ return(errno == ETIMEDOUT \
+ ? S_RESETFAIL : S_OOPS); \
+ } \
+ }
+
+#include "stonith_config_xml.h"
+
+#define XML_SERIALPORT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_SERIALPORT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_SERIALPORT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The serial port of the IPDU which can powercycle the node" \
+ XML_PARM_LONGDESC_END
+
+#define XML_SERIALPORT_PARM \
+ XML_PARAMETER_BEGIN(ST_SERIALPORT, "string", "1", "0") \
+ XML_SERIALPORT_SHORTDESC \
+ XML_SERIALPORT_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *cycladesXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_LOGIN_PARM
+ XML_SERIALPORT_PARM
+ XML_PARAMETERS_END;
+
+static int
+CYCScanLine(struct pluginDevice *sd, int timeout, char * buf, int max)
+{
+ if (EXPECT_TOK(sd->rdfd, CRNL, timeout, buf, max, Debug) < 0) {
+ Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid);
+ return(S_OOPS);
+ }
+ return(S_OK);
+}
+
+static int
+cyclades_status(StonithPlugin *s)
+{
+ struct pluginDevice *sd;
+ char *cmd = status_all;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ sd = (struct pluginDevice*) s;
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run status all command");
+ return(S_OOPS);
+ }
+
+ EXPECT(sd->rdfd, StatusOutput, 50);
+
+ return(S_OK);
+}
+
+static int CYC_run_command(struct pluginDevice *sd, char *cmd)
+{
+ char SshCommand[MAX_OUTLETS*4];
+
+ snprintf(SshCommand, sizeof(SshCommand),
+ "exec ssh -q %s@%s /bin/pmCommand %d %s 2>/dev/null",
+ sd->user, sd->device, sd->serial_port, cmd);
+
+ sd->pid = STARTPROC(SshCommand, &sd->rdfd, &sd->wrfd);
+
+ if (sd->pid <= 0) {
+ return(S_OOPS);
+ }
+
+ return(S_OK);
+}
+
+static int
+CYC_robust_cmd(struct pluginDevice *sd, char *cmd)
+{
+ int rc = S_OOPS;
+ int i;
+
+ for (i=0; i < 20 && rc != S_OK; i++) {
+
+ if (sd->pid > 0) {
+ Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid);
+ }
+
+ if (CYC_run_command(sd, cmd) != S_OK) {
+ Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid);
+ continue;
+ }
+ rc = S_OK;
+ }
+
+ return rc;
+}
+
+#define MAXSAVE 512
+static int CYCNametoOutlet(struct pluginDevice *sd, const char *host, int *outlets, int maxoutlet)
+{
+ char *cmd = status_all;
+ char savebuf[MAXSAVE];
+ int err;
+ int outlet, numoutlet = 0;
+ char name[17], locked[11], on[4];
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run status all command");
+ return 0;
+ }
+
+ ZEROEXPECT(sd->rdfd, StatusOutput, 50);
+
+ ZEROEXPECT(sd->rdfd, CRNL, 50);
+
+ do {
+
+ memset(savebuf, 0, sizeof(savebuf));
+ memset(name, 0, sizeof(name));
+ memset(locked, 0, sizeof(locked));
+ memset(on, 0, sizeof(on));
+
+ err = CYCScanLine(sd, 2, savebuf, sizeof(savebuf));
+
+ if ((err == S_OK) &&
+ (sscanf(savebuf,"%3d %16s %10s %3s", &outlet,
+ name, locked, on) > 0)) {
+ if (!strncasecmp(name, host, strlen(host))) {
+ if (numoutlet >= maxoutlet) {
+ LOG(PIL_CRIT, "too many outlets");
+ return 0;
+ }
+ outlets[numoutlet++] = outlet;
+ }
+ }
+
+ } while (err == S_OK);
+
+ return (numoutlet);
+}
+
+
+/*
+ * Return the list of hosts configured for this Cyclades device
+ */
+
+static char **
+cyclades_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+ char *cmd = status_all;
+ char savebuf[MAXSAVE];
+ int err, i;
+ int outlet;
+ int numnames = 0;
+ char name[17], locked[11], on[4];
+ char *NameList[MAX_OUTLETS];
+ char **ret = NULL;
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ sd = (struct pluginDevice*) s;
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run status all command");
+ return (NULL);
+ }
+
+ memset(savebuf, 0, sizeof(savebuf));
+
+ NULLEXPECT(sd->rdfd, StatusOutput, 50);
+
+ NULLEXPECT(sd->rdfd, CRNL, 50);
+
+ do {
+ char *nm;
+
+ memset(savebuf, 0, sizeof(savebuf));
+ memset(name, 0, sizeof(name));
+ memset(locked, 0, sizeof(locked));
+ memset(on, 0, sizeof(on));
+
+ err = CYCScanLine(sd, 2, savebuf, sizeof(savebuf));
+
+ if ((err == S_OK) &&
+ (sscanf(savebuf,"%3d %16s %10s %3s", &outlet,
+ name, locked, on) > 0)) {
+ nm = (char *) STRDUP (name);
+ if (!nm) {
+ goto out_of_memory;
+ }
+ strdown(nm);
+ NameList[numnames] = nm;
+ numnames++;
+ NameList[numnames] = NULL;
+ }
+
+ } while (err == S_OK);
+
+ if (numnames) {
+
+ ret = (char **)MALLOC((numnames+1)*sizeof(char*));
+ if (ret == NULL) {
+ goto out_of_memory;
+ } else {
+ memcpy(ret, NameList, (numnames+1)*sizeof(char*));
+ }
+ return (ret);
+ }
+ return(ret);
+
+out_of_memory:
+ LOG(PIL_CRIT, "out of memory");
+ for (i=0; i<numnames; i++) {
+ FREE(NameList[i]);
+ }
+
+ return (NULL);
+}
+
+
+static char *cyclades_outletstr(int *outlet, int numoutlet)
+{
+ int i, len;
+ char *ret;
+
+ /* maximum length per outlet is currently four (outlet is one to
+ * three digits, followed by either a comma or null), so add one
+ * for good measure */
+ len = numoutlet * 5 * sizeof(char);
+ if ((ret = MALLOC(len)) != NULL) {
+ snprintf(ret, len, "%d", outlet[0]);
+ for (i = 1; i < numoutlet; i++) {
+ char buf[5];
+ snprintf(buf, sizeof(buf), ",%d", outlet[i]);
+ strcat(ret, buf);
+ }
+ }
+ return(ret);
+}
+
+
+static int cyclades_onoff(struct pluginDevice *sd, int *outlet, int numoutlet,
+ const char *unitid, int req)
+{
+ const char * onoff;
+ char cmd[MAX_OUTLETS*4], expstring[64];
+ struct Etoken exp[] = {{NULL, 0, 0}, {NULL, 0, 0}};
+ char *outletstr;
+ int i;
+
+ onoff = (req == ST_POWERON ? "on" : "off");
+
+ memset(cmd, 0, sizeof(cmd));
+
+ outletstr = cyclades_outletstr(outlet, numoutlet);
+ if (outletstr == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return (S_OOPS);
+ }
+ snprintf(cmd, sizeof(cmd), "%s %s", onoff, outletstr);
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run %s command", onoff);
+ FREE(outletstr);
+ return(S_OOPS);
+ }
+
+ for (i = 0; i < numoutlet; i++) {
+ memset(expstring, 0, sizeof(expstring));
+ snprintf(expstring, sizeof(expstring), "%d: Outlet turned %s."
+ , outlet[i], onoff);
+
+ exp[0].string = expstring;
+
+ /* FIXME: should handle "already powered on/off" case and inform
+ to log */
+
+ EXPECT(sd->rdfd, exp, 50);
+ }
+
+ LOG(PIL_DEBUG, "Power to host %s turned %s", unitid, onoff);
+
+ FREE(outletstr);
+ return (S_OK);
+}
+
+static int cyclades_reset(struct pluginDevice *sd, int *outlet, int numoutlet,
+ const char *unitid)
+{
+ char cmd[MAX_OUTLETS*4], expstring[64];
+ struct Etoken exp[] = {{NULL, 0, 0}, {NULL, 0, 0}};
+ char *outletstr;
+ int i;
+
+ memset(cmd, 0, sizeof(cmd));
+
+ outletstr = cyclades_outletstr(outlet, numoutlet);
+ if (outletstr == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return (S_OOPS);
+ }
+ snprintf(cmd, sizeof(cmd), "%s %s", cycle, outletstr);
+
+ LOG(PIL_INFO, "Host %s being rebooted.", unitid);
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run cycle command");
+ FREE(outletstr);
+ return(S_OOPS);
+ }
+
+ for (i = 0; i < numoutlet; i++) {
+ memset(expstring, 0, sizeof(expstring));
+ snprintf(expstring, sizeof(expstring)
+ , "%d: Outlet turned off.", outlet[i]);
+
+ exp[0].string = expstring;
+ RESETEXPECT(sd->rdfd, exp, 50);
+ }
+
+ for (i = 0; i < numoutlet; i++) {
+ memset(expstring, 0, sizeof(expstring));
+ snprintf(expstring, sizeof(expstring)
+ , "%d: Outlet turned on.", outlet[i]);
+
+ exp[0].string = expstring;
+ RESETEXPECT(sd->rdfd, exp, 50);
+ }
+
+ FREE(outletstr);
+ return (S_OK);
+}
+
+/*
+ * Reset the given host on this Stonith device.
+ */
+static int
+cyclades_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice *sd;
+ int rc = 0;
+ int numoutlet, outlets[MAX_OUTLETS];
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ sd = (struct pluginDevice*) s;
+
+ numoutlet = CYCNametoOutlet(sd, host, outlets, MAX_OUTLETS);
+
+ if (!numoutlet) {
+ LOG(PIL_CRIT, "Unknown host %s to Cyclades PM", host);
+ return (S_OOPS);
+ }
+
+
+ switch (request) {
+ case ST_POWERON:
+ case ST_POWEROFF:
+ rc = cyclades_onoff(sd, outlets, numoutlet, host, request);
+ break;
+
+ case ST_GENERIC_RESET:
+ rc = cyclades_reset(sd, outlets, numoutlet, host);
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static const char * const *
+cyclades_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_LOGIN, ST_SERIALPORT, NULL};
+ return ret;
+}
+
+/*
+ * Parse the config information in the given string, and stash it away...
+ */
+static int
+cyclades_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy[] =
+ { {ST_IPADDR, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_SERIALPORT, 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->serial_port = atoi(namestocopy[2].s_value);
+ FREE(namestocopy[2].s_value);
+
+ return(S_OK);
+}
+
+static const char *
+cyclades_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice * sd;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ sd = (struct pluginDevice*) s;
+
+ switch (reqtype) {
+ case ST_DEVICEID: /* What type of device? */
+ /* FIXME: could inform the exact PM model */
+ ret = sd->idinfo;
+ break;
+
+ case ST_DEVICENAME: /* What particular device? */
+ ret = sd->device;
+ break;
+
+ case ST_DEVICEDESCR: /* Description of dev type */
+ ret = "Cyclades AlterPath PM "
+ "series power switches (via TS/ACS/KVM).";
+ break;
+
+ case ST_DEVICEURL: /* Manufacturer's web site */
+ ret = "http://www.cyclades.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = cycladesXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Cyclades Stonith destructor...
+ */
+static void
+cyclades_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd = (struct pluginDevice*) s;
+
+ sd->pluginid = NOTpluginID;
+ Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid);
+ if (sd->device != NULL) {
+ FREE(sd->device);
+ sd->device = NULL;
+ }
+ if (sd->user != NULL) {
+ FREE(sd->user);
+ sd->user = NULL;
+ }
+
+ FREE(sd);
+}
+
+/* Create a new cyclades Stonith device */
+static StonithPlugin *
+cyclades_new(const char *plugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ sd->pid = -1;
+ sd->rdfd = -1;
+ sd->wrfd = -1;
+ sd->idinfo = DEVICE;
+ sd->sp.s_ops = &cycladesOps;
+
+ return &(sd->sp); /* same as sd */
+}
diff --git a/lib/plugins/stonith/drac3.c b/lib/plugins/stonith/drac3.c
new file mode 100644
index 0000000..95be775
--- /dev/null
+++ b/lib/plugins/stonith/drac3.c
@@ -0,0 +1,359 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ * Tiny bits Copyright 2005 International Business Machines
+ * Significantly Mangled by Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005
+ *
+ * (Using snippets of other stonith modules code)
+ *
+ * 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
+ *
+ */
+
+#define DEVICE "Dell DRACIII Card"
+#include "stonith_plugin_common.h"
+
+#include <curl/curl.h>
+#include "drac3_command.h"
+
+#define PIL_PLUGIN drac3
+#define PIL_PLUGIN_S "drac3"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+#include "stonith_signal.h"
+
+static StonithPlugin * drac3_new(const char *);
+static void drac3_destroy(StonithPlugin *);
+static const char * const * drac3_get_confignames(StonithPlugin *);
+static int drac3_set_config(StonithPlugin *, StonithNVpair *);
+static const char * drac3_getinfo(StonithPlugin * s, int InfoType);
+static int drac3_status(StonithPlugin * );
+static int drac3_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** drac3_hostlist(StonithPlugin *);
+
+static struct stonith_ops drac3Ops ={
+ drac3_new, /* Create new STONITH object */
+ drac3_destroy, /* Destroy STONITH object */
+ drac3_getinfo, /* Return STONITH info string */
+ drac3_get_confignames, /* Return configuration parameters */
+ drac3_set_config, /* Set configuration */
+ drac3_status, /* Return STONITH device status */
+ drac3_reset_req, /* Request a reset */
+ drac3_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &drac3Ops
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+#define BUFLEN 1024
+#define ST_HOST "host"
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char *pluginid;
+ const char *idinfo;
+ CURL *curl;
+ char *host;
+ char *user;
+ char *pass;
+};
+
+static const char *pluginid = "Dell-DRACIII-Stonith";
+static const char *NOTpluginID = "Dell DRACIII device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_HOST_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_HOST \
+ XML_PARM_SHORTDESC_END
+
+#define XML_HOST_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The hostname of the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_HOST_PARM \
+ XML_PARAMETER_BEGIN(ST_HOST, "string", "1", "1") \
+ XML_HOST_SHORTDESC \
+ XML_HOST_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *drac3XML =
+ XML_PARAMETERS_BEGIN
+ XML_HOST_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+/* ------------------------------------------------------------------ */
+/* STONITH PLUGIN API */
+/* ------------------------------------------------------------------ */
+static StonithPlugin *
+drac3_new(const char *subplugin)
+{
+ struct pluginDevice *drac3d = ST_MALLOCT(struct pluginDevice);
+
+ if (drac3d == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(drac3d, 0, sizeof(*drac3d));
+ drac3d->pluginid = pluginid;
+ drac3d->curl = curl_easy_init();
+ drac3InitCurl(drac3d->curl);
+ drac3d->host = NULL;
+ drac3d->user = NULL;
+ drac3d->pass = NULL;
+ drac3d->idinfo = DEVICE;
+ drac3d->sp.s_ops = &drac3Ops;
+ return (&(drac3d->sp));
+}
+
+/* ------------------------------------------------------------------ */
+static void
+drac3_destroy(StonithPlugin * s)
+{
+ struct pluginDevice *drac3d;
+
+ VOIDERRIFWRONGDEV(s);
+
+ drac3d = (struct pluginDevice *) s;
+
+ drac3d->pluginid = NOTpluginID;
+
+ /* release curl connection */
+ if (drac3d->curl != NULL) {
+ drac3Logout(drac3d->curl, drac3d->host);
+ curl_easy_cleanup(drac3d->curl);
+ drac3d->curl = NULL;
+ }
+
+ if (drac3d->host != NULL) {
+ FREE(drac3d->host);
+ drac3d->host = NULL;
+ }
+ if (drac3d->user != NULL) {
+ FREE(drac3d->user);
+ drac3d->user = NULL;
+ }
+ if (drac3d->pass != NULL) {
+ FREE(drac3d->pass);
+ drac3d->pass = NULL;
+ }
+
+ /* release stonith-object itself */
+ FREE(drac3d);
+}
+
+/* ------------------------------------------------------------------ */
+static const char * const *
+drac3_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_HOST, ST_LOGIN, ST_PASSWD, NULL};
+ return ret;
+}
+
+/* ------------------------------------------------------------------ */
+static int
+drac3_set_config(StonithPlugin * s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOST, 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->host = namestocopy[0].s_value;
+ sd->user = namestocopy[1].s_value;
+ sd->pass = namestocopy[2].s_value;
+
+ return(S_OK);
+}
+
+/* ------------------------------------------------------------------ */
+const char *
+drac3_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice *drac3d;
+ const char *ret = NULL;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ drac3d = (struct pluginDevice *) s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = drac3d->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = drac3d->host;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Dell DRACIII (via HTTPS)\n"
+ "The Dell Remote Access Controller accepts XML "
+ "commands over HTTPS";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.dell.com/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = drac3XML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+
+ return(ret);
+}
+
+/* ------------------------------------------------------------------ */
+int
+drac3_status(StonithPlugin *s)
+{
+ struct pluginDevice *drac3d;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ drac3d = (struct pluginDevice *) s;
+
+ if (drac3VerifyLogin(drac3d->curl, drac3d->host)) {
+ if (drac3Login(drac3d->curl, drac3d->host,
+ drac3d->user, drac3d->pass)) {
+ LOG(PIL_CRIT, "%s: cannot log into %s at %s",
+ __FUNCTION__,
+ drac3d->idinfo,
+ drac3d->host);
+ return(S_ACCESS);
+ }
+ }
+
+ if (drac3GetSysInfo(drac3d->curl, drac3d->host)) {
+ return(S_ACCESS);
+ }else{
+ return(S_OK);
+ }
+}
+
+/* ------------------------------------------------------------------ */
+int
+drac3_reset_req(StonithPlugin * s, int request, const char *host)
+{
+ struct pluginDevice *drac3d;
+ int rc = S_OK;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ drac3d = (struct pluginDevice *) s;
+
+ if (strcasecmp(host, drac3d->host)) {
+ LOG(PIL_CRIT, "%s doesn't control host [%s]"
+ , drac3d->idinfo, host);
+ return(S_BADHOST);
+ }
+
+ if (drac3VerifyLogin(drac3d->curl, drac3d->host)) {
+ if (drac3Login(drac3d->curl, drac3d->host,
+ drac3d->user, drac3d->pass)) {
+ LOG(PIL_CRIT, "%s: cannot log into %s at %s",
+ __FUNCTION__,
+ drac3d->idinfo,
+ drac3d->host);
+ return(S_ACCESS);
+ }
+ }
+
+ switch(request) {
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+ case ST_POWERON:
+ case ST_POWEROFF:
+ /* TODO... */
+#endif
+ case ST_GENERIC_RESET:
+ if (drac3PowerCycle(drac3d->curl, drac3d->host))
+ rc = S_ACCESS;
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+
+ return(rc);
+}
+
+/* ------------------------------------------------------------------ */
+char **
+drac3_hostlist(StonithPlugin * s)
+{
+ struct pluginDevice *drac3d;
+ char **hl;
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ drac3d = (struct pluginDevice *) s;
+
+ hl = OurImports->StringToHostList(drac3d->host);
+ if (hl == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ } else {
+ strdown(hl[0]);
+ }
+
+ return(hl);
+}
diff --git a/lib/plugins/stonith/drac3_command.c b/lib/plugins/stonith/drac3_command.c
new file mode 100644
index 0000000..4d9002d
--- /dev/null
+++ b/lib/plugins/stonith/drac3_command.c
@@ -0,0 +1,342 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ *
+ * 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>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <curl/curl.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+
+#include "drac3_command.h"
+#include "drac3_hash.h"
+
+#define BUFLEN 1024 /* buffer */
+#define SBUFLEN 256 /* small buffer */
+#define MD5LEN 16 /* md5 buffer */
+
+#define DEBUG 0
+
+/* Hardcoded XML commands and response codes */
+#define CMD_POWERCYCLE "<?XML version=\"1.0\"?><?RMCXML version=\"1.0\"?><RMCSEQ><REQ CMD=\"serveraction\"><ACT>powercycle</ACT></REQ></RMCSEQ>\n"
+#define CMD_GETSYSINFO "<?XML version=\"1.0\"?><?RMCXML version=\"1.0\"?><RMCSEQ><REQ CMD=\"xml2cli\"><CMDINPUT>getsysinfo -A</CMDINPUT></REQ></RMCSEQ>\n"
+#define RC_OK "0x0\n"
+
+struct Chunk {
+ char *memory;
+ size_t size;
+};
+
+/* prototypes */
+int xmlGetXPathString (const char *str, const char * expr, char * rc, const int len);
+size_t writeFunction (void *ptr, size_t size, size_t nmemb, void *data);
+
+
+/* ---------------------------------------------------------------------- *
+ * XML PARSING *
+ * ---------------------------------------------------------------------- */
+
+int
+xmlGetXPathString (const char *str,
+ const char * expr,
+ char * rc,
+ const int len)
+{
+ xmlDocPtr doc;
+ xmlNodePtr cur;
+ xmlXPathContextPtr ctx;
+ xmlXPathObjectPtr path;
+ xmlChar *xmlRC;
+
+ if (!strchr(str,'<')) {
+ fprintf(stderr,"%s malformed\n", str);
+ rc[0] = 0x00;
+ return(1);
+ }
+
+ doc = xmlParseMemory(str, strlen(str));
+ xmlXPathInit();
+ ctx = xmlXPathNewContext(doc);
+ path = xmlXPathEvalExpression((const xmlChar *)expr, ctx);
+ cur = path->nodesetval->nodeTab[0];
+
+ if (cur != NULL) {
+ xmlRC = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+ snprintf(rc, len, "%s\n", xmlRC);
+ xmlFree(xmlRC);
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ xmlXPathFreeObject(path);
+ xmlXPathFreeContext(ctx);
+
+ return(0);
+ } else {
+ fprintf(stderr,"error in obtaining XPath %s\n", expr);
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ xmlXPathFreeObject(path);
+ xmlXPathFreeContext(ctx);
+
+ rc[0] = 0x00;
+ return(1);
+ }
+}
+
+
+/* ---------------------------------------------------------------------- *
+ * CURL CALLBACKS *
+ * ---------------------------------------------------------------------- */
+
+size_t
+writeFunction (void *ptr, size_t size, size_t nmemb, void *data)
+{
+
+ register int realsize = size * nmemb;
+ struct Chunk *mem = (struct Chunk *)data;
+
+ mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
+ if (mem->memory) {
+ memcpy(&(mem->memory[mem->size]), ptr, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+ }
+ return realsize;
+}
+
+
+/* ---------------------------------------------------------------------- *
+ * DRAC3 CURL COMMANDS *
+ * ---------------------------------------------------------------------- */
+
+int
+drac3InitCurl (CURL *curl)
+{
+#ifdef CURLOPT_NOSIGNAL
+ if (curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1)) return(1);
+#endif
+ if (curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_VERBOSE, 0)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/dev/null")) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0)) return(1);
+ return(0);
+}
+
+int
+drac3Login (CURL *curl,
+ const char *host,
+ const char *user,
+ const char *pass)
+{
+ char url[BUFLEN];
+ char chall[BUFLEN];
+ char token[BUFLEN];
+ char rc[SBUFLEN];
+ int status;
+ struct Chunk chunk;
+
+ chunk.memory = NULL;
+ chunk.size = 0;
+ if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk))
+ return(1);
+
+ /* ask for challenge */
+ snprintf(url, BUFLEN, "https://%s/cgi/challenge", host);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url))
+ return(1);
+ if (curl_easy_perform(curl))
+ return(1);
+
+ /* extract challenge */
+ status = xmlGetXPathString(chunk.memory, "//CHALLENGE", chall, BUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+
+ /* calculate authToken */
+ drac3AuthHash(chall, pass, token, BUFLEN);
+
+ if (DEBUG) printf("T: %s\n", token);
+
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ if (status) return(1);
+ chunk.memory = NULL;
+ chunk.size = 0;
+
+ /* sends authToken */
+ snprintf(url, BUFLEN, "https://%s/cgi/login?user=%s&hash=%s",
+ host,
+ user,
+ token);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url))
+ return(1);
+ if (curl_easy_perform(curl))
+ return(1);
+
+ if (DEBUG) printf("R: %s\n", chunk.memory);
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ return(status);
+}
+
+int
+drac3PowerCycle (CURL *curl,
+ const char *host)
+{
+ char url[BUFLEN];
+ char cmd[]=CMD_POWERCYCLE;
+ char rc[SBUFLEN];
+ int status;
+ struct Chunk chunk;
+
+ chunk.memory = NULL;
+ chunk.size = 0;
+ if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk)) return(1);
+
+ snprintf(url, BUFLEN, "https://%s/cgi/bin",
+ host);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_POSTFIELDS, cmd)) return(1);
+ if (curl_easy_perform(curl)) return(1);
+
+ if (DEBUG) printf("R: %s\n", chunk.memory);
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ return(status);
+}
+
+
+int
+drac3GetSysInfo (CURL *curl,
+ const char *host)
+{
+ char url[BUFLEN];
+ char cmd[]=CMD_GETSYSINFO;
+ char rc[SBUFLEN];
+ int status;
+ struct Chunk chunk;
+
+ chunk.memory = NULL;
+ chunk.size = 0;
+ if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk)) return(1);
+
+ snprintf(url, BUFLEN, "https://%s/cgi/bin",
+ host);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_POSTFIELDS, cmd)) return(1);
+ if (curl_easy_perform(curl)) return(1);
+
+ if (DEBUG) printf("R: %s\n", chunk.memory);
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ return(status);
+}
+
+
+int
+drac3Logout (CURL *curl,
+ const char *host)
+{
+ char url[BUFLEN];
+ char rc[SBUFLEN];
+ int status;
+ struct Chunk chunk;
+
+ chunk.memory = NULL;
+ chunk.size = 0;
+ if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk)) return(1);
+
+ snprintf(url, BUFLEN, "https://%s/cgi/logout",
+ host);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url)) return(1);
+ if (curl_easy_perform(curl)) return(1);
+
+ if (DEBUG) printf("R: %s\n", chunk.memory);
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ return(status);
+}
+
+int
+drac3VerifyLogin (CURL *curl,
+ const char *host)
+{
+ /*We try to do a GetSysInfo */
+ return(drac3GetSysInfo (curl, host));
+}
+
+/* -------------------------------------------------------------------- */
+
diff --git a/lib/plugins/stonith/drac3_command.h b/lib/plugins/stonith/drac3_command.h
new file mode 100644
index 0000000..cd03e15
--- /dev/null
+++ b/lib/plugins/stonith/drac3_command.h
@@ -0,0 +1,29 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ *
+ * 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
+ *
+ */
+
+int drac3InitCurl (CURL *curl);
+int drac3Login (CURL *curl, const char *host, const char *user, const char *pass);
+int drac3PowerCycle (CURL *curl, const char *host);
+int drac3GetSysInfo (CURL *curl, const char *host);
+int drac3Logout (CURL *curl, const char *host);
+int drac3VerifyLogin (CURL *curl, const char *host);
+
diff --git a/lib/plugins/stonith/drac3_hash.c b/lib/plugins/stonith/drac3_hash.c
new file mode 100644
index 0000000..605a126
--- /dev/null
+++ b/lib/plugins/stonith/drac3_hash.c
@@ -0,0 +1,106 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ *
+ * 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>
+
+#include <string.h>
+#include <stdio.h>
+#include <clplumbing/base64.h>
+#include <clplumbing/md5.h>
+#include <glib.h>
+
+#include "drac3_hash.h"
+
+#define BUFLEN 1024 /* buffer */
+#define SBUFLEN 256 /* small buffer */
+#define MD5LEN 16 /* md5 buffer */
+
+/* Hash functions for DRAC3 authentication */
+
+guint16
+drac3Crc16(const char *str,
+ const int l) {
+
+ int i,j;
+ guint16 crc = 0;
+
+ for (i=0; i<l; i++) {
+ crc = crc ^ (str[i] << 8);
+ for (j=0; j<8; j++)
+ crc = ( (crc & 0x8000) == 32768 ? (crc<<1) ^ 0x1021 : crc<<1);
+ }
+ crc = crc & 0xFFFF;
+ return crc;
+}
+
+void
+drac3AuthHash(const char * chall,
+ const char * pass,
+ char * token,
+ int len) {
+
+ char * chall_dup;
+ char challBytes[MD5LEN];
+ char passMD5[MD5LEN];
+ char xorBytes[MD5LEN];
+ char xorBytesMD5[MD5LEN];
+ guint16 crc;
+ char response[MD5LEN+2];
+ char responseb64[SBUFLEN];
+ int i;
+
+ /* decodes chall -> challBytes */
+ memset(challBytes, 0, MD5LEN);
+ chall_dup = g_strdup(chall);
+ if (chall_dup[strlen(chall_dup) - 1] == '\n' ) {
+ chall_dup[strlen(chall_dup) - 1] = '\0';
+ }
+ base64_to_binary(chall_dup, strlen(chall_dup), challBytes, MD5LEN);
+
+ /* gets MD5 from pass -> passMD5 */
+ MD5((const unsigned char *)pass, strlen(pass), (unsigned char *)passMD5);
+
+ /* calculate challBytes and passMD5 xor -> xorBytes */
+ for (i=0; i<MD5LEN; i++) {
+ xorBytes[i] = challBytes[i] ^ passMD5[i];
+ }
+
+ /* calculate xorBytes MD5 -> xorBytesMD5 */
+ MD5((unsigned char *)xorBytes, MD5LEN, (unsigned char *)xorBytesMD5);
+
+ /* calculate xorBytesMD5 crc16 */
+ crc = drac3Crc16(xorBytesMD5, MD5LEN);
+
+ /* joins xorBytesMD5 and crc16 -> response */
+ memcpy(response, xorBytesMD5, MD5LEN);
+ memcpy(response+MD5LEN, &crc, 2);
+
+ /* calculate response base64 -> responseb64 */
+ memset(responseb64, 0, SBUFLEN);
+ binary_to_base64(response, MD5LEN+2, responseb64, SBUFLEN);
+
+ /* assuring null-termination */
+ responseb64[SBUFLEN-1]=0x00;
+
+ snprintf(token, len, "%s", responseb64);
+ token[len-1]=0x00;
+}
diff --git a/lib/plugins/stonith/drac3_hash.h b/lib/plugins/stonith/drac3_hash.h
new file mode 100644
index 0000000..fab2f58
--- /dev/null
+++ b/lib/plugins/stonith/drac3_hash.h
@@ -0,0 +1,28 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ *
+ * 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 <sys/types.h>
+#include <glib.h>
+
+guint16 drac3Crc16(const char *str, const int l);
+void drac3AuthHash(const char *chall, const char *pass, char *token, int len);
+
diff --git a/lib/plugins/stonith/external.c b/lib/plugins/stonith/external.c
new file mode 100644
index 0000000..da03665
--- /dev/null
+++ b/lib/plugins/stonith/external.c
@@ -0,0 +1,868 @@
+/*
+ * Stonith module for EXTERNAL Stonith device
+ *
+ * Copyright (c) 2001 SuSE Linux AG
+ * Portions Copyright (c) 2004, tummy.com, ltd.
+ *
+ * Based on ssh.c, Authors: Joachim Gleissner <jg@suse.de>,
+ * Lars Marowsky-Bree <lmb@suse.de>
+ * Modified for external.c: Scott Kleihege <scott@tummy.com>
+ * Reviewed, tested, and config parsing: Sean Reifschneider <jafo@tummy.com>
+ * And overhauled by Lars Marowsky-Bree <lmb@suse.de>, so the circle
+ * closes...
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ * Changed to allow full-featured external plugins by Dave Blaschke
+ * <debltc@us.ibm.com>
+ *
+ * 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>
+
+#include <dirent.h>
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN external
+#define PIL_PLUGIN_S "external"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+
+#include <pils/plugin.h>
+
+static StonithPlugin * external_new(const char *);
+static void external_destroy(StonithPlugin *);
+static int external_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * external_get_confignames(StonithPlugin *);
+static const char * external_getinfo(StonithPlugin * s, int InfoType);
+static int external_status(StonithPlugin * );
+static int external_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** external_hostlist(StonithPlugin *);
+
+static struct stonith_ops externalOps ={
+ external_new, /* Create new STONITH object */
+ external_destroy, /* Destroy STONITH object */
+ external_getinfo, /* Return STONITH info string */
+ external_get_confignames, /* Return STONITH info string */
+ external_set_config, /* Get configuration from NVpairs */
+ external_status, /* Return STONITH device status */
+ external_reset_req, /* Request a reset */
+ external_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &externalOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * EXTERNAL STONITH device
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ GHashTable * cmd_opts;
+ char * subplugin;
+ char ** confignames;
+ char * outputbuf;
+};
+
+static const char * pluginid = "ExternalDevice-Stonith";
+static const char * NOTpluginID = "External device has been destroyed";
+
+/* Prototypes */
+
+/* Run the command with op and return the exit status + the output
+ * (NULL -> discard output) */
+static int external_run_cmd(struct pluginDevice *sd, const char *op,
+ char **output);
+/* Just free up the configuration and the memory, if any */
+static void external_unconfig(struct pluginDevice *sd);
+
+static int
+external_status(StonithPlugin *s)
+{
+ struct pluginDevice * sd;
+ const char * op = "status";
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ rc = external_run_cmd(sd, op, NULL);
+ if (rc != 0) {
+ LOG(PIL_WARN, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ }
+ return rc;
+}
+
+static int
+get_num_tokens(char *str)
+{
+ int namecount = 0;
+
+ while (*str != EOS) {
+ str += strspn(str, WHITESPACE);
+ if (*str == EOS)
+ break;
+ str += strcspn(str, WHITESPACE);
+ namecount++;
+ }
+ return namecount;
+}
+
+static char **
+external_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+ const char * op = "gethosts";
+ int rc, i, namecount;
+ char ** ret;
+ char * output = NULL;
+ char * tmp;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(NULL);
+ }
+
+ rc = external_run_cmd(sd, op, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ FREE(output);
+ }
+ return NULL;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+
+ if (!output) {
+ LOG(PIL_CRIT, "%s: '%s %s' returned an empty hostlist",
+ __FUNCTION__, sd->subplugin, op);
+ return NULL;
+ }
+
+ namecount = get_num_tokens(output);
+ ret = MALLOC((namecount+1)*sizeof(char *));
+ if (!ret) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ FREE(output);
+ return NULL;
+ }
+ memset(ret, 0, (namecount+1)*sizeof(char *));
+
+ /* White-space split the output here */
+ i = 0;
+ tmp = strtok(output, WHITESPACE);
+ while (tmp != NULL) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s host %s",
+ __FUNCTION__, sd->subplugin, tmp);
+ }
+ ret[i] = STRDUP(tmp);
+ if (!ret[i]) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ FREE(output);
+ stonith_free_hostlist(ret);
+ return NULL;
+ }
+ i++;
+ tmp = strtok(NULL, WHITESPACE);
+ }
+
+ FREE(output);
+
+ if (i == 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' returned an empty hostlist",
+ __FUNCTION__, sd->subplugin, op);
+ stonith_free_hostlist(ret);
+ ret = NULL;
+ }
+
+ return(ret);
+}
+
+static int
+external_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice * sd;
+ const char * op;
+ int rc;
+ char * args1and2;
+ int argslen;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Host external-reset initiating on %s", host);
+ }
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ switch (request) {
+ case ST_GENERIC_RESET:
+ op = "reset";
+ break;
+
+ case ST_POWEROFF:
+ op = "off";
+ break;
+
+ case ST_POWERON:
+ op = "on";
+ break;
+
+ default:
+ LOG(PIL_CRIT, "%s: Unknown stonith request %d",
+ __FUNCTION__, request);
+ return S_OOPS;
+ break;
+ }
+
+ argslen = strlen(op) + strlen(host) + 2 /* 1 for blank, 1 for EOS */;
+ args1and2 = (char *)MALLOC(argslen);
+ if (args1and2 == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__);
+ return S_OOPS;
+ }
+ rc = snprintf(args1and2, argslen, "%s %s", op, host);
+ if (rc <= 0 || rc >= argslen) {
+ FREE(args1and2);
+ return S_OOPS;
+ }
+
+ rc = external_run_cmd(sd, args1and2, NULL);
+ FREE(args1and2);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' for host %s failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, host, rc);
+ return S_RESETFAIL;
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ return S_OK;
+ }
+
+}
+
+static int
+external_parse_config_info(struct pluginDevice* sd, StonithNVpair * info)
+{
+ char * key;
+ char * value;
+ StonithNVpair * nv;
+
+ sd->cmd_opts = g_hash_table_new(g_str_hash, g_str_equal);
+
+ /* TODO: Maybe treat "" as delimeters too so
+ * whitespace can be passed to the plugins... */
+ for (nv = info; nv->s_name; nv++) {
+ if (!nv->s_name || !nv->s_value) {
+ continue;
+ }
+
+ key = STRDUP(nv->s_name);
+ if (!key) {
+ goto err_mem;
+ }
+ value = STRDUP(nv->s_value);
+ if (!value) {
+ FREE(key);
+ goto err_mem;
+ }
+ g_hash_table_insert(sd->cmd_opts, key, value);
+ }
+
+ return(S_OK);
+
+err_mem:
+ LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__);
+ external_unconfig(sd);
+
+ return(S_OOPS);
+}
+
+static gboolean
+let_remove_eachitem(gpointer key, gpointer value, gpointer user_data)
+{
+ if (key) {
+ FREE(key);
+ }
+ if (value) {
+ FREE(value);
+ }
+ return TRUE;
+}
+
+static void
+external_unconfig(struct pluginDevice *sd) {
+ if (sd->cmd_opts) {
+ g_hash_table_foreach_remove(sd->cmd_opts,
+ let_remove_eachitem, NULL);
+ g_hash_table_destroy(sd->cmd_opts);
+ sd->cmd_opts = NULL;
+ }
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+external_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ struct pluginDevice * sd;
+ char ** p;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ /* make sure that command has not already been set */
+ if (s->isconfigured) {
+ return(S_OOPS);
+ }
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ if (sd->confignames == NULL) {
+ /* specified by name=value pairs, check required parms */
+ if (external_get_confignames(s) == NULL) {
+ return(S_OOPS);
+ }
+
+ for (p = sd->confignames; *p; p++) {
+ if (OurImports->GetValue(list, *p) == NULL) {
+ LOG(PIL_DEBUG, "Cannot get parameter %s from "
+ "StonithNVpair", *p);
+ }
+ }
+ }
+
+ return external_parse_config_info(sd, list);
+}
+
+
+/* Only interested in regular files that are also executable */
+static int
+exec_select(const struct dirent *dire)
+{
+ struct stat statf;
+ char filename[FILENAME_MAX];
+ int rc;
+
+ rc = snprintf(filename, FILENAME_MAX, "%s/%s",
+ STONITH_EXT_PLUGINDIR, dire->d_name);
+ if (rc <= 0 || rc >= FILENAME_MAX) {
+ return 0;
+ }
+
+ if ((stat(filename, &statf) == 0) &&
+ (S_ISREG(statf.st_mode)) &&
+ (statf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) {
+ if (statf.st_mode & (S_IWGRP|S_IWOTH)) {
+ LOG(PIL_WARN, "Executable file %s ignored "
+ "(writable by group/others)", filename);
+ return 0;
+ }else{
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+external_get_confignames(StonithPlugin* p)
+{
+ struct pluginDevice * sd;
+ const char * op = "getconfignames";
+ int i, rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ sd = (struct pluginDevice *)p;
+
+ if (sd->subplugin != NULL) {
+ /* return list of subplugin's required parameters */
+ char *output = NULL, *pch;
+ int namecount;
+
+ rc = external_run_cmd(sd, op, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ FREE(output);
+ }
+ return NULL;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_DEBUG, "plugin output: %s", output);
+ }
+ }
+
+ namecount = get_num_tokens(output);
+ sd->confignames = (char **)MALLOC((namecount+1)*sizeof(char *));
+ if (sd->confignames == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ if (output) { FREE(output); }
+ return NULL;
+ }
+
+ /* now copy over confignames */
+ pch = strtok(output, WHITESPACE);
+ for (i = 0; i < namecount; i++) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s configname %s",
+ __FUNCTION__, sd->subplugin, pch);
+ }
+ sd->confignames[i] = STRDUP(pch);
+ pch = strtok(NULL, WHITESPACE);
+ }
+ FREE(output);
+ sd->confignames[namecount] = NULL;
+ }else{
+ /* return list of subplugins in external directory */
+ struct dirent ** files = NULL;
+ int dircount;
+
+ /* get the external plugin's confignames (list of subplugins) */
+ dircount = scandir(STONITH_EXT_PLUGINDIR, &files,
+ SCANSEL_CAST exec_select, NULL);
+ if (dircount < 0) {
+ return NULL;
+ }
+
+ sd->confignames = (char **)MALLOC((dircount+1)*sizeof(char *));
+ if (!sd->confignames) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return NULL;
+ }
+
+ for (i = 0; i < dircount; i++) {
+ sd->confignames[i] = STRDUP(files[i]->d_name);
+ free(files[i]);
+ files[i] = NULL;
+ }
+ free(files);
+ sd->confignames[dircount] = NULL;
+ }
+
+ return (const char * const *)sd->confignames;
+}
+
+/*
+ * Return STONITH info string
+ */
+static const char *
+external_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* sd;
+ char * output = NULL;
+ const char * op;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+ sd = (struct pluginDevice *)s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(NULL);
+ }
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ op = "getinfo-devid";
+ break;
+
+ case ST_DEVICENAME:
+ op = "getinfo-devname";
+ break;
+
+ case ST_DEVICEDESCR:
+ op = "getinfo-devdescr";
+ break;
+
+ case ST_DEVICEURL:
+ op = "getinfo-devurl";
+ break;
+
+ case ST_CONF_XML:
+ op = "getinfo-xml";
+ break;
+
+ default:
+ return NULL;
+ }
+
+ rc = external_run_cmd(sd, op, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ FREE(output);
+ }
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ if (sd->outputbuf != NULL) {
+ FREE(sd->outputbuf);
+ }
+ sd->outputbuf = output;
+ return(output);
+ }
+ return(NULL);
+}
+
+/*
+ * EXTERNAL Stonith destructor...
+ */
+static void
+external_destroy(StonithPlugin *s)
+{
+ struct pluginDevice * sd;
+ char ** p;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd = (struct pluginDevice *)s;
+
+ sd->pluginid = NOTpluginID;
+ external_unconfig(sd);
+ if (sd->confignames != NULL) {
+ for (p = sd->confignames; *p; p++) {
+ FREE(*p);
+ }
+ FREE(sd->confignames);
+ sd->confignames = NULL;
+ }
+ if (sd->subplugin != NULL) {
+ FREE(sd->subplugin);
+ sd->subplugin = NULL;
+ }
+ if (sd->outputbuf != NULL) {
+ FREE(sd->outputbuf);
+ sd->outputbuf = NULL;
+ }
+ FREE(sd);
+}
+
+/* Create a new external Stonith device */
+static StonithPlugin *
+external_new(const char *subplugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ if (subplugin != NULL) {
+ sd->subplugin = STRDUP(subplugin);
+ if (sd->subplugin == NULL) {
+ FREE(sd);
+ return(NULL);
+ }
+ }
+ sd->sp.s_ops = &externalOps;
+ return &(sd->sp);
+}
+
+static void
+ext_add_to_env(gpointer key, gpointer value, gpointer user_data)
+{
+ if (setenv((char *)key, (char *)value, 1) != 0) {
+ LOG(PIL_CRIT, "%s: setenv failed.", __FUNCTION__);
+ }
+}
+
+static void
+ext_del_from_env(gpointer key, gpointer value, gpointer user_data)
+{
+ unsetenv((char *)key);
+}
+
+#define LOGTAG_VAR "HA_LOGTAG"
+
+/* Run the command with op as command line argument(s) and return the exit
+ * status + the output */
+static int
+external_run_cmd(struct pluginDevice *sd, const char *op, char **output)
+{
+ const int BUFF_LEN=4096;
+ char buff[BUFF_LEN];
+ int read_len = 0;
+ int status, rc;
+ char * data = NULL;
+ FILE * file;
+ char cmd[FILENAME_MAX+64];
+ struct stat buf;
+ int slen;
+ char *path, *new_path, *logtag, *savevar = NULL;
+ int new_path_len, logtag_len;
+ gboolean nodata;
+
+ rc = snprintf(cmd, FILENAME_MAX, "%s/%s",
+ STONITH_EXT_PLUGINDIR, sd->subplugin);
+ if (rc <= 0 || rc >= FILENAME_MAX) {
+ LOG(PIL_CRIT, "%s: external command too long.", __FUNCTION__);
+ return -1;
+ }
+
+ if (stat(cmd, &buf) != 0) {
+ LOG(PIL_CRIT, "%s: stat(2) of %s failed: %s",
+ __FUNCTION__, cmd, strerror(errno));
+ return -1;
+ }
+
+ if (!S_ISREG(buf.st_mode)
+ || (!(buf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) {
+ LOG(PIL_CRIT, "%s: %s found NOT to be executable.",
+ __FUNCTION__, cmd);
+ return -1;
+ }
+
+ if (buf.st_mode & (S_IWGRP|S_IWOTH)) {
+ LOG(PIL_CRIT, "%s: %s found to be writable by group/others, "
+ "NOT executing for security purposes.",
+ __FUNCTION__, cmd);
+ return -1;
+ }
+
+ strcat(cmd, " ");
+ strcat(cmd, op);
+
+ /* We only have a global environment to use here. So we add our
+ * options to it, and then later remove them again. */
+ if (sd->cmd_opts) {
+ g_hash_table_foreach(sd->cmd_opts, ext_add_to_env, NULL);
+ }
+
+ /* external plugins need path to ha_log.sh */
+ path = getenv("PATH");
+ if (strncmp(GLUE_SHARED_DIR,path,strlen(GLUE_SHARED_DIR))) {
+ new_path_len = strlen(path)+strlen(GLUE_SHARED_DIR)+2;
+ new_path = (char *)g_malloc(new_path_len);
+ snprintf(new_path, new_path_len, "%s:%s", GLUE_SHARED_DIR, path);
+ setenv("PATH", new_path, 1);
+ g_free(new_path);
+ }
+
+ /* set the logtag appropriately */
+ logtag_len = strlen(PIL_PLUGIN_S)+strlen(sd->subplugin)+2;
+ logtag = (char *)g_malloc(logtag_len);
+ snprintf(logtag, logtag_len, "%s/%s", PIL_PLUGIN_S, sd->subplugin);
+ if (getenv(LOGTAG_VAR)) {
+ savevar = g_strdup(getenv(LOGTAG_VAR));
+ }
+ setenv(LOGTAG_VAR, logtag, 1);
+ g_free(logtag);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: Calling '%s'", __FUNCTION__, cmd );
+ }
+ file = popen(cmd, "r");
+ if (NULL==file) {
+ LOG(PIL_CRIT, "%s: Calling '%s' failed",
+ __FUNCTION__, cmd);
+ rc = -1;
+ goto out;
+ }
+
+ if (output) {
+ slen=0;
+ data = MALLOC(1);
+ data[slen] = EOS;
+ }
+ while (!feof(file)) {
+ nodata = TRUE;
+ if (output) {
+ read_len = fread(buff, 1, BUFF_LEN, file);
+ if (read_len > 0) {
+ data = REALLOC(data, slen+read_len+1);
+ if (data == NULL) {
+ break;
+ }
+ memcpy(data + slen, buff, read_len);
+ slen += read_len;
+ data[slen] = EOS;
+ nodata = FALSE;
+ }
+ } else {
+ if (fgets(buff, BUFF_LEN, file)) {
+ LOG(PIL_INFO, "%s: '%s' output: %s", __FUNCTION__, cmd, buff);
+ nodata = FALSE;
+ }
+ }
+ if (nodata) {
+ sleep(1);
+ }
+ }
+ if (output && !data) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ rc = -1;
+ goto out;
+ }
+
+ status = pclose(file);
+ if (WIFEXITED(status)) {
+ rc = WEXITSTATUS(status);
+ if (rc != 0 && Debug) {
+ LOG(PIL_DEBUG,
+ "%s: Calling '%s' returned %d", __FUNCTION__, cmd, rc);
+ }
+ } else {
+ if (WIFSIGNALED(status)) {
+ LOG(PIL_CRIT, "%s: '%s' got signal %d",
+ __FUNCTION__, cmd, WTERMSIG(status));
+ } else if (WIFSTOPPED(status)) {
+ LOG(PIL_INFO, "%s: '%s' stopped with signal %d",
+ __FUNCTION__, cmd, WSTOPSIG(status));
+ } else {
+ LOG(PIL_CRIT, "%s: '%s' exited abnormally (core dumped?)",
+ __FUNCTION__, cmd);
+ }
+ rc = -1;
+ }
+ if (Debug && output && data) {
+ LOG(PIL_DEBUG, "%s: '%s' output: %s", __FUNCTION__, cmd, data);
+ }
+
+out:
+ if (savevar) {
+ setenv(LOGTAG_VAR, savevar, 1);
+ g_free(savevar);
+ } else {
+ unsetenv(LOGTAG_VAR);
+ }
+ if (sd->cmd_opts) {
+ g_hash_table_foreach(sd->cmd_opts, ext_del_from_env, NULL);
+ }
+ if (!rc) {
+ if (output) {
+ *output = data;
+ }
+ } else {
+ if (data) {
+ FREE(data);
+ }
+ if (output) {
+ *output = NULL;
+ }
+ }
+ return rc;
+}
diff --git a/lib/plugins/stonith/external/Makefile.am b/lib/plugins/stonith/external/Makefile.am
new file mode 100644
index 0000000..42e0046
--- /dev/null
+++ b/lib/plugins/stonith/external/Makefile.am
@@ -0,0 +1,33 @@
+# Makefile.am for OCF RAs
+#
+# Author: Sun Jing Dong
+# Copyright (C) 2004 IBM
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+EXTRA_DIST = drac5 dracmc-telnet ibmrsa-telnet ipmi rackpdu vmware vcenter xen0 \
+ xen0-ha-dom0-stonith-helper kdumpcheck nut
+
+extdir = $(stonith_ext_plugindir)
+
+helperdir = $(stonith_plugindir)
+
+ext_SCRIPTS = drac5 dracmc-telnet ibmrsa ibmrsa-telnet ipmi riloe ssh vmware vcenter rackpdu xen0 hmchttp \
+ xen0-ha kdumpcheck ippower9258 nut libvirt \
+ hetzner
+
+helper_SCRIPTS = xen0-ha-dom0-stonith-helper
diff --git a/lib/plugins/stonith/external/drac5.in b/lib/plugins/stonith/external/drac5.in
new file mode 100644
index 0000000..218cbd3
--- /dev/null
+++ b/lib/plugins/stonith/external/drac5.in
@@ -0,0 +1,113 @@
+#!/bin/sh
+#
+# External STONITH module for DRAC5 adapters.
+#
+# Author: Jun Wang
+# License: GNU General Public License (GPL)
+#
+
+trap 'if [ -n "$outf" ]; then ha_log.sh err "`cat $outf`"; rm -f "$outf"; fi' 0
+outf=`mktemp` || {
+ ha_log.sh err "mktemp failed"
+ exit 1
+}
+
+sshlogin() {
+ if [ x = "x$ipaddr" -o x = "x$userid" ]
+ then
+ ha_log.sh err "ipaddr or userid missing; check configuration"
+ return 1
+ fi
+ @SSH@ -q -x -n $userid@$ipaddr racadm serveraction "$1" >$outf 2>&1
+}
+
+drac_reset() {
+ sshlogin hardreset
+}
+
+drac_on() {
+ sshlogin poweron
+}
+
+drac_off() {
+ sshlogin poweroff
+}
+
+drac_status() {
+ sshlogin powerstatus
+}
+
+case $1 in
+gethosts)
+ echo $hostname
+ ;;
+on)
+ drac_poweron
+ ;;
+off)
+ drac_poweroff
+ ;;
+reset)
+ drac_reset
+ ;;
+status)
+ drac_status
+ ;;
+getconfignames)
+ for i in hostname ipaddr userid; do
+ echo $i
+ done
+ ;;
+getinfo-devid)
+ echo "DRAC5 STONITH device"
+ ;;
+getinfo-devname)
+ echo "DRAC5 STONITH device"
+ ;;
+getinfo-devdescr)
+ echo "DRAC5 host reset/poweron/poweroff"
+ ;;
+getinfo-devurl)
+ echo "http://www.dell.com"
+ ;;
+getinfo-xml)
+ cat <<EOF
+<parameters>
+
+<parameter name="hostname" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostname
+</shortdesc>
+<longdesc lang="en">
+The hostname of the host to be managed by this STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="ipaddr" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+IP Address
+</shortdesc>
+<longdesc lang="en">
+The IP address of the STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="userid" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+Login
+</shortdesc>
+<longdesc lang="en">
+The username used for logging in to the STONITH device
+</longdesc>
+</parameter>
+
+</parameters>
+EOF
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/dracmc-telnet b/lib/plugins/stonith/external/dracmc-telnet
new file mode 100644
index 0000000..d993961
--- /dev/null
+++ b/lib/plugins/stonith/external/dracmc-telnet
@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+# vim: set filetype=python
+#######################################################################
+#
+# dracmc-telnet - External stonith plugin for HAv2 (http://linux-ha.org/wiki)
+# Connects to Dell Drac/MC Blade Enclosure via a Cyclades
+# terminal server with telnet and switches power of named
+# blade servers appropriatelly.
+#
+# Required parameters:
+# nodename: The name of the server you want to touch on your network
+# cyclades_ip: The IP address of the cyclades terminal server
+# cyclades_port: The port for telnet to access on the cyclades (i.e. 7032)
+# servername: The DRAC/MC server name of the blade (i.e. Server-7)
+# username: The login user name for the DRAC/MC
+# password: The login password for the DRAC/MC
+#
+# Author: Alex Tsariounov <alext@novell.com>
+#
+# Based on ibmrsa-telnet external stonith plugin by Andreas Mock
+# (andreas.mock@web.de), Copyright by Adreas Mock and released as part
+# of HAv2.
+#
+# History:
+# 2009-10-12 First release.
+#
+# Copyright (c) 2009 Novell, Inc.
+# All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 or later of the GNU General Public
+# License as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+#######################################################################
+import sys
+import os
+import time
+import telnetlib
+import random
+import subprocess
+
+LOGINRETRIES = 10
+
+class TimeoutException(Exception):
+ def __init__(self, value=None):
+ Exception.__init__(self)
+ self.value = value
+
+ def __str__(self):
+ return repr(self.value)
+
+class DracMC(telnetlib.Telnet):
+ def __init__(self, *args, **kwargs):
+ telnetlib.Telnet.__init__(self)
+ self._timeout = 4
+ self._loggedin = 0
+ self._history = []
+ self._appl = os.path.basename(sys.argv[0])
+ self._server = args[0]
+
+ def _get_timestamp(self):
+ ct = time.time()
+ msecs = (ct - long(ct)) * 1000
+ return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(ct)), msecs)
+
+ def write(self, buffer):
+ self._history.append(self._get_timestamp() + ': WRITE: ' + repr(buffer))
+ telnetlib.Telnet.write(self, buffer)
+
+ def read_until(self, what, timeout=2):
+ line = telnetlib.Telnet.read_until(self, what, timeout)
+ self._history.append(self._get_timestamp() + ': READ : ' + repr(line))
+ if not line.endswith(what):
+ raise TimeoutException("Timeout while waiting for '%s'." % (what, ))
+ return line
+
+ def login(self, user, passwd):
+ time.sleep(0.3)
+ try:
+ line = self.read_until('Login: ', self._timeout)
+ self.write(user)
+ self.write('\r')
+ line = self.read_until('Password: ', self._timeout)
+ self.write(passwd)
+ self.write('\r')
+ except:
+ self.write("\r")
+ line = self.read_until('Login: ', self._timeout)
+ self.write(user)
+ self.write('\r')
+ line = self.read_until('Password: ', self._timeout)
+ self.write(passwd)
+ self.write('\r')
+ try:
+ line = self.read_until('DRAC/MC:', self._timeout)
+ except:
+ self.write("\r")
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def hardreset(self):
+ self.write('serveraction -s %s hardreset\r' % self._server)
+ line = self.read_until('OK', 10)
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def powercycle(self):
+ self.write('serveraction -s %s powercycle\r' % self._server)
+ line = self.read_until('OK', 10)
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def on(self):
+ self.write('serveraction -s %s powerup\r' % self._server)
+ line = self.read_until('OK', 10)
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def off(self):
+ self.write('serveraction -s %s powerdown\r' % self._server)
+ line = self.read_until('OK', 10)
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def exit(self):
+ self.write('exit\r')
+
+ def get_history(self):
+ return "\n".join(self._history)
+
+
+class DracMCStonithPlugin:
+ def __init__(self):
+ # define the external stonith plugin api
+ self._required_cmds = \
+ 'reset gethosts status getconfignames getinfo-devid ' \
+ 'getinfo-devname getinfo-devdescr getinfo-devurl ' \
+ 'getinfo-xml'
+ self._optional_cmds = 'on off'
+ self._required_cmds_list = self._required_cmds.split()
+ self._optional_cmds_list = self._optional_cmds.split()
+
+ # who am i
+ self._appl = os.path.basename(sys.argv[0])
+
+ # telnet connection object
+ self._connection = None
+
+ # the list of configuration names
+ self._confignames = ['nodename', 'cyclades_ip', 'cyclades_port',
+ 'servername', 'username', 'password']
+
+ # catch the parameters provided by environment
+ self._parameters = {}
+ for name in self._confignames:
+ try:
+ self._parameters[name] = os.environ.get(name, '').split()[0]
+ except IndexError:
+ self._parameters[name] = ''
+
+ def _get_timestamp(self):
+ ct = time.time()
+ msecs = (ct - long(ct)) * 1000
+ return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(ct)), msecs)
+
+ def _echo_debug(self, *args):
+ subprocess.call("ha_log.sh debug '%s'" % ' '.join(args), shell=True)
+
+ def echo(self, *args):
+ what = ''.join([str(x) for x in args])
+ sys.stdout.write(what)
+ sys.stdout.write('\n')
+ sys.stdout.flush()
+ self._echo_debug("STDOUT:", what)
+
+ def echo_log(self, level, *args):
+ subprocess.call("ha_log.sh %s '%s'" % (level,' '.join(args)), shell=True)
+
+ def _get_connection(self):
+ if not self._connection:
+ c = DracMC(self._parameters['servername'])
+ self._echo_debug("Connecting to '%s:%s'" %
+ (self._parameters['cyclades_ip'],
+ self._parameters['cyclades_port']))
+ tries = 0
+ while tries < LOGINRETRIES:
+ try:
+ c.open(self._parameters['cyclades_ip'],
+ self._parameters['cyclades_port'])
+ c.login(self._parameters['username'],
+ self._parameters['password'])
+ except Exception, args:
+ if "Connection reset by peer" in str(args):
+ self._echo_debug("Someone is already logged in... retry=%s" % tries)
+ c.close()
+ time.sleep(random.uniform(1.0, 5.0))
+ else:
+ raise
+ else:
+ break
+ tries += 1
+
+ if tries == LOGINRETRIES:
+ c.close()
+ raise Exception("Could not log in to %s:%s" %
+ (self._parameters['cyclades_ip'],
+ self._parameters['cyclades_port']))
+ self._connection = c
+
+ def _end_connection(self):
+ if self._connection:
+ self._connection.exit()
+ self._connection.close()
+
+ def reset(self):
+ self._get_connection()
+ # self._connection.hardreset()
+ self._connection.powercycle()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Reset of node '%s' done" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def on(self):
+ self._get_connection()
+ self._connection.on()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Switched node '%s' ON" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def off(self):
+ self._get_connection()
+ self._connection.off()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Switched node '%s' OFF" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def gethosts(self):
+ self.echo(self._parameters['nodename'])
+ return(0)
+
+ def status(self):
+ self._get_connection()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ return(0)
+
+ def getconfignames(self):
+ for name in ['nodename', 'cyclades_ip', 'cyclades_port', 'servername',
+ 'username', 'password']:
+ self.echo(name)
+ return(0)
+
+ def getinfo_devid(self):
+ self.echo("External Stonith Plugin for Dell DRAC/MC via Cyclades")
+ return(0)
+
+ def getinfo_devname(self):
+ self.echo("External Stonith Plugin for Dell Drac/MC connecting "
+ "via Telnet to a Cyclades port")
+ return(0)
+
+ def getinfo_devdescr(self):
+ self.echo("External stonith plugin for HAv2 which connects to "
+ "a Dell DRAC/MC connected via a Cyclades port with telnet. "
+ "Commands to turn on/off power and to reset server are sent "
+ "appropriately. "
+ "(c) 2009 by Novell, Inc. (alext@novell.com)")
+ return(0)
+
+ def getinfo_devurl(self):
+ self.echo("http://support.dell.com/support/edocs/software/smdrac3/dracmc/1.3/en/index.htm")
+
+ def getinfo_xml(self):
+ info = """<parameters>
+ <parameter name="nodename" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">nodename to shoot</shortdesc>
+ <longdesc lang="en">
+ Name of the node to be stonithed.
+ </longdesc>
+ </parameter>
+ <parameter name="cyclades_ip" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">hostname or ip address of cyclades</shortdesc>
+ <longdesc lang="en">
+ Hostname or IP address of Cyclades connected to DRAC/MC.
+ </longdesc>
+ </parameter>
+ <parameter name="cyclades_port" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">telnet port to use on cyclades</shortdesc>
+ <longdesc lang="en">
+ Port used with the Cyclades telnet interface which is connected to the DRAC/MC.
+ </longdesc>
+ </parameter>
+ <parameter name="servername" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">DRAC/MC name of blade to be stonithed</shortdesc>
+ <longdesc lang="en">
+ Name of server blade to be stonithed on the DRAC/MC (example: Server-7)
+ </longdesc>
+ </parameter>
+ <parameter name="username" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">username to login on the DRAC/MC</shortdesc>
+ <longdesc lang="en">
+ Username to login to the DRAC/MC once connected via the Cyclades port.
+ </longdesc>
+ </parameter>
+ <parameter name="password" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">password to login on the DRAC/MC</shortdesc>
+ <longdesc lang="en">
+ Password to login to the DRAC/MC once connected via the Cyclades port.
+ </longdesc>
+ </parameter>
+ </parameters>
+ """
+ self.echo(info)
+ return(0)
+
+ def not_implemented(self, cmd):
+ self.echo_log("err", "Command '%s' not implemented." % (cmd,))
+ return(1)
+
+ def usage(self):
+ usage = "Call me with one of the allowed commands: %s, %s" % (
+ ', '.join(self._required_cmds_list),
+ ', '.join(self._optional_cmds_list))
+ return usage
+
+ def process(self, argv):
+ self._echo_debug("========== Start =============")
+ if len(argv) < 1:
+ self.echo_log("err", 'At least one commandline argument required.')
+ return(1)
+ cmd = argv[0]
+ self._echo_debug("cmd:", cmd)
+ if cmd not in self._required_cmds_list and \
+ cmd not in self._optional_cmds_list:
+ self.echo_log("err", "Command '%s' not supported." % (cmd,))
+ return(1)
+ try:
+ cmd = cmd.lower().replace('-', '_')
+ func = getattr(self, cmd, self.not_implemented)
+ rc = func()
+ return(rc)
+ except Exception, args:
+ self.echo_log("err", 'Exception raised:', str(args))
+ if self._connection:
+ self.echo_log("err", self._connection.get_history())
+ self._connection.close()
+ return(1)
+
+
+if __name__ == '__main__':
+ stonith = DracMCStonithPlugin()
+ rc = stonith.process(sys.argv[1:])
+ sys.exit(rc)
diff --git a/lib/plugins/stonith/external/hetzner b/lib/plugins/stonith/external/hetzner
new file mode 100755
index 0000000..2b3e675
--- /dev/null
+++ b/lib/plugins/stonith/external/hetzner
@@ -0,0 +1,139 @@
+#!/bin/sh
+#
+# External STONITH module for Hetzner.
+#
+# Copyright (c) 2011 MMUL S.a.S. - Raoul Scarazzini <rasca@mmul.it>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+# Read parameters from config file, format is based upon the hetzner OCF resource agent
+# developed by Kumina: http://blog.kumina.nl/2011/02/hetzner-failover-ip-ocf-script/
+conf_file="/etc/hetzner.cfg"
+
+case $1 in
+ get*) ;; # don't print errors if conf_file not present
+ *)
+ user=`sed -n 's/^user.*=\ *//p' $conf_file`
+ pass=`sed -n 's/^pass.*=\ *//p' $conf_file`
+ ;;
+esac
+
+hetzner_server="https://robot-ws.your-server.de"
+
+check_http_response() {
+ # If the response is 200 then return 0
+ if [ $1 = 200 ]
+ then
+ return 0
+ else
+ # If the response is not 200 then display a description of the problem and return 1
+ case $1 in
+ 400) ha_log.sh err "INVALID_INPUT - Invalid input parameters"
+ ;;
+ 404) ha_log.sh err "SERVER_NOT_FOUND - Server with ip $remote_ip not found"
+ ;;
+ 409) ha_log.sh err "RESET_MANUAL_ACTIVE - There is already a running manual reset"
+ ;;
+ 500) ha_log.sh err "RESET_FAILED - Resetting failed due to an internal error"
+ ;;
+ esac
+ return 1
+ fi
+}
+
+case $1 in
+gethosts)
+ echo $hostname
+ exit 0
+ ;;
+on)
+ # Can't really be implemented because Hetzner's webservice cannot power on a system
+ ha_log.sh err "Power on is not available since Hetzner's webservice can't do this operation."
+ exit 1
+ ;;
+off)
+ # Can't really be implemented because Hetzner's webservice cannot power on a system
+ ha_log.sh err "Power off is not available since Hetzner's webservice can't do this operation."
+ exit 1
+ ;;
+reset)
+ # Launching the reset action via webservice
+ check_http_response $(curl --silent -o /dev/null -w '%{http_code}' -u $user:$pass $hetzner_server/reset/$remote_ip -d type=hw)
+ exit $?
+ ;;
+status)
+ # Check if we can contact the webservice
+ check_http_response "$(curl --silent -o /dev/null -w '%{http_code}' -u $user:$pass $hetzner_server/server/$remote_ip)"
+ exit $?
+ ;;
+getconfignames)
+ echo "hostname"
+ echo "remote_ip"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "Hetzner STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "Hetzner STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "Hetzner host reset"
+ echo "Manages the remote webservice for reset a remote server."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://wiki.hetzner.de/index.php/Robot_Webservice_en"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << HETZNERXML
+<parameters>
+<parameter name="hostname" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostname
+</shortdesc>
+<longdesc lang="en">
+The name of the host to be managed by this STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="remote_ip" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Remote IP
+</shortdesc>
+<longdesc lang="en">
+The address of the remote IP that manages this server.
+</longdesc>
+</parameter>
+</parameters>
+HETZNERXML
+ exit 0
+ ;;
+*)
+ ha_log.sh err "Don't know what to do for '$remote_ip'"
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/hmchttp b/lib/plugins/stonith/external/hmchttp
new file mode 100644
index 0000000..9d111bc
--- /dev/null
+++ b/lib/plugins/stonith/external/hmchttp
@@ -0,0 +1,218 @@
+#!/bin/sh
+# External STONITH module for HMC web console
+#
+# Copyright (c) 2007 Xinwei Hu <hxinwei@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+#set -x
+hostlist=`echo $hostlist | tr ',' ' '`
+
+trap '[ ! -e "$COOKIEFILE" ] || rm -f "$COOKIEFILE"' 0
+COOKIEFILE=`mktemp` || exit 1
+
+: ${CURLBIN="/usr/bin/curl"}
+: ${user=admin}
+: ${password=admin}
+
+check_parameter() {
+ if [ ! -x $CURLBIN ]
+ then
+ ha_log.sh err "Curl can't be found in normal place. Set CURLBIN to override the default value"
+ exit 1
+ fi
+
+ if [ -z $hmc_ipaddr ]
+ then
+ ha_log.sh err "The address of HMC web console is not specified"
+ exit 1
+ fi
+}
+
+HMCUSERNAME=$user
+HMCPASSWORD=$password
+
+HMC_LOGIN_COMMAND="$CURLBIN -3 -k -c $COOKIEFILE -d user=$HMCUSERNAME -d password=$HMCPASSWORD -d lang=0 -d submit=Log+in "
+HMC_LOGOUT_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d submit=Log+out "
+HMC_TOC_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=2 "
+HMC_POWERON_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=60 -d sp=255 -d is=0 -d om=4 -d id=1 -d ip=2 -d plt=1 -d pm=0 -d on=Save+settings+and+power+on "
+HMC_POWERSTATE_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=60 "
+HMC_POWEROFF_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=30 -d submit=Continue "
+HMC_REBOOT_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=74 -d submit=Continue "
+
+hmc_login() {
+ iamin=0
+ while [ $iamin -eq 0 ]; do
+ $HMC_LOGIN_COMMAND https://$hmc_ipaddr/cgi-bin/cgi >/dev/null 2>&1
+ $HMC_TOC_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null | grep -q "Too many users"
+ iamin=$?
+ sleep 2
+ done
+}
+hmc_logout() {
+ $HMC_LOGOUT_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null
+}
+
+hmc_reboot() {
+ check_parameter
+ $HMC_REBOOT_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null
+}
+hmc_poweron() {
+ r=1
+ while [ 0 -ne $r ]; do
+ $HMC_POWERON_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null | grep -q "Operation completed successfully"
+ r=$?
+ done
+}
+hmc_poweroff() {
+ check_parameter
+ $HMC_POWEROFF_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null
+}
+hmc_powerstate() {
+ check_parameter
+ r=`$HMC_POWERSTATE_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null| grep "Current system power state:" | sed 's/<br>//g' | awk '{print $5}'`
+ echo $r
+}
+
+hmc_poweroffon() {
+ check_parameter
+ hmc_poweroff
+ while [ 1 ]; do
+ r=`hmc_powerstate`
+ ha_log.sh debug "power state: $r"
+ if [ $r = "Off" ]; then
+ break
+ fi
+ sleep 5
+ done
+ sleep 3
+ hmc_poweron
+}
+
+case $1 in
+gethosts)
+ for h in $hostlist; do
+ echo $h
+ done
+ exit 0
+ ;;
+status)
+ if
+ ping -w1 -c1 "$hmc_ipaddr" 2>&1
+ then
+ exit 0
+ fi
+ exit 1
+ ;;
+getconfignames)
+ for f in hostlist hmc_ipaddr user password; do
+ echo $f
+ done
+ exit 0
+ ;;
+getinfo-devid)
+ echo "HMC web console STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "HMC web console STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "HMC web console based host power control"
+ echo "Use for i5, p5, pSeries and OpenPower systems that are managed via "
+ echo "web console through a direct connection to system's HMC port."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://www.ibm.com"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << HMCXML
+<parameters>
+
+<parameter name="hostlist" unique="1" required="1">
+<content type="string"/>
+<shortdesc lang="en">Hostlist</shortdesc>
+<longdesc lang="en">
+The list of hosts that the STONITH device controls
+</longdesc>
+</parameter>
+
+<parameter name="hmc_ipaddr" unique="1" required="1">
+<content type="string"/>
+<shortdesc lang="en">HMC IPAddr</shortdesc>
+<longdesc lang="en">
+The IP address of the HMC web console
+</longdesc>
+</parameter>
+
+<parameter name="user" unique="1" required="1">
+<content type="string"/>
+<shortdesc lang="en">User</shortdesc>
+<longdesc lang="en">
+User name to log into HMC web console
+</longdesc>
+</parameter>
+
+<parameter name="password" unique="1" required="1">
+<content type="string"/>
+<shortdesc lang="en">Password</shortdesc>
+<longdesc lang="en">
+The password of user name to log into HMC web console
+</longdesc>
+</parameter>
+
+</parameters>
+HMCXML
+ exit 0
+ ;;
+esac
+
+case $1 in
+on|off|reset|powerstate|poweroffon)
+ hmc_login
+ case $1 in
+ on)
+ hmc_poweron $hmc_ipaddr
+ ;;
+ off)
+ hmc_poweroff $hmc_ipaddr
+ ;;
+ reset)
+# hmc_reboot $hmc_ipaddr
+ hmc_poweroffon $hmc_ipaddr
+ ;;
+ powerstate)
+ hmc_powerstate
+ ;;
+ poweroffon)
+ hmc_poweroffon
+ ;;
+ esac
+ hmc_logout
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/ibmrsa b/lib/plugins/stonith/external/ibmrsa
new file mode 100644
index 0000000..7408465
--- /dev/null
+++ b/lib/plugins/stonith/external/ibmrsa
@@ -0,0 +1,157 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Dejan Muhamedagic <dmuhamedagic@at.ibm.com>, IBM Austria
+#
+# External STONITH module for IBM RSA adapters.
+# External STONITH module for IBM BMC.
+# This STONITH module depends on IBMmpcli.
+#
+
+trap 'rm -f "$outf"' 0
+outf=`mktemp` || {
+ ha_log.sh err 'mktemp failed'
+ exit 1
+}
+
+chkmpcli() {
+ test -x /opt/IBMmpcli/bin/MPCLI.sh
+}
+mpcli() {
+ chkmpcli || {
+ ha_log.sh err "IBM mpcli not installed"
+ return 1
+ }
+ if [ x = "x$ipaddr" -o x = "x$userid" -o x = "x$passwd" ]
+ then
+ ha_log.sh err "ipaddr, userid, or passwd missing; check configuration"
+ return 1
+ fi
+ type=${type:-"ibm"}
+
+ goodstg="SUCCESS"
+ failstg="FAILURE"
+ (
+ echo "logonip -h $ipaddr -u $userid -p $passwd -t $type"
+ echo "outputfile $outf"
+ cat
+ ) | /opt/IBMmpcli/bin/MPCLI.sh | grep -w $goodstg >/dev/null 2>&1
+ rc=$?
+ grep -w $failstg $outf >/dev/null
+ if [ $rc -eq 0 -a $? -eq 1 ]; then
+ return 0
+ else
+ ha_log.sh err "MPCLI.sh failed: `cat $outf`"
+ return 1
+ fi
+}
+ibmrsa_reboot() {
+ echo restart -now | mpcli
+}
+ibmrsa_poweron() {
+ echo poweron | mpcli
+}
+ibmrsa_poweroff() {
+ echo poweroff | mpcli
+}
+ibmrsa_status() {
+ echo | mpcli
+}
+
+hostname=`echo ${hostname} | tr ',' ' '`
+
+case $1 in
+gethosts)
+ echo $hostname
+ ;;
+on)
+ ibmrsa_poweron
+ ;;
+off)
+ ibmrsa_poweroff
+ ;;
+reset)
+ ibmrsa_reboot
+ ;;
+status)
+ ibmrsa_status
+ ;;
+getconfignames)
+ for i in hostname ipaddr userid passwd type; do
+ echo $i
+ done
+ ;;
+getinfo-devid)
+ echo "IBM MP STONITH device"
+ ;;
+getinfo-devname)
+ echo "IBM MP STONITH device"
+ ;;
+getinfo-devdescr)
+ echo "IBM MP host reboot/poweron/poweroff"
+ ;;
+getinfo-devurl)
+ echo "http://www.ibm.com"
+ ;;
+getinfo-xml)
+ cat <<EOF
+<parameters>
+
+<parameter name="hostname" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostname
+</shortdesc>
+<longdesc lang="en">
+The hostname of the host to be managed by this STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="ipaddr" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+IP Address
+</shortdesc>
+<longdesc lang="en">
+The IP address of the STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="userid" unique="0" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Login
+</shortdesc>
+<longdesc lang="en">
+The username used to login into the STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="passwd" unique="0" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Password
+</shortdesc>
+<longdesc lang="en">
+The password used to login into the STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="type" unique="0" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Management processor type
+</shortdesc>
+<longdesc lang="en">
+The type of the management processor. Possible values are
+"ibm" (default, typically used for RSA) and "ipmi"
+(for IPMI compliant processors such as BMC).
+</longdesc>
+</parameter>
+
+</parameters>
+EOF
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/ibmrsa-telnet b/lib/plugins/stonith/external/ibmrsa-telnet
new file mode 100644
index 0000000..4d75d9a
--- /dev/null
+++ b/lib/plugins/stonith/external/ibmrsa-telnet
@@ -0,0 +1,320 @@
+#!/usr/bin/python
+# vim: set filetype=python
+#######################################################################
+#
+# ibmrsa-telnet - External stonith plugin for HAv2 (http://linux-ha.org/wiki)
+# Connects to IBM RSA Board via telnet and switches power
+# of server appropriately.
+#
+# Author: Andreas Mock (andreas.mock@web.de)
+#
+# History:
+# 2007-10-19 Fixed bad commandline handling in case of stonithing
+# 2007-10-11 First release.
+#
+# Comment: Please send bug fixes and enhancements.
+# I hope the functionality of communicating via telnet is encapsulated
+# enough so that someone can use it for similar purposes.
+#
+# Description: IBM offers Remote Supervisor Adapters II for several
+# servers. These RSA boards can be accessed in different ways.
+# One of that is via telnet. Once logged in you can use 'help' to
+# show all available commands. With 'power' you can reset, power on and
+# off the controlled server. This command is used in combination
+# with python's standard library 'telnetlib' to do it automatically.
+#
+# cib-snippet: Please see README.ibmrsa-telnet for examples.
+#
+# Copyright (c) 2007 Andreas Mock (andreas.mock@web.de)
+# All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 or later of the GNU General Public
+# License as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+#######################################################################
+import sys
+import os
+import time
+import telnetlib
+import subprocess
+
+class TimeoutException(Exception):
+ def __init__(self, value=None):
+ Exception.__init__(self)
+ self.value = value
+
+ def __str__(self):
+ return repr(self.value)
+
+class RSABoard(telnetlib.Telnet):
+ def __init__(self, *args, **kwargs):
+ telnetlib.Telnet.__init__(self, *args, **kwargs)
+ self._timeout = 10
+ self._loggedin = 0
+ self._history = []
+ self._appl = os.path.basename(sys.argv[0])
+
+ def _get_timestamp(self):
+ ct = time.time()
+ msecs = (ct - long(ct)) * 1000
+ return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(ct)), msecs)
+
+ def write(self, buffer, nolog = False):
+ self._history.append(self._get_timestamp() + ': WRITE: ' +
+ (nolog and '******' or repr(buffer)))
+ telnetlib.Telnet.write(self, buffer)
+
+ def expect(self, what, timeout=20):
+ line = telnetlib.Telnet.expect(self, what, timeout)
+ self._history.append(self._get_timestamp() + ': READ : ' + repr(line))
+ if not line:
+ raise TimeoutException("Timeout while waiting for '%s'." % (what, ))
+ return line
+
+ def login(self, user, passwd):
+ time.sleep(1)
+ line = self.expect(['\nlogin : ', '\nusername: '], self._timeout)
+ self.write(user)
+ self.write('\r')
+ line = self.expect(['\nPassword: ', '\npassword: '], self._timeout)
+ self.write(passwd, nolog = True)
+ self.write('\r')
+ line = self.expect(['\nsystem>', '> '], self._timeout)
+
+ def reset(self):
+ self.write('power cycle\r')
+ line = self.expect(['\nok'], self._timeout)
+ line = self.expect(['\nsystem>', '> '], self._timeout)
+
+ def on(self):
+ self.write('power on\r')
+ line = self.expect(['\nok'], self._timeout)
+ line = self.expect(['\nsystem>', '> '], self._timeout)
+
+ def off(self):
+ self.write('power off\r')
+ line = self.expect(['\nok'], self._timeout)
+ line = self.expect(['\nsystem>', '> '], self._timeout)
+
+ def exit(self):
+ self.write('exit\r')
+
+ def get_history(self):
+ return "\n".join(self._history)
+
+
+class RSAStonithPlugin:
+ def __init__(self):
+ # define the external stonith plugin api
+ self._required_cmds = \
+ 'reset gethosts status getconfignames getinfo-devid ' \
+ 'getinfo-devname getinfo-devdescr getinfo-devurl ' \
+ 'getinfo-xml'
+ self._optional_cmds = 'on off'
+ self._required_cmds_list = self._required_cmds.split()
+ self._optional_cmds_list = self._optional_cmds.split()
+
+ # who am i
+ self._appl = os.path.basename(sys.argv[0])
+
+ # telnet connection object
+ self._connection = None
+
+ # the list of configuration names
+ self._confignames = ['nodename', 'ip_address', 'username', 'password']
+
+ # catch the parameters provided by environment
+ self._parameters = {}
+ for name in self._confignames:
+ try:
+ self._parameters[name] = os.environ.get(name, '').split()[0]
+ except IndexError:
+ self._parameters[name] = ''
+
+ def _get_timestamp(self):
+ ct = time.time()
+ msecs = (ct - long(ct)) * 1000
+ return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(ct)), msecs)
+
+ def _echo_debug(self, *args):
+ self.echo_log('debug', *args)
+
+ def echo(self, *args):
+ what = ''.join([str(x) for x in args])
+ sys.stdout.write(what)
+ sys.stdout.write('\n')
+ sys.stdout.flush()
+ self._echo_debug("STDOUT:", what)
+
+ def echo_log(self, level, *args):
+ subprocess.call(('ha_log.sh', level) + args)
+
+ def _get_connection(self):
+ if not self._connection:
+ c = RSABoard()
+ self._echo_debug("Connect to '%s'" %
+ (self._parameters['ip_address'],))
+ c.open(self._parameters['ip_address'])
+ self._echo_debug("Connection established")
+ c.login(self._parameters['username'],
+ self._parameters['password'])
+ self._connection = c
+
+ def _end_connection(self):
+ if self._connection:
+ self._connection.exit()
+ self._connection.close()
+
+ def reset(self):
+ self._get_connection()
+ self._connection.reset()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Reset of node '%s' done" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def on(self):
+ self._get_connection()
+ self._connection.on()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Switched node '%s' ON" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def off(self):
+ self._get_connection()
+ self._connection.off()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Switched node '%s' OFF" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def gethosts(self):
+ self.echo(self._parameters['nodename'])
+ return(0)
+
+ def status(self):
+ self._get_connection()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ return(0)
+
+ def getconfignames(self):
+ for name in ['nodename', 'ip_address', 'username', 'password']:
+ self.echo(name)
+ return(0)
+
+ def getinfo_devid(self):
+ self.echo("External Stonith Plugin for IBM RSA Boards")
+ return(0)
+
+ def getinfo_devname(self):
+ self.echo("External Stonith Plugin for IBM RSA Boards connecting "
+ "via Telnet")
+ return(0)
+
+ def getinfo_devdescr(self):
+ self.echo("External stonith plugin for HAv2 which connects to "
+ "a RSA board on IBM servers via telnet. Commands to "
+ "turn on/off power and to reset server are sent "
+ "appropriately. "
+ "(c) 2007 by Andreas Mock (andreas.mock@web.de)")
+ return(0)
+
+ def getinfo_devurl(self):
+ self.echo("http://www.ibm.com/Search/?q=remote+supervisor+adapter")
+
+ def getinfo_xml(self):
+ info = """<parameters>
+ <parameter name="nodename" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">nodename to shoot</shortdesc>
+ <longdesc lang="en">
+ Name of the node which has to be stonithed in case.
+ </longdesc>
+ </parameter>
+ <parameter name="ip_address" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">hostname or ip address of RSA</shortdesc>
+ <longdesc lang="en">
+ Hostname or ip address of RSA board used to reset node.
+ </longdesc>
+ </parameter>
+ <parameter name="username" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">username to login on RSA board</shortdesc>
+ <longdesc lang="en">
+ Username to login on RSA board.
+ </longdesc>
+ </parameter>
+ <parameter name="password" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">password to login on RSA board</shortdesc>
+ <longdesc lang="en">
+ Password to login on RSA board.
+ </longdesc>
+ </parameter>
+ </parameters>
+ """
+ self.echo(info)
+ return(0)
+
+ def not_implemented(self, cmd):
+ self.echo_log("err", "Command '%s' not implemented." % (cmd,))
+ return(1)
+
+ def usage(self):
+ usage = "Call me with one of the allowed commands: %s, %s" % (
+ ', '.join(self._required_cmds_list),
+ ', '.join(self._optional_cmds_list))
+ return usage
+
+ def process(self, argv):
+ self._echo_debug("========== Start =============")
+ if len(argv) < 1:
+ self.echo_log("err", 'At least one commandline argument required.')
+ return(1)
+ cmd = argv[0]
+ self._echo_debug("cmd:", cmd)
+ if cmd not in self._required_cmds_list and \
+ cmd not in self._optional_cmds_list:
+ self.echo_log("err", "Command '%s' not supported." % (cmd,))
+ return(1)
+ try:
+ cmd = cmd.lower().replace('-', '_')
+ func = getattr(self, cmd, self.not_implemented)
+ rc = func()
+ return(rc)
+ except Exception, args:
+ self.echo_log("err", 'Exception raised:', str(args))
+ if self._connection:
+ self.echo_log("err", self._connection.get_history())
+ self._connection.close()
+ return(1)
+
+
+if __name__ == '__main__':
+ stonith = RSAStonithPlugin()
+ rc = stonith.process(sys.argv[1:])
+ sys.exit(rc)
diff --git a/lib/plugins/stonith/external/ipmi b/lib/plugins/stonith/external/ipmi
new file mode 100644
index 0000000..abadd5a
--- /dev/null
+++ b/lib/plugins/stonith/external/ipmi
@@ -0,0 +1,276 @@
+#!/bin/sh
+#
+# External STONITH module using IPMI.
+# This modules uses uses the ipmitool program available from
+# http://ipmitool.sf.net/ for actual communication with the
+# managed device.
+#
+# Copyright (c) 2007 Martin Bene <martin.bene@icomedias.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+# Initialization -- fix locale settings so we can parse output from
+# binaries if we need it
+LANG=C
+LC_ALL=C
+
+RESET="power reset"
+POWEROFF="power off"
+POWERON="power on"
+STATUS="power status"
+IPMITOOL=${ipmitool:-"`which ipmitool 2>/dev/null`"}
+
+have_ipmi() {
+ test -x "${IPMITOOL}"
+}
+
+# Wrapper function for ipmitool that sets the correct host IP address,
+# username, and password, and invokes ipmitool with any arguments
+# passed in
+run_ipmitool() {
+ local ipmitool_opts privlvl=""
+ have_ipmi || {
+ ha_log.sh err "ipmitool not installed"
+ return 1
+ }
+ if [ -z "${ipaddr}" -o -z "${userid}" -o -z "${passwd}" ]; then
+ ha_log.sh err "ipaddr, userid or passwd missing; check configuration"
+ return 1
+ fi
+
+ if [ -z "${interface}" ]; then
+ # default to "lan" interface
+ interface="lan"
+ fi
+ if [ -n "${priv}" ]; then
+ # default to "lan" interface
+ privlvl="-L $priv"
+ fi
+
+ ipmitool_opts="-I ${interface} -H ${ipaddr} $privlvl"
+
+ case "${passwd_method}" in
+ param|'')
+ passwd_method=param
+ M="-P"
+ ;;
+ env)
+ M="-E"
+ ;;
+ file)
+ M="-f"
+ ;;
+ *)
+ ha_log.sh err "invalid passwd_method: \"${passwd_method}\""
+ return 1
+ esac
+
+ action="$*"
+
+ if [ $passwd_method = env ]
+ then
+ IPMI_PASSWORD="${passwd}" ${IPMITOOL} $ipmitool_opts -U "${userid}" -E ${action}
+ else
+ ${IPMITOOL} $ipmitool_opts -U "${userid}" $M "${passwd}" ${action}
+ fi 2>&1
+}
+
+# Yet another convenience wrapper that invokes run_ipmitool, captures
+# its output, logs the output, returns either 0 (on success) or 1 (on
+# any error)
+do_ipmi() {
+ if outp=`run_ipmitool $*`; then
+ ha_log.sh debug "ipmitool output: `echo $outp`"
+ return 0
+ else
+ ha_log.sh err "error executing ipmitool: `echo $outp`"
+ return 1
+ fi
+}
+
+# Check if the managed node is powered on. To do so, issue the "power
+# status" command. Should return either "Chassis Power is on" or
+# "Chassis Power is off".
+ipmi_is_power_on() {
+ local outp
+ outp=`run_ipmitool ${STATUS}`
+ case "${outp}" in
+ *on)
+ return 0
+ ;;
+ *off)
+ return 1
+ ;;
+ esac
+}
+
+
+case ${1} in
+gethosts)
+ echo $hostname
+ exit 0
+ ;;
+on)
+ do_ipmi "${POWERON}"
+ exit
+ ;;
+off)
+ do_ipmi "${POWEROFF}"
+ exit
+ ;;
+reset)
+ if ipmi_is_power_on; then
+ do_ipmi "${RESET}"
+ else
+ do_ipmi "${POWERON}"
+ fi
+ exit
+ ;;
+status)
+ # "status" reflects the status of the stonith _device_, not
+ # the managed node. Hence, only check if we can contact the
+ # IPMI device with "power status" command, don't pay attention
+ # to whether the node is in fact powered on or off.
+ do_ipmi "${STATUS}"
+ exit $?
+ ;;
+getconfignames)
+ for i in hostname ipaddr userid passwd interface; do
+ echo $i
+ done
+ exit 0
+ ;;
+getinfo-devid)
+ echo "IPMI STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "IPMI STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "ipmitool based power management. Apparently, the power off"
+ echo "method of ipmitool is intercepted by ACPI which then makes"
+ echo "a regular shutdown. If case of a split brain on a two-node"
+ echo "it may happen that no node survives. For two-node clusters"
+ echo "use only the reset method."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://ipmitool.sf.net/"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << IPMIXML
+<parameters>
+<parameter name="hostname" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostname
+</shortdesc>
+<longdesc lang="en">
+The name of the host to be managed by this STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="ipaddr" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+IP Address
+</shortdesc>
+<longdesc lang="en">
+The IP address of the STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="userid" unique="0">
+<content type="string" />
+<shortdesc lang="en">
+Login
+</shortdesc>
+<longdesc lang="en">
+The username used for logging in to the STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="passwd" unique="0">
+<content type="string" />
+<shortdesc lang="en">
+Password
+</shortdesc>
+<longdesc lang="en">
+The password used for logging in to the STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="passwd_method" unique="0">
+<content type="string" default="param"/>
+<shortdesc lang="en">
+Method for passing passwd parameter
+</shortdesc>
+<longdesc lang="en">
+Method for passing the passwd parameter to ipmitool
+ param: pass as parameter (-P)
+ env: pass via environment (-E)
+ file: value of "passwd" is actually a file name, pass with (-f)
+</longdesc>
+</parameter>
+
+<parameter name="interface" unique="0">
+<content type="string" default="lan"/>
+<shortdesc lang="en">
+IPMI interface
+</shortdesc>
+<longdesc lang="en">
+IPMI interface to use, such as "lan" or "lanplus".
+</longdesc>
+</parameter>
+
+<parameter name="priv" unique="0">
+<content type="string" default=""/>
+<shortdesc lang="en">
+The privilege level of the user.
+</shortdesc>
+<longdesc lang="en">
+The privilege level of the user, for instance OPERATOR. If
+unspecified the privilege level is ADMINISTRATOR. See
+ipmitool(1) -L option for more information.
+</longdesc>
+</parameter>
+
+<parameter name="ipmitool" unique="0">
+<content type="string" default=""/>
+<shortdesc lang="en">
+IPMI command(ipmitool)
+</shortdesc>
+<longdesc lang="en">
+Specify the full path to IPMI command.
+</longdesc>
+</parameter>
+
+</parameters>
+IPMIXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/ippower9258.in b/lib/plugins/stonith/external/ippower9258.in
new file mode 100755
index 0000000..6ae7e02
--- /dev/null
+++ b/lib/plugins/stonith/external/ippower9258.in
@@ -0,0 +1,316 @@
+#!/bin/sh
+#
+# External STONITH module using IP Power 9258 or compatible devices.
+#
+# Copyright (c) 2010 Helmut Weymann (Helmut (at) h-weymann (dot) de)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+#
+# Basic commands & parameters independent from individual device
+
+DEVICE="IP Power 9258"
+IPPowerOn="1"
+IPPowerOff="0"
+IPGetPower="Set.cmd?CMD=GetPower"
+IPSetPower="Set.cmd?CMD=SetPower"
+IPPort_name="P"
+IPPort0=60
+HTTP_COMMAND="wget -q -O - --"
+LOG_ERROR="ha_log.sh err"
+LOG_WARNING="ha_log.sh warn"
+LOG_INFO="ha_log.sh info"
+LOG_DEBUG="ha_log.sh debug"
+MY_COOKIES="cookies.txt"
+MY_TEMPFILE="temp.htm"
+PORT_STATUS="iocontrol.htm"
+UNDEFINED_HOSTNAME="*not-defined*"
+
+#
+# check MY_ROOT_PATH for IP Power 9258 and create it if necessary
+MY_ROOT_PATH="@GLUE_STATE_DIR@/heartbeat/rsctmp/ippower9258"
+
+#
+# script functions
+#
+
+get_challenge() {
+ #
+ # device sends a challenge for md5 encryption of username, password and challenge
+ send_web_command - "http://$deviceip/" | grep Challenge | grep input | cut -d '"' -f 6
+}
+
+get_cookie_from_device(){
+ # the form on the login page has these fields:
+ # Username, Password, Challenge, Response, ScreenWidth
+ #
+ challenge=`get_challenge`
+ response=`echo -n "$username$password$challenge" | md5sum | cut -b -32`
+ postdata="Username=$username&Password=&Challenge=&Response=$response&ScreenWidth=1024"
+ send_web_command " $MY_PATH/$MY_TEMPFILE --post-data=$postdata" "http://$deviceip/tgi/login.tgi"
+ if grep -qs "Invalid User name or Password" $MY_PATH/$MY_TEMPFILE
+ then
+ $LOG_ERROR "Login to device $deviceip failed."
+ $LOG_ERROR "Received Challenge = <<<$challenge>>>."
+ $LOG_ERROR "Sent postdata = <<<$postdata>>>."
+ exit 1
+ fi
+}
+
+get_data_from_device() {
+ # If successful all device info is available in MY_PATH
+ rm -f "$MY_PATH/$PORT_STATUS"
+ send_web_command "$MY_PATH/$PORT_STATUS" "http://$deviceip/$PORT_STATUS"
+ if grep -qs "Cookie Time Out" $MY_PATH/$PORT_STATUS
+ then
+ $LOG_ERROR "received no port data from $deviceip (Cookie Time Out)"
+ exit 1
+ fi
+}
+
+send_http_request() {
+ # ececution of http commands supported by the device
+ $HTTP_COMMAND "http://$username:$password@$deviceip/$1"
+}
+
+send_web_command(){
+ # ececution of web commands through the web-interface
+ WEB_COMMAND="wget -q --keep-session-cookies"
+ WEB_COMMAND="$WEB_COMMAND --load-cookies $MY_PATH/$MY_COOKIES"
+ WEB_COMMAND="$WEB_COMMAND --save-cookies $MY_PATH/$MY_COOKIES"
+ $WEB_COMMAND -O $1 -- $2
+}
+
+name2port() {
+ local name=$1
+ local i=$IPPort0
+ for h in $device_hostlist ; do
+ if [ $h = $name ]; then
+ echo $IPPort_name$i
+ return
+ fi
+ i=`expr $i + 1`
+ done
+ echo "invalid"
+}
+
+set_port() {
+ #
+ # port status is always set. Even if requested status is current status.
+ # host status is not considered.
+ local host=$1
+ local requested_status=$2 # 0 or 1
+ local port=`name2port $host`
+ if [ "$port" = "invalid" ]
+ then
+ $LOG_ERROR "Host $host is not in hostlist ($hostlist) for $deviceip."
+ exit 1
+ fi
+ ret=`send_http_request "$IPSetPower+$port=$requested_status" | cut -b 11`
+ if [ "$ret" != "$requested_status" ]
+ then
+ $LOG_ERROR "$DEVICE at $deviceip responds with wrong status $ret for host $host at port $port."
+ exit 1
+ fi
+ return 0
+}
+
+build_device_hostlist() {
+ #
+ # hostnames are available from http://$deviceip/iocontrol.htm"
+ # check for number of ports
+ #
+ device_hostlist=$(
+ w3m -dump $MY_PATH/$PORT_STATUS | grep 'Power[1-8]' |
+ sed 's/[^[]*\[//;s/\].*//;s/ *//' |
+ while read h; do
+ [ -z "$h" ] &&
+ echo $UNDEFINED_HOSTNAME ||
+ echo $h
+ done
+ )
+ if [ x = x"$device_hostlist" ]; then
+ $LOG_ERROR "cannot get hostlist for $deviceip"
+ exit 1
+ fi
+ $LOG_DEBUG "Got new hostlist ($device_hostlist) from $deviceip"
+}
+
+filter_device_hostlist() {
+ # check the given hostlist against the device hostlist
+ local host
+ for host in $device_hostlist; do
+ [ "$host" != "$UNDEFINED_HOSTNAME" ] &&
+ echo $host
+ done
+}
+
+check_hostlist() {
+ # check the given hostlist against the device hostlist
+ local cnt=`echo "$hostlist" | wc -w`
+ local cnt2=0
+ local host
+ for host in $hostlist; do
+ if [ `name2port $host` != "invalid" ]; then
+ cnt2=$((cnt2+1))
+ else
+ $LOG_ERROR "host $host not defined at $deviceip"
+ fi
+ done
+ [ $cnt -ne $cnt2 ] &&
+ exit 1
+}
+
+get_http_status() {
+ pattern="P60=[01],P61=[01],P62=[01],P63=[01],P64=[01],P65=[01],P66=[01],P67=[01]"
+ ret=`send_http_request "$IPGetPower" | grep $pattern`
+ if [ "X$ret" = "X" ]
+ then
+ $LOG_ERROR "$DEVICE at $deviceip returns invalid or no string."
+ exit 1
+ fi
+}
+
+hostlist=`echo $hostlist | tr ',' ' '`
+case $1 in
+gethosts|on|off|reset|status)
+ # need environment from stonithd
+ # and device information from individual device
+ #
+ # default device username is admin
+ # IP Power 9258 does not allow user management.
+ #
+ if [ "X$username" = "X" ]
+ then
+ username="admin"
+ fi
+
+ mkdir -p $MY_ROOT_PATH
+ tmp_path="$deviceip" # ensure a simple unique pathname
+ MY_PATH="$MY_ROOT_PATH/$tmp_path"
+ mkdir -p $MY_PATH
+ get_cookie_from_device
+ get_data_from_device
+ build_device_hostlist
+ if [ "X$hostlist" = "X" ]; then
+ hostlist="`filter_device_hostlist`"
+ else
+ check_hostlist
+ fi
+ ;;
+*)
+ # the client is asking for meta-data
+ ;;
+esac
+
+target=`echo $2 | sed 's/[.].*//'`
+# the necessary actions for stonithd
+case $1 in
+gethosts)
+ echo $hostlist
+ ;;
+on)
+ set_port $target $IPPowerOn
+ ;;
+off)
+ set_port $target $IPPowerOff
+ ;;
+reset)
+ set_port $target $IPPowerOff
+ sleep 5
+ set_port $target $IPPowerOn
+ ;;
+status)
+ # werify http command interface
+ get_http_status
+ ;;
+getconfignames)
+ # return all the config names
+ for ipparam in deviceip username password hostlist
+ do
+ echo $ipparam
+ done
+ ;;
+getinfo-devid)
+ echo "IP Power 9258"
+ ;;
+getinfo-devname)
+ echo "IP Power 9258 power switch"
+ ;;
+getinfo-devdescr)
+ echo "Power switch IP Power 9258 with 4 or 8 power outlets."
+ echo "WARNING: It is different from IP Power 9258 HP"
+ ;;
+getinfo-devurl)
+ echo "http://www.aviosys.com/manual.htm"
+ ;;
+getinfo-xml)
+ cat << IPPOWERXML
+<parameters>
+<parameter name="deviceip" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+IP address or hostname of the device.
+</shortdesc>
+<longdesc lang="en">
+The IP Address or the hostname of the device.
+</longdesc>
+</parameter>
+
+<parameter name="password" unique="0" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Password
+</shortdesc>
+<longdesc lang="en">
+The password to log in with.
+</longdesc>
+</parameter>
+
+<parameter name="hostlist" unique="0" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of hosts that the device controls.
+If you leave this list empty, we will retrieve the hostnames from the device.
+</longdesc>
+</parameter>
+
+<parameter name="username" unique="0" required="0">
+<content type="string" default="admin"/>
+<shortdesc lang="en">
+Account Name
+</shortdesc>
+<longdesc lang="en">
+The user to log in with.
+</longdesc>
+</parameter>
+
+</parameters>
+IPPOWERXML
+ ;;
+*)
+ $LOG_ERROR "Unexpected command $1 for $DEVICE at $deviceip."
+ exit 1;
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/kdumpcheck.in b/lib/plugins/stonith/external/kdumpcheck.in
new file mode 100644
index 0000000..7f3f752
--- /dev/null
+++ b/lib/plugins/stonith/external/kdumpcheck.in
@@ -0,0 +1,274 @@
+#!/bin/sh
+#
+# External STONITH module to check kdump.
+#
+# Copyright (c) 2008 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+SSH_COMMAND="@SSH@ -q -x -o PasswordAuthentication=no -o StrictHostKeyChecking=no -n"
+#Set default user name.
+USERNAME="kdumpchecker"
+#Initialize identity file-path options for ssh command
+IDENTITY_OPTS=""
+
+#Rewrite the hostlist to accept "," as a delimeter for hostnames too.
+hostlist=`echo ${hostlist} | tr ',' ' '`
+
+##
+# Check the parameter hostlist is set or not.
+# If not, exit with 6 (ERR_CONFIGURED).
+##
+check_hostlist() {
+ if [ -z "${hostlist}" ]; then
+ ha_log.sh err "hostlist is empty"
+ exit 6 #ERR_CONFIGURED
+ fi
+}
+
+##
+# Set kdump check user name to USERNAME.
+# always return 0.
+##
+get_username() {
+ kdump_conf="/etc/kdump.conf"
+
+ if [ ! -f "${kdump_conf}" ]; then
+ ha_log.sh debug "${kdump_conf} doesn't exist"
+ return 0
+ fi
+
+ tmp=""
+ while read config_opt config_val; do
+ if [ "${config_opt}" = "kdump_check_user" ]; then
+ tmp="${config_val}"
+ fi
+ done < "${kdump_conf}"
+ if [ -n "${tmp}" ]; then
+ USERNAME="${tmp}"
+ fi
+
+ ha_log.sh debug "kdump check user name is ${USERNAME}."
+}
+
+##
+# Check the specified or default identity file exists or not.
+# If not, exit with 6 (ERR_CONFIGURED).
+##
+check_identity_file() {
+ IDENTITY_OPTS=""
+ if [ -n "${identity_file}" ]; then
+ if [ ! -f "${identity_file}" ]; then
+ ha_log.sh err "${identity_file} doesn't exist."
+ exit 6 #ERR_CONFIGURED
+ fi
+ IDENTITY_OPTS="-i ${identity_file}"
+ else
+ flg_file_exists=0
+ homedir=`eval echo "~${USERNAME}"`
+ for filename in "${homedir}/.ssh/id_rsa" \
+ "${homedir}/.ssh/id_dsa" \
+ "${homedir}/.ssh/identity"
+ do
+ if [ -f "${filename}" ]; then
+ flg_file_exists=1
+ IDENTITY_OPTS="${IDENTITY_OPTS} -i ${filename}"
+ fi
+ done
+ if [ ${flg_file_exists} -eq 0 ]; then
+ ha_log.sh err "${USERNAME}'s identity file for ssh command" \
+ " doesn't exist."
+ exit 6 #ERR_CONFIGURED
+ fi
+ fi
+}
+
+##
+# Check the user to check doing kdump exists or not.
+# If not, exit with 6 (ERR_CONFIGURED).
+##
+check_user_existence() {
+
+ # Get kdump check user name and check whether he exists or not.
+ grep -q "^${USERNAME}:" /etc/passwd > /dev/null 2>&1
+ ret=$?
+ if [ ${ret} != 0 ]; then
+ ha_log.sh err "user ${USERNAME} doesn't exist." \
+ "please confirm \"kdump_check_user\" setting in /etc/kdump.conf." \
+ "(default user name is \"kdumpchecker\")"
+ exit 6 #ERR_CONFIGURED
+ fi
+}
+
+##
+# Check the target node is kdumping or not.
+# arg1 : target node name.
+# ret : 0 -> the target is kdumping.
+# : 1 -> the target is _not_ kdumping.
+# : else -> failed to check.
+##
+check_kdump() {
+ target_node="$1"
+
+ # Get kdump check user name.
+ get_username
+ check_user_existence
+ exec_cmd="${SSH_COMMAND} -l ${USERNAME}"
+
+ # Specify kdump check user's identity file for ssh command.
+ check_identity_file
+ exec_cmd="${exec_cmd} ${IDENTITY_OPTS}"
+
+ # Now, check the target!
+ # In advance, Write the following setting at the head of
+ # kdump_check_user's public key in authorized_keys file on target node.
+ # command="test -s /proc/vmcore", \
+ # no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty
+ ha_log.sh debug "execute the command [${exec_cmd} ${target_node}]."
+ ${exec_cmd} ${target_node} > /dev/null 2>&1
+ ret=$?
+ ha_log.sh debug "the command's result is ${ret}."
+
+ #ret -> 0 : vmcore file's size is not zero. the node is kdumping.
+ #ret -> 1 : the node is _not_ kdumping (vmcore didn't exist or
+ # its size is zero). It still needs to be STONITH'ed.
+ #ret -> 255 : ssh command is failed.
+ # else : Maybe command strings in authorized_keys is wrong...
+ return ${ret}
+}
+
+###
+#
+# Main function.
+#
+###
+case $1 in
+gethosts)
+ check_hostlist
+ for hostname in ${hostlist} ; do
+ echo "${hostname}"
+ done
+ exit 0
+ ;;
+on)
+ # This plugin does only check whether a target node is kdumping or not.
+ exit 1
+ ;;
+reset|off)
+ check_hostlist
+ ret=1
+ h_target=`echo $2 | tr A-Z a-z`
+ for hostname in ${hostlist}
+ do
+ hostname=`echo $hostname | tr A-Z a-z`
+ if [ "${hostname}" != "$h_target" ]; then
+ continue
+ fi
+ while [ 1 ]
+ do
+ check_kdump "$2"
+ ret=$?
+ if [ ${ret} -ne 255 ]; then
+ exit ${ret}
+ fi
+ #255 means ssh command itself is failed.
+ #For example, connection failure as if network doesn't start yet
+ #in 2nd kernel on the target node.
+ #So, retry to check after a little while.
+ sleep 1
+ done
+ done
+ exit ${ret}
+ ;;
+status)
+ check_hostlist
+ for hostname in ${hostlist}
+ do
+ if ping -w1 -c1 "${hostname}" 2>&1 | grep "unknown host"
+ then
+ exit 1
+ fi
+ done
+ get_username
+ check_user_existence
+ check_identity_file
+ exit 0
+ ;;
+getconfignames)
+ echo "hostlist identity_file"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "kdump check STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "kdump check STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "ssh-based kdump checker"
+ echo "To check whether a target node is dumping or not."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "kdump -> http://lse.sourceforge.net/kdump/"
+ echo "ssh -> http://openssh.org"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << SSHXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of hosts that the STONITH device controls
+</longdesc>
+</parameter>
+
+<parameter name="identity_file" unique="1" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Identity file's full path for kdump check user
+</shortdesc>
+<longdesc lang="en">
+The full path of kdump check user's identity file for ssh command.
+The identity in the specified file have to be restricted to execute
+only the following command.
+"test -s /proc/vmcore"
+Default: kdump check user's default identity file path.
+NOTE: You can specify kdump check user name in /etc/kdump.conf.
+ The parameter name is "kdump_check_user".
+ Default user is "kdumpchecker".
+</longdesc>
+</parameter>
+
+</parameters>
+SSHXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/libvirt b/lib/plugins/stonith/external/libvirt
new file mode 100644
index 0000000..494b048
--- /dev/null
+++ b/lib/plugins/stonith/external/libvirt
@@ -0,0 +1,298 @@
+#!/bin/sh
+#
+# External STONITH module for a libvirt managed hypervisor (kvm/Xen).
+# Uses libvirt as a STONITH device to control guest.
+#
+# Copyright (c) 2010 Holger Teutsch <holger.teutsch@web.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+# start a domain
+libvirt_start() {
+ out=$($VIRSH -c $hypervisor_uri start $domain_id 2>&1)
+ if [ $? -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id was started"
+ return 0
+ fi
+
+ $VIRSH -c $hypervisor_uri dominfo $domain_id 2>&1 |
+ egrep -q '^State:.*(running|idle)|already active'
+ if [ $? -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id is already active"
+ return 0
+ fi
+
+ ha_log.sh err "Failed to start domain $domain_id"
+ ha_log.sh err "$out"
+ return 1
+}
+# reboot a domain
+# return
+# 0: success
+# 1: error
+libvirt_reboot() {
+ local rc out
+ out=$($VIRSH -c $hypervisor_uri reboot $domain_id 2>&1)
+ rc=$?
+ if [ $rc -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id was rebooted"
+ return 0
+ fi
+ ha_log.sh err "Failed to reboot domain $domain_id (exit code: $rc)"
+ ha_log.sh err "$out"
+ return 1
+}
+
+# stop a domain
+# return
+# 0: success
+# 1: error
+# 2: was already stopped
+libvirt_stop() {
+ out=$($VIRSH -c $hypervisor_uri destroy $domain_id 2>&1)
+ if [ $? -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id was stopped"
+ return 0
+ fi
+
+ $VIRSH -c $hypervisor_uri dominfo $domain_id 2>&1 |
+ egrep -q '^State:.*shut off|not found|not running'
+ if [ $? -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id is already stopped"
+ return 2
+ fi
+
+ ha_log.sh err "Failed to stop domain $domain_id"
+ ha_log.sh err "$out"
+ return 1
+}
+
+# get status of stonith device (*NOT* of the domain).
+# If we can retrieve some info from the hypervisor
+# the stonith device is OK.
+libvirt_status() {
+ out=$($VIRSH -c $hypervisor_uri version 2>&1)
+ if [ $? -eq 0 ]
+ then
+ return 0
+ fi
+
+ ha_log.sh err "Failed to get status for $hypervisor_uri"
+ ha_log.sh err "$out"
+ return 1
+}
+
+# check config and set variables
+# does not return on error
+libvirt_check_config() {
+ VIRSH=`which virsh 2>/dev/null`
+
+ if [ ! -x "$VIRSH" ]
+ then
+ ha_log.sh err "virsh not installed"
+ exit 1
+ fi
+
+ if [ -z "$hostlist" -o -z "$hypervisor_uri" ]
+ then
+ ha_log.sh err "hostlist or hypervisor_uri missing; check configuration"
+ exit 1
+ fi
+
+ case "$reset_method" in
+ power_cycle|reboot) : ;;
+ *)
+ ha_log.sh err "unrecognized reset_method: $reset_method"
+ exit 1
+ ;;
+ esac
+}
+
+# set variable domain_id for the host specified as arg
+libvirt_set_domain_id ()
+{
+ for h in $hostlist
+ do
+ case $h in
+ $1:*)
+ domain_id=`expr $h : '.*:\(.*\)'`
+ return
+ ;;
+
+ $1)
+ domain_id=$1
+ return
+ esac
+ done
+
+ ha_log.sh err "Should never happen: Called for host $1 but $1 is not in $hostlist."
+ exit 1
+}
+
+libvirt_info() {
+cat << LVIRTXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+List of hostname[:domain_id]..
+</shortdesc>
+<longdesc lang="en">
+List of controlled hosts: hostname[:domain_id]..
+The optional domain_id defaults to the hostname.
+</longdesc>
+</parameter>
+
+<parameter name="hypervisor_uri" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hypervisor URI
+</shortdesc>
+<longdesc lang="en">
+URI for connection to the hypervisor.
+driver[+transport]://[username@][hostlist][:port]/[path][?extraparameters]
+e.g.
+qemu+ssh://my_kvm_server.mydomain.my/system (uses ssh for root)
+xen://my_kvm_server.mydomain.my/ (uses TLS for client)
+
+virsh must be installed (e.g. libvir-client package) and access control must
+be configured for your selected URI.
+</longdesc>
+</parameter>
+
+<parameter name="reset_method" required="0">
+<content type="string" default="power_cycle"/>
+<shortdesc lang="en">
+How to reset a guest.
+</shortdesc>
+<longdesc lang="en">
+A guest reset may be done by a sequence of off and on commands
+(power_cycle) or by the reboot command. Which method works
+depend on the hypervisor and guest configuration management.
+</longdesc>
+</parameter>
+</parameters>
+LVIRTXML
+exit 0
+}
+
+#############
+# Main code #
+#############
+
+# don't fool yourself when testing with stonith(8)
+# and transport ssh
+unset SSH_AUTH_SOCK
+
+# support , as a separator as well
+hostlist=`echo $hostlist| sed -e 's/,/ /g'`
+
+reset_method=${reset_method:-"power_cycle"}
+
+case $1 in
+ gethosts)
+ hostnames=`echo $hostlist|sed -e 's/:[^ ]*//g'`
+ for h in $hostnames
+ do
+ echo $h
+ done
+ exit 0
+ ;;
+
+ on)
+ libvirt_check_config
+ libvirt_set_domain_id $2
+
+ libvirt_start
+ exit $?
+ ;;
+
+ off)
+ libvirt_check_config
+ libvirt_set_domain_id $2
+
+ libvirt_stop
+ [ $? = 1 ] && exit 1
+ exit 0
+ ;;
+
+ reset)
+ libvirt_check_config
+ libvirt_set_domain_id $2
+
+ if [ "$reset_method" = "power_cycle" ]; then
+ libvirt_stop
+ [ $? = 1 ] && exit 1
+ sleep 2
+ libvirt_start
+ else
+ libvirt_reboot
+ fi
+ exit $?
+ ;;
+
+ status)
+ libvirt_check_config
+ libvirt_status
+ exit $?
+ ;;
+
+ getconfignames)
+ echo "hostlist hypervisor_uri reboot_method"
+ exit 0
+ ;;
+
+ getinfo-devid)
+ echo "libvirt STONITH device"
+ exit 0
+ ;;
+
+ getinfo-devname)
+ echo "libvirt STONITH external device"
+ exit 0
+ ;;
+
+ getinfo-devdescr)
+ echo "libvirt-based host reset for Xen/KVM guest domain through hypervisor"
+ exit 0
+ ;;
+
+ getinfo-devurl)
+ echo "http://libvirt.org/uri.html http://linux-ha.org/wiki"
+ exit 0
+ ;;
+
+ getinfo-xml)
+ libvirt_info
+ echo 0;
+ ;;
+
+ *)
+ exit 1
+ ;;
+esac
+
+# vi:et:ts=4:sw=4
diff --git a/lib/plugins/stonith/external/nut b/lib/plugins/stonith/external/nut
new file mode 100644
index 0000000..9e51bb8
--- /dev/null
+++ b/lib/plugins/stonith/external/nut
@@ -0,0 +1,302 @@
+#!/bin/sh
+
+# External STONITH module that uses the NUT daemon to control an external UPS.
+# See the comments below, and the various NUT man pages, for how this
+# script works. It should work unchanged with most modern "smart" APC UPSes in
+# a Redhat/Fedora/RHEL-style distribution with the nut package installed.
+
+# Author: William Seligman <seligman@nevis.columbia.edu>
+# License: GPLv2
+
+# As you're designing your UPS and STONITH set-up, it may help to consider that
+# there can be potentially three computers involved:
+# 1) the machine running this STONITH module;
+# 2) the machine being controlled by this STONITH module ($hostname);
+# 3) the machine that can send commands to the UPS.
+
+# On my cluster, all the UPSes have SNMP smartcards, so every host can communicate
+# with every UPS; in other words, machines (1) and (3) are the same. If your UPSes
+# are controlled via serial or USB connections, then you might have a
+# situation in which $hostname is plugged into a UPS, which has a serial connection
+# to some master "power-control" computer, and can potentially be STONITHed
+# by any other machine in your cluster.
+
+# In general, you'll probably need the nut daemon running on both the hosts (1) and
+# (3) in the above list. The NUT daemon will also have to run on (2) if you want the
+# reset command to gracefully reboot $hostname.
+
+# The NUT command default locations. In the RHEL-type nut packages, these binaries
+# are in /usr/bin.
+RHELUPSCMD="/usr/bin/upscmd"
+RHELUPSC="/usr/bin/upsc"
+
+# Defaults for APC smart UPSes:
+
+# Reset = reboot $hostname; this will be a graceful reboot if the host
+# is running NUT and monitoring $ups.
+APCRESET="shutdown.return"
+
+# Poweroff = turn off $hostname immediately by cutting the power on $ups.
+# For a graceful shutdown, use shutdown.stayoff instead of load.off,
+# but it might take a few minutes to shutdown in this way.
+APCPOWEROFF="load.off"
+
+# Poweron = turn on the power to $ups, which will presumably turn on $hostname.
+# (Did you set $hostname's BIOS to boot up on AC power restore, as opposed to
+# "last state"?)
+APCPOWERON="load.on"
+
+# Status = returns a short string with the $ups status; OL = on-line, OFF = off-line, etc.
+APCSTATUSVAR="ups.status"
+
+
+# Stick in the defaults, if needed.
+if [ -z "${poweron}" ]; then
+ poweron=${APCPOWERON}
+fi
+if [ -z "${poweroff}" ]; then
+ poweroff=${APCPOWEROFF}
+fi
+if [ -z "${reset}" ]; then
+ reset=${APCRESET}
+fi
+if [ -z "${statusvar}" ]; then
+ statusvar=${APCSTATUSVAR}
+fi
+if [ -z "${upscmd}" ]; then
+ upscmd=${RHELUPSCMD}
+fi
+if [ -z "${upsc}" ]; then
+ upsc=${RHELUPSC}
+fi
+
+
+# Define the command to fetch the UPS status.
+STATUSCMD="${upsc} ${ups} ${statusvar}"
+
+usage() {
+ echo "Usage: $0 {on|off|reset|status|gethosts|getconfignames|getinfo-devid|getinfo-devname|getinfo-devdescr|getinfo-devurl|getinfo-xml}"
+}
+
+# Can we find the NUT binary?
+have_nut() {
+ test -x "${upscmd}"
+}
+have_upsc() {
+ test -x "${upsc}"
+}
+
+do_nut() {
+ have_nut || {
+ echo "Can't find NUT upscmd command"
+ return 1
+ }
+ if [ -z "${username}" -o -z "${password}" -o -z "${ups}" ]; then
+ echo "username, password or ups name missing; check configuration"
+ return 1
+ fi
+ # Execute the command given in argument 1.
+ ${upscmd} -u ${username} -p ${password} ${ups} ${1} || {
+ echo "error executing nut command"
+ return 1
+ }
+}
+
+case ${1} in
+gethosts)
+ echo ${hostname}
+ exit 0
+ ;;
+on)
+ result=1
+ do_nut "${poweron}"
+ result=$?
+ exit ${result}
+ ;;
+off)
+ result=1
+ do_nut "${poweroff}"
+ result=$?
+ exit ${result}
+ ;;
+reset)
+ result=1
+ do_nut "${reset}"
+ result=$?
+ exit $result
+ ;;
+status)
+ have_upsc || {
+ echo "Can't find NUT upsc command"
+ exit 1
+ }
+ ${STATUSCMD}
+ exit $?
+ ;;
+getconfignames)
+ echo "hostname ups username password poweron poweroff reset statusvar upscmd upsc"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "NUT STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "NUT STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "A STONITH device based on NUT (Network UPS Tools)."
+ echo " "
+ echo "For this STONITH script to work, the following conditions have"
+ echo "to be met:"
+ echo " "
+ echo "- NUT has to be installed on both the host running this script"
+ echo " and the host that controls the UPS (on RHEL systems, NUT is"
+ echo " in packages nut and nut-client) and the nut daemon services"
+ echo " (normally called the ups or upsd service) must be running"
+ echo " on both systems."
+ echo " "
+ echo "- The UPS name has to be defined in ups.conf on the host"
+ echo " that controls the UPS."
+ echo " "
+ echo "- The username/password to access the UPS must be defined in"
+ echo " upsd.users on the host that controls the UPS, with the instcmds"
+ echo " for poweron, poweroff, and reset allowed."
+ echo " "
+ echo "- The host that is running this script must be allowed access"
+ echo " via upsd.conf and upsd.users on the host the controls the UPS."
+ echo " "
+ echo "On RHEL systems, the files listed above are in /etc/ups."
+ echo " "
+ echo "The defaults will probably work with APC UPS devices. It might"
+ echo "work on others; 'upscmd -l (ups)' and 'upsc (ups)' will list"
+ echo "the commands and variables, and you can change the values"
+ echo "for poweron, poweroff, reset, and statusvar to suit your UPS."
+ echo "Change upscmd and upsc if your NUT binaries are not in /usr/bin."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://www.networkupstools.org/"
+ exit 0
+ ;;
+getinfo-xml)
+cat << nutXML
+<parameters>
+
+<parameter name="hostname" unique="1" required="1">
+<content type="string" default="" />
+<shortdesc lang="en">Hostname</shortdesc>
+<longdesc lang="en">
+The name of the host to be managed by this STONITH device.
+The nut daemon must be running on the host controllng the
+UPS _and_ on the host running this script; this script does
+not start/stop the daemons for you.
+</longdesc>
+</parameter>
+
+<parameter name="ups" required="1">
+<content type="string" default="" />
+<shortdesc lang="en">UPS name</shortdesc>
+<longdesc lang="en">
+The name of the UPS as defined in ups.conf on the host
+controlling the UPS. The format for this option is
+upsname[@controlhost[:port]]. The default controlhost is
+"localhost".
+</longdesc>
+</parameter>
+
+<parameter name="username" required="1">
+<content type="string" default="" />
+<shortdesc lang="en">Username</shortdesc>
+<longdesc lang="en">
+The username used for accessing the UPS. This is defined in
+upsd.conf on the host controlling the UPS.
+</longdesc>
+</parameter>
+
+<parameter name="password" required="1">
+<content type="string" default="" />
+<shortdesc lang="en">Password</shortdesc>
+<longdesc lang="en">
+The password used for logging in to the UPS for the host
+controlling the UPS, as defined in upsd.conf on that host.
+</longdesc>
+</parameter>
+
+<parameter name="poweron">
+<content type="string" default="$APCPOWERON" />
+<shortdesc lang="en">UPS Power On command</shortdesc>
+<longdesc lang="en">
+The NUT hardware command to turn on the UPS. The default
+should work for most "smart" APC UPSes. For a list of
+commands that your UPS can support, type 'upscmd -l (ups)'
+on the command line.</longdesc>
+</parameter>
+
+<parameter name="poweroff">
+<content type="string" default="$APCPOWEROFF" />
+<shortdesc lang="en">UPS Power Off command</shortdesc>
+<longdesc lang="en">
+The NUT hardware command to turn off the UPS. On most APC
+"smart" UPSes, the command shutdown.stayoff will result
+in a graceful shutdown, provided the host is running the
+nut daemon, but this might take a few minutes; load.off
+will cut the power immediately. For a list of commands that
+your UPS can support, type 'upscmd -l (ups)' on the command
+line.
+</longdesc>
+</parameter>
+
+<parameter name="reset">
+<content type="string" default="$APCRESET" />
+<shortdesc lang="en">UPS Reset command</shortdesc>
+<longdesc lang="en">
+The NUT hardware command to reset the host. On most APC
+"smart" UPSes, the command shutdown.return will result
+in a graceful shutdown, with power restored after perhaps
+a short interval. For a list of commands that your UPS can
+ support, type 'upscmd -l (ups)' on the command line.
+</longdesc>
+</parameter>
+
+<parameter name="statusvar">
+<content type="string" default="$APCSTATUSVAR" />
+<shortdesc lang="en">UPS Status variable</shortdesc>
+<longdesc lang="en">
+The NUT variable that returns the status of the UPS. On APC
+UPSes, the value of ups.status will be "OL" if the UPS is
+"on-line." For a list of variables that your UPS supports,
+type 'upsc (ups)' on the command line.
+</longdesc>
+</parameter>
+
+<parameter name="upscmd">
+<content type="string" default="$RHELUPSCMD" />
+<shortdesc lang="en">upscmd binary location</shortdesc>
+<longdesc lang="en">
+The full path to the NUT binary command 'upscmd'. On RHEL
+systems with the nut RPM installed, this location is
+/usr/bin/upscmd.
+</longdesc>
+</parameter>
+
+<parameter name="upsc">
+<content type="string" default="$RHELUPSC" />
+<shortdesc lang="en">upsc binary location</shortdesc>
+<longdesc lang="en">
+The full path to the NUT binary command 'upsc'. On RHEL
+systems with the nut RPM installed, this location is
+/usr/bin/upsc.
+</longdesc>
+</parameter>
+
+</parameters>
+nutXML
+exit 0
+;;
+*)
+ usage
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/rackpdu b/lib/plugins/stonith/external/rackpdu
new file mode 100644
index 0000000..7d0e20b
--- /dev/null
+++ b/lib/plugins/stonith/external/rackpdu
@@ -0,0 +1,280 @@
+#!/bin/sh
+#
+# External STONITH module for APC Switched Rack PDU
+#
+# Copyright (c) 2008 Sergey Maznichenko <msergeyb@gmail.com> <inbox@it-consultant.su>
+# Version 1.2
+#
+# See http://www.it-consultant.su/rackpdu
+# for additional information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+SWITCH_ON="1"
+SWITCH_OFF="2"
+SWITCH_RESET="3"
+
+DEFAULT_NAMES_OID=".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2"
+DEFAULT_COMMAND_OID=".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4"
+
+if [ -z "$oid" ]; then
+ oid=$DEFAULT_COMMAND_OID
+fi
+
+if [ -z "$names_oid" ]; then
+ names_oid=$DEFAULT_NAMES_OID
+fi
+
+if [ -z "$outlet_config" ]; then
+ outlet_config="none"
+fi
+
+GetOutletNumber() {
+ local nodename=$1
+
+ if [ "$outlet_config" != "none" ]; then
+ # Get outlet number from file
+
+ if [ -f "$outlet_config" ]; then
+ local outlet_num=`grep $nodename $outlet_config | tr -d ' ' | cut -f2 -d'='`
+ if [ -z "$outlet_num" ]; then
+ ha_log.sh err "Outlet number not found for node $nodename. Check configuration file $outlet_config"
+ return 0
+ fi
+ return $outlet_num
+ else
+ ha_log.sh err "File $outlet_config not found."
+ return 0
+ fi
+ else
+ # Get outlet number from device
+
+ local outlet_num=1
+ local snmp_result
+ snmp_result=`snmpwalk -v1 -c $community $pduip $names_oid 2>&1`
+ if [ $? -ne 0 ]; then
+ ha_log.sh err "snmpwalk $community $pduip $names_oid failed. Result: $snmp_result"
+ return 0
+ fi
+
+ local names
+ names=`echo "$snmp_result" | cut -f2 -d'"' | tr ' ' '_' | tr '\012' ' '`
+ for name in $names; do
+ if [ "$name" != "$nodename" ]; then
+ local outlet_num=`expr $outlet_num + 1`
+ continue
+ fi
+
+ return $outlet_num
+ done
+
+ ha_log.sh err "Outlet number not found for node $nodename. Result: $snmp_result"
+ return 0
+ fi
+}
+
+SendCommand() {
+
+ local host=$1
+ local command=$2
+
+ GetOutletNumber $host
+ local outlet=$?
+
+ if [ $outlet -gt 0 ]; then
+ local set_result
+ set_result=`snmpset -v1 -c $community $pduip $oid.$outlet i $command 2>&1`
+ if [ $? -ne 0 ]; then
+ ha_log.sh err "Write SNMP to $pduip value $oid.$outlet=$command failed. Result: $set_result"
+ return 1
+ fi
+ if echo "$set_result" | grep -qs "Timeout"; then
+ ha_log.sh err "Write SNMP to $pduip value $oid.$outlet=$command timed out. Result: $set_result"
+ return 1
+ fi
+ return 0
+ else
+ return 1
+ fi
+}
+
+hostlist=`echo $hostlist | tr ',' ' '`
+incommand=$1
+innode=$2
+
+case $incommand in
+gethosts)
+ if [ "$hostlist" = "AUTO" ]; then
+ snmp_result=`snmpwalk -v1 -c $community $pduip $names_oid 2>&1`
+ if [ $? -ne 0 ]; then
+ ha_log.sh err "snmpwalk $community $pduip $names_oid failed. Result: $snmp_result"
+ exit 1
+ fi
+ if echo "$snmp_result" | grep -qs "Timeout"; then
+ ha_log.sh err "snmpwalk $community $pduip $names_oid timed out. Result: $snmp_result"
+ exit 1
+ else
+ hostlist=`echo "$snmp_result" | cut -f2 -d'"' | tr ' ' '_' | tr '\012' ' '`
+ fi
+ fi
+
+ for h in $hostlist ; do
+ echo $h
+ done
+
+ exit 0
+ ;;
+on)
+ if
+ SendCommand $innode $SWITCH_ON
+ then
+ exit 0
+ else
+ exit 1
+ fi
+ ;;
+off)
+ if
+ SendCommand $innode $SWITCH_OFF
+ then
+ exit 0
+ else
+ exit 1
+ fi
+ ;;
+reset)
+ if
+ SendCommand $innode $SWITCH_RESET
+ then
+ exit 0
+ else
+ exit 1
+ fi
+ ;;
+status)
+ if [ -z "$pduip" ]; then
+ exit 1
+ fi
+
+ if ping -w1 -c1 $pduip >/dev/null 2>&1; then
+ exit 0
+ else
+ exit 1
+ fi
+ ;;
+getconfignames)
+ echo "hostlist pduip community"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "rackpdu STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "rackpdu STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "APC Switched Rack PDU"
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://www.apcc.com/products/family/index.cfm?id=30"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << PDUXML
+<parameters>
+ <parameter name="hostlist" unique="1" required="1">
+ <content type="string" default="AUTO" />
+ <shortdesc lang="en">Hostlist</shortdesc>
+ <longdesc lang="en">
+The list of hosts that the STONITH device controls (comma or space separated).
+If you set value of this parameter to AUTO, list of hosts will be get from Rack PDU device.
+ </longdesc>
+ </parameter>
+
+ <parameter name="pduip" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">Name or IP address of Rack PDU device.</shortdesc>
+ <longdesc lang="en">Name or IP address of Rack PDU device.</longdesc>
+ </parameter>
+
+ <parameter name="community" unique="1" required="1">
+ <content type="string" default="private" />
+ <shortdesc lang="en">Name of write community.</shortdesc>
+ <longdesc lang="en">Name of write community.</longdesc>
+ </parameter>
+
+ <parameter name="oid" unique="1" required="0">
+ <content type="string" />
+ <shortdesc lang="en">
+ The OID without the outlet number.
+ </shortdesc>
+ <longdesc lang="en">
+The SNMP OID for the PDU. minus the outlet number.
+Try .1.3.6.1.4.1.318.1.1.12.3.3.1.1.4 (default value)
+or use mib from ftp://ftp.apcc.com/apc/public/software/pnetmib/mib/
+Varies on different APC hardware and firmware.
+Warning! No dot at the end of OID
+ </longdesc>
+ </parameter>
+
+ <parameter name="names_oid" unique="1" required="0">
+ <content type="string" />
+ <shortdesc lang="en">The OID for getting names of outlets.</shortdesc>
+ <longdesc lang="en">
+The SNMP OID for getting names of outlets.
+It is required to recognize outlet number by nodename.
+Try ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2" (default value)
+or use mib from ftp://ftp.apcc.com/apc/public/software/pnetmib/mib/
+Names of nodes must be equal names of outlets, in other way use outlet_config parameter.
+If you set 'names_oid' parameter then parameter outlet_config must not be use.
+Varies on different APC hardware and firmware.
+Warning! No dot at the end of OID
+ </longdesc>
+ </parameter>
+
+ <parameter name="outlet_config" unique="1" required="0">
+ <content type="string" />
+ <shortdesc lang="en">Configuration file. Other way to recognize outlet number by nodename.</shortdesc>
+ <longdesc lang="en">
+Configuration file. Other way to recognize outlet number by nodename.
+Configuration file which contains
+node_name=outlet_number
+strings.
+
+Example:
+server1=1
+server2=2
+
+If you use outlet_config parameter then names_oid parameter can have any value and it is not uses.
+ </longdesc>
+ </parameter>
+
+</parameters>
+PDUXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/riloe b/lib/plugins/stonith/external/riloe
new file mode 100644
index 0000000..ce98847
--- /dev/null
+++ b/lib/plugins/stonith/external/riloe
@@ -0,0 +1,530 @@
+#!/usr/bin/env python
+#
+# Stonith module for RILOE Stonith device
+#
+# Copyright (c) 2004 Alain St-Denis <alain.st-denis@ec.gc.ca>
+#
+# Modified by Alan Robertson <alanr@unix.sh> for STONITH external compatibility.
+#
+# Extended and merged by Tijl Van den broeck <subspawn@gmail.com>
+# with ilo-v2 script from Guy Coates
+#
+# Cleanup by Andrew Beekhof <abeekhof@suse.de>
+#
+# Rewritten by Dejan Muhamedagic <dejan@suse.de>
+# Now, the plugin actually reads replies from iLO.
+#
+# Extended by Jochen Roeder <jochen.roeder@novell.com>
+# to enable access via proxies
+#
+# 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
+#
+import sys
+import os
+import socket
+import subprocess
+import xml.dom.minidom
+import httplib
+import time
+import re
+
+def log_msg(level,msg):
+ subprocess.call("ha_log.sh %s '%s'" % (level,msg), shell=True)
+def my_err(msg):
+ log_msg("err", msg)
+def my_warn(msg):
+ log_msg("warn", msg)
+def my_debug(msg):
+ log_msg("debug", msg)
+def fatal(msg):
+ my_err(msg)
+ sys.exit(1)
+
+argv = sys.argv
+
+try:
+ cmd = argv[1]
+except IndexError:
+ my_err("Not enough arguments")
+ sys.exit(1)
+
+legacy_RI_HOST = os.environ.get('RI_HOST', '')
+legacy_RI_HOSTRI = os.environ.get('RI_HOSTRI', '')
+legacy_RI_LOGIN = os.environ.get('RI_LOGIN', 'Administrator')
+legacy_RI_PASSWORD = os.environ.get('RI_PASSWORD', '')
+
+reset_ok = os.environ.get('ilo_can_reset', '0')
+ilo_protocol = os.environ.get('ilo_protocol', '1.2')
+power_method = os.environ.get('ilo_powerdown_method', 'power')
+
+realhost = os.environ.get('hostlist', legacy_RI_HOST)
+rihost = os.environ.get('ilo_hostname', legacy_RI_HOSTRI)
+ilouser = os.environ.get('ilo_user', legacy_RI_LOGIN)
+ilopass = os.environ.get('ilo_password', legacy_RI_PASSWORD)
+iloproxyhost = os.environ.get('ilo_proxyhost', '')
+try:
+ iloproxyport = int(os.environ.get('ilo_proxyport', 3128))
+except ValueError:
+ my_err("ilo_proxyport is not a number")
+ sys.exit(1)
+
+xmlinfo = '''<parameters>
+ <parameter name="hostlist" unique="1" required="1">
+ <content type="string"/>
+ <shortdesc lang="en">ilo target hostname</shortdesc>
+ <longdesc lang="en">
+ Contains the hostname that the ilo controls
+ </longdesc>
+ </parameter>
+<parameter name="ilo_hostname" unique="1" required="1">
+ <content type="string"/>
+ <shortdesc lang="en">ilo device hostname</shortdesc>
+ <longdesc lang="en">
+ The hostname of the ilo device
+ </longdesc>
+ </parameter>
+<parameter name="ilo_user" unique="0" required="1">
+ <content type="string" default="Administrator"/>
+ <shortdesc lang="en">ilo user</shortdesc>
+ <longdesc lang="en">
+ The user for connecting to the ilo device
+ </longdesc>
+ </parameter>
+<parameter name="ilo_password" unique="0" required="1">
+ <content type="string" default=""/>
+ <shortdesc lang="en">password</shortdesc>
+ <longdesc lang="en">
+ The password for the ilo device user
+ </longdesc>
+ </parameter>
+<parameter name="ilo_can_reset" unique="0" required="0">
+ <content type="string" default="0"/>
+ <shortdesc lang="en">Device can reset</shortdesc>
+ <longdesc lang="en">
+ Does the ILO device support RESET commands (hint: older ones cannot)
+ </longdesc>
+ </parameter>
+<parameter name="ilo_protocol" unique="0" required="0">
+ <content type="string" default="1.2"/>
+ <shortdesc lang="en">ILO Protocol</shortdesc>
+ <longdesc lang="en">
+ Protocol version supported by the ILO device.
+ Known supported versions: 1.2, 2.0
+ </longdesc>
+ </parameter>
+<parameter name="ilo_powerdown_method" unique="0" required="0">
+ <content type="string" default="power"/>
+ <shortdesc lang="en">Power down method</shortdesc>
+ <longdesc lang="en">
+ The method to powerdown the host in question.
+ * button - Emulate holding down the power button
+ * power - Emulate turning off the machines power
+
+ NB: A button request takes around 20 seconds. The power method
+ about half a minute.
+ </longdesc>
+ </parameter>
+<parameter name="ilo_proxyhost" unique="0" required="0">
+ <content type="string" default=""/>
+ <shortdesc lang="en">Proxy hostname</shortdesc>
+ <longdesc lang="en">
+ proxy hostname if required to access ILO board
+ </longdesc>
+ </parameter>
+<parameter name="ilo_proxyport" unique="0" required="0">
+ <content type="string" default="3128"/>
+ <shortdesc lang="en">Proxy port</shortdesc>
+ <longdesc lang="en">
+ proxy port if required to access ILO board
+ parameter will be ignored if proxy hostname is not set
+ </longdesc>
+ </parameter>
+
+</parameters>'''
+
+info = {
+ 'getinfo-devid': 'iLO2',
+ 'getinfo-devname': 'ilo2 ' + rihost,
+ 'getinfo-devdescr': 'HP/COMPAQ iLO2 STONITH device',
+ 'getinfo-devurl': 'http://www.hp.com/',
+ 'gethosts': realhost,
+ 'getinfo-xml': xmlinfo
+}
+
+if cmd in info:
+ print info[cmd]
+ sys.exit(0)
+
+if cmd == 'getconfignames':
+ for arg in [ "hostlist", "ilo_hostname", "ilo_user", "ilo_password", "ilo_can_reset", "ilo_protocol", "ilo_powerdown_method", "ilo_proxyhost", "ilo_proxyport"]:
+ print arg
+ sys.exit(0)
+
+if not rihost:
+ fatal("ILO device hostname not specified")
+
+if not realhost:
+ fatal("Host controlled by this ILO device not specified")
+
+if not power_method in ("power","button"):
+ my_err('unknown power method %s, setting to "power"')
+ power_method = "power"
+
+# XML elements
+E_RIBCL = "RIBCL"
+E_LOGIN = "LOGIN"
+E_SERVER_INFO = "SERVER_INFO"
+
+# power mgmt methods
+E_RESET = "RESET_SERVER" # error if powered off
+E_COLD_BOOT = "COLD_BOOT_SERVER" # error if powered off
+E_WARM_BOOT = "WARM_BOOT_SERVER" # error if powered off
+E_PRESS_BUTTON = "PRESS_PWR_BTN"
+E_HOLD_BUTTON = "HOLD_PWR_BTN"
+
+# get/set status elements
+E_SET_POWER = "SET_HOST_POWER"
+E_GET_PSTATUS = "GET_HOST_POWER_STATUS"
+
+# whatever this means, but we have to use it to get good XML
+E_LOCFG = "LOCFG"
+LOCFG_VER = '2.21'
+
+# attributes
+A_VERSION = "VERSION" # ilo_protocol
+A_USER = "USER_LOGIN"
+A_PWD = "PASSWORD"
+A_MODE = "MODE" # info mode (read or write)
+A_POWER_SW = "HOST_POWER" # "Y" or "N"
+A_POWER_STATE = "HOST_POWER" # "ON" or "OFF"
+
+def new_power_req(tag, name = None, value = None):
+ '''
+ Create a new RIBCL request (as XML).
+ '''
+ my_debug("creating power request: %s,%s,%s"%(tag,name,value))
+ doc = xml.dom.minidom.Document()
+ locfg = doc.createElement(E_LOCFG)
+ locfg.setAttribute(A_VERSION,LOCFG_VER)
+ ribcl = doc.createElement(E_RIBCL)
+ ribcl.setAttribute(A_VERSION,ilo_protocol)
+ login = doc.createElement(E_LOGIN)
+ login.setAttribute(A_USER,ilouser)
+ login.setAttribute(A_PWD,ilopass)
+ serv_info = doc.createElement(E_SERVER_INFO)
+ # read or write, it doesn't really matter, i.e. even if we
+ # say "write" that doesn't mean we can't read
+ serv_info.setAttribute(A_MODE,"write")
+ doc.appendChild(locfg)
+ locfg.appendChild(ribcl)
+ ribcl.appendChild(login)
+ login.appendChild(serv_info)
+ el_node = doc.createElement(tag)
+ if name:
+ el_node.setAttribute(name,value)
+ serv_info.appendChild(el_node)
+ s = doc.toprettyxml()
+ doc.unlink()
+ # work around an iLO bug: last line containing "</LOCFG>"
+ # produces a syntax error
+ lines = s.split('\n')
+ return '\n'.join(lines[:-2])
+
+E_RESPONSE = "RESPONSE"
+E_HOST_POWER = "GET_HOST_POWER"
+A_STATUS = "STATUS"
+# documentation mentions both; better safe than sorry
+A_MSG = "MSG"
+A_MSG2 = "MESSAGE"
+
+def is_element(xmlnode):
+ return xmlnode.nodeType == xmlnode.ELEMENT_NODE
+
+def read_resp(node):
+ '''
+ Check if the RESPONSE XML is OK.
+ '''
+ msg = ""
+ str_status = ""
+ for attr in node.attributes.keys():
+ if attr == A_STATUS:
+ str_status = node.getAttribute(attr)
+ elif attr == A_MSG:
+ msg = node.getAttribute(attr)
+ elif attr == A_MSG2:
+ msg = node.getAttribute(attr)
+ else:
+ my_warn("unexpected attribute %s in %s" % (attr,E_RESPONSE))
+ if not str_status:
+ my_err("no status in response")
+ return -1
+ try:
+ status = int(str_status,16)
+ except ValueError:
+ my_err("unexpected status %s in response" % str_status)
+ return -1
+ if status != 0:
+ my_err("%s (rc: %s)"%(msg,str_status))
+ return -1
+ return 0
+
+def read_power(node):
+ '''
+ Read the power from the XML node. Set the global power
+ variable correspondingly.
+ '''
+ global power
+ for attr in node.attributes.keys():
+ if attr == A_POWER_STATE:
+ power_state = node.getAttribute(attr).upper()
+ else:
+ my_warn("unexpected attribute %s in %s" % (attr,node.tagName))
+ if not power_state:
+ my_err("no %s attribute in %s" % (A_POWER_STATE,node.tagName))
+ return -1
+ if power_state not in ("ON","OFF"):
+ my_err("unexpected value for %s: %s" % (A_POWER_STATE,power_state))
+ return -1
+ power = (power_state == "ON")
+ my_debug("Host has power: %s"%power)
+ return 0
+
+el_parsers = {
+ E_RESPONSE:read_resp,
+ E_HOST_POWER:read_power
+}
+def proc_resp(doc):
+ '''
+ Process one iLO reply. Real work is done in el_parsers.
+ '''
+ ribcl = doc.childNodes[0]
+ if not is_element(ribcl) or ribcl.tagName != E_RIBCL:
+ my_err("unexpected top element in response")
+ return -1
+ for child in ribcl.childNodes:
+ if not is_element(child):
+ continue
+ if child.tagName in el_parsers:
+ rc = el_parsers[child.tagName](child)
+ if rc != 0:
+ return -1
+ else:
+ my_warn("unexpected element in response: %s" % child.toxml())
+ return 0
+
+def open_ilo(host):
+ # open https connection
+ try:
+ if iloproxyhost != "" and iloproxyport != 0:
+ proxy=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
+ proxy.connect((iloproxyhost, iloproxyport))
+ proxy_connect='CONNECT %s:%s HTTP/1.1\r\n'%(host,443)
+ user_agent='User-Agent: python\r\n'
+ proxy_pieces=proxy_connect+user_agent+'\r\n'
+ proxy.sendall(proxy_pieces)
+ response=proxy.recv(8192)
+ status=response.split()[1]
+ if status!=str(200):
+ fatal("Error status=: %s" %(response))
+ import ssl
+ sock = ssl.wrap_socket(proxy)
+ h=httplib.HTTPConnection('localhost')
+ h.sock=sock
+ return h
+ else:
+ return httplib.HTTPSConnection(host)
+ except socket.gaierror, msg:
+ fatal("%s: %s" %(msg,host))
+ except socket.sslerror, msg:
+ fatal("%s for %s" %(msg,host))
+ except socket.error, msg:
+ fatal("%s while talking to %s" %(msg,host))
+ except ImportError, msg:
+ fatal("ssl support missing (%s)" %msg)
+
+def send_request(req,proc_f):
+ '''
+ 1. After every request, the iLO closes the connection.
+ 2. For every request, there are multiple replies. Each reply
+ is an XML document. Most of replies are just a kind of
+ (verbose) XML "OK".
+ '''
+ t_begin = time.time()
+ c = open_ilo(rihost)
+ try:
+ c.send(req+'\r\n')
+ except socket.error, msg:
+ fatal("%s, while talking to %s" %(msg,rihost))
+ t_end = time.time()
+ my_debug("request sent in %0.2f s" % ((t_end-t_begin)))
+
+ t_begin = time.time()
+ result = []
+ while True:
+ try:
+ reply = c.sock.recv(1024)
+ if not reply:
+ break
+ result.append(reply)
+ except socket.error, msg:
+ if msg[0] == 6: # connection closed
+ break
+ my_err("%s, while talking to %s" %(msg,rihost))
+ return -1
+ c.close()
+ t_end = time.time()
+
+ if not result:
+ fatal("no response from %s within %0.2f s"%(rihost,(t_end-t_begin)))
+ for reply in result:
+ # work around the iLO bug, i.e. element RIBCL closed twice
+ if re.search("</RIBCL", reply) and re.search("<RIBCL.*/>", reply):
+ reply = re.sub("<(RIBCL.*)/>", r"<\1>", reply)
+ try:
+ doc = xml.dom.minidom.parseString(reply)
+ except xml.parsers.expat.ExpatError,msg:
+ fatal("malformed response: %s\n%s"%(msg,reply))
+ rc = proc_f(doc)
+ doc.unlink()
+ if rc != 0:
+ break
+ my_debug("iLO processed request (rc=%d) in %0.2f s" % (rc,(t_end-t_begin)))
+ return rc
+
+def manage_power(cmd):
+ '''
+ Before trying to send a request we have to check the power
+ state.
+ '''
+ rc = 0
+ req = ''
+ # it won't do to turn it on if it's already on!
+ if cmd == "on" and not power:
+ req = new_power_req(E_SET_POWER,A_POWER_SW,"Y")
+ # also to turn it off if it's already off
+ elif cmd == "off" and power:
+ req = new_power_req(E_SET_POWER,A_POWER_SW,"N")
+ elif cmd == "cold_boot" and power:
+ req = new_power_req(E_COLD_BOOT)
+ elif cmd == "warm_boot" and power:
+ req = new_power_req(E_WARM_BOOT)
+ elif cmd == "reset":
+ if power:
+ req = new_power_req(E_RESET)
+ # reset doesn't work if the host's off
+ else:
+ req = new_power_req(E_SET_POWER,A_POWER_SW,"Y")
+ if req:
+ rc = send_request(req,proc_resp)
+ return rc
+def power_on():
+ '''
+ Update the power variable without checking the power state.
+ The iLO is slow at times to report the actual power state, so
+ we assume that it changed if the request succeeded.
+ '''
+ rc = manage_power("on")
+ if rc == 0:
+ global power
+ power = True
+ return rc
+def power_off():
+ rc = manage_power("off")
+ if rc == 0:
+ global power
+ power = False
+ return rc
+def cold_boot():
+ rc = manage_power("cold_boot")
+ return rc
+def warm_boot():
+ rc = manage_power("warm_boot")
+ return rc
+def reset():
+ rc = manage_power("reset")
+ if rc == 0:
+ global power
+ power = True
+ return rc
+def hold_button():
+ '''
+ Hold the power button. Got this error message when tried
+ without the TOGGLE attribute:
+ Command without TOGGLE="Yes" attribute is ignored
+ when host power is off. (rc: 0x0054)
+ Didn't find any documentation about TOGGLE.
+ '''
+ if power:
+ req = new_power_req(E_HOLD_BUTTON)
+ else:
+ req = new_power_req(E_HOLD_BUTTON,"TOGGLE","Yes")
+ rc = send_request(req,proc_resp)
+ return rc
+def read_power_state():
+ req = new_power_req(E_GET_PSTATUS)
+ rc = send_request(req,proc_resp)
+ return rc
+
+def reset_power():
+ '''
+ Three methods to reset:
+ - hold power button
+ - reset (only if host has power and user said that reset is ok)
+ - power off/on
+ '''
+ do_power_on = False
+ if power_method == 'button':
+ rc = hold_button()
+ elif reset_ok != '0':
+ if power:
+ return reset()
+ else:
+ return power_on()
+ else:
+ do_power_on = True
+ rc = power_off()
+ if rc == 0:
+ rc = read_power_state()
+ if do_power_on:
+ while rc == 0 and power: # wait for the power state to go off
+ time.sleep(5)
+ rc = read_power_state()
+ if rc == 0 and do_power_on and not power:
+ rc = power_on()
+ return rc
+
+# track state of host power
+power = -1
+
+todo = {
+'reset':reset_power,
+'on':power_on,
+'off':power_off,
+'cold':cold_boot,
+'warm':warm_boot,
+'status':lambda: 0 # just return 0, we already read the state
+}
+
+rc = read_power_state()
+if rc == 0:
+ if cmd in todo:
+ rc = todo[cmd]()
+ else:
+ fatal('Invalid command: %s' % cmd)
+if rc != 0:
+ fatal("request failed")
+sys.exit(rc)
+
+# vi:ts=4:sw=4:et:
diff --git a/lib/plugins/stonith/external/ssh.in b/lib/plugins/stonith/external/ssh.in
new file mode 100644
index 0000000..2a8eb73
--- /dev/null
+++ b/lib/plugins/stonith/external/ssh.in
@@ -0,0 +1,176 @@
+#!/bin/sh
+#
+# External STONITH module for ssh.
+#
+# Copyright (c) 2004 SUSE LINUX AG - Lars Marowsky-Bree <lmb@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+SSH_COMMAND="@SSH@ -q -x -o PasswordAuthentication=no -o StrictHostKeyChecking=no -n -l root"
+#SSH_COMMAND="@SSH@ -q -x -n -l root"
+
+REBOOT_COMMAND="echo 'sleep 2; @REBOOT@ @REBOOT_OPTIONS@' | SHELL=/bin/sh at now >/dev/null 2>&1"
+
+# Warning: If you select this poweroff command, it'll physically
+# power-off the machine, and quite a number of systems won't be remotely
+# revivable.
+# TODO: Probably should touch a file on the server instead to just
+# prevent heartbeat et al from being started after the reboot.
+# POWEROFF_COMMAND="echo 'sleep 2; /sbin/poweroff -nf' | SHELL=/bin/sh at now >/dev/null 2>&1"
+POWEROFF_COMMAND="echo 'sleep 2; @REBOOT@ @REBOOT_OPTIONS@' | SHELL=/bin/sh at now >/dev/null 2>&1"
+
+# Rewrite the hostlist to accept "," as a delimeter for hostnames too.
+hostlist=`echo $hostlist | tr ',' ' '`
+
+is_host_up() {
+ for j in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ do
+ if
+ ping -w1 -c1 "$1" >/dev/null 2>&1
+ then
+ sleep 1
+ else
+ return 1
+ fi
+ done
+ return 0
+}
+
+
+case $1 in
+gethosts)
+ for h in $hostlist ; do
+ echo $h
+ done
+ exit 0
+ ;;
+on)
+ # Can't really be implemented because ssh cannot power on a system
+ # when it is powered off.
+ exit 1
+ ;;
+off)
+ # Shouldn't really be implemented because if ssh cannot power on a
+ # system, it shouldn't be allowed to power it off.
+ exit 1
+ ;;
+reset)
+ h_target=`echo $2 | tr A-Z a-z`
+ for h in $hostlist
+ do
+ h=`echo $h | tr A-Z a-z`
+ [ "$h" != "$h_target" ] &&
+ continue
+ if
+ case ${livedangerously} in
+ [Yy]*) is_host_up $h;;
+ *) true;;
+ esac
+ then
+ $SSH_COMMAND "$2" "$REBOOT_COMMAND"
+ # Good thing this is only for testing...
+ if
+ is_host_up $h
+ then
+ exit 1
+ else
+ exit 0
+ fi
+ else
+ # well... Let's call it successful, after all this is only for testing...
+ exit 0
+ fi
+ done
+ exit 1
+ ;;
+status)
+ if
+ [ -z "$hostlist" ]
+ then
+ exit 1
+ fi
+ for h in $hostlist
+ do
+ if
+ ping -w1 -c1 "$h" 2>&1 | grep "unknown host"
+ then
+ exit 1
+ fi
+ done
+ exit 0
+ ;;
+getconfignames)
+ echo "hostlist"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "ssh STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "ssh STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "ssh-based host reset"
+ echo "Fine for testing, but not suitable for production!"
+ echo "Only reboot action supported, no poweroff, and, surprisingly enough, no poweron."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://openssh.org"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << SSHXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of hosts that the STONITH device controls
+</longdesc>
+</parameter>
+
+<parameter name="livedangerously" unique="0" required="0">
+<content type="enum" />
+<shortdesc lang="en">
+Live Dangerously!!
+</shortdesc>
+<longdesc lang="en">
+Set to "yes" if you want to risk your system's integrity.
+Of course, since this plugin isn't for production, using it
+in production at all is a bad idea. On the other hand,
+setting this parameter to yes makes it an even worse idea.
+Viva la Vida Loca!
+</longdesc>
+</parameter>
+
+</parameters>
+SSHXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/vcenter b/lib/plugins/stonith/external/vcenter
new file mode 100755
index 0000000..71a6302
--- /dev/null
+++ b/lib/plugins/stonith/external/vcenter
@@ -0,0 +1,280 @@
+#!/usr/bin/env perl
+#
+# External STONITH module for VMWare vCenter/ESX
+#
+# Author: Nhan Ngo Dinh
+# License: GNU General Public License (GPL)
+#
+
+require 5.010;
+
+use strict;
+use warnings;
+
+sub dielog {
+ my $msg = "[";
+ $msg .= "$ARGV[0]" if defined($ARGV[0]);
+ $msg .= " $ARGV[1]" if defined($ARGV[1]);
+ $msg .= "]";
+ ( $_ ) = @_;
+ $msg .= " $_";
+ system("ha_log.sh", "err", "$msg");
+ die();
+}
+
+# Define command groups
+my @configCommands = qw{getconfignames getinfo-devid getinfo-devname getinfo-devdescr getinfo-devurl getinfo-xml};
+my @actionCommands = qw{reset on off};
+my @netCommands = (@actionCommands, qw{status gethosts listvms});
+
+# Process command line arguments
+my $command = $ARGV[0] || dielog("No command specified\n");
+
+# Command belongs to the group of commands that do not require any connection to VMware vCenter
+if ($command ~~ @configCommands) {
+ if ($command eq "getconfignames") {
+ print "VI_SERVER\nVI_PORTNUMBER\nVI_PROTOCOL\nVI_SERVICEPATH\nVI_CREDSTORE\nHOSTLIST\nRESETPOWERON\n";
+ }
+ elsif ($command eq "getinfo-devid") {
+ print "VMware vCenter STONITH device\n";
+ }
+ elsif ($command eq "getinfo-devname") {
+ print "VMware vCenter STONITH device\n";
+ }
+ elsif ($command eq "getinfo-devdescr") {
+ print "VMWare vCenter STONITH device\n";
+ }
+ elsif ($command eq "getinfo-devurl") {
+ print "http://www.vmware.com/\n";
+ }
+ elsif ($command eq "getinfo-xml") {
+ print q{<parameters>
+<parameter name="HOSTLIST" required="1">
+<content type="string"/>
+<shortdesc lang="en">List of hosts and virtual machines (required)</shortdesc>
+<longdesc lang="en">
+The list of hosts that the VMware vCenter STONITH device controls.
+Syntax is:
+ hostname1[=VirtualMachineName1];hostname2[=VirtualMachineName2]
+
+NOTE: omit =VirtualMachineName if hostname and virtual machine names are identical
+
+Example:
+ cluster1=VMCL1;cluster2=VMCL2
+</longdesc>
+</parameter>
+<parameter name="VI_SERVER">
+<content type="string" default="localhost"/>
+<shortdesc lang="en">VMware vCenter address</shortdesc>
+<longdesc lang="en">
+The VMware vCenter address
+</longdesc>
+</parameter>
+<parameter name="VI_PROTOCOL">
+<content type="string" default="https"/>
+<shortdesc lang="en">VMware vCenter protocol</shortdesc>
+<longdesc lang="en">
+The VMware vCenter protocol
+</longdesc>
+</parameter>
+<parameter name="VI_PORTNUMBER">
+<content type="string" default="443"/>
+<shortdesc lang="en">VMware vCenter port number</shortdesc>
+<longdesc lang="en">
+The VMware vCenter port number
+</longdesc>
+</parameter>
+<parameter name="VI_SERVICEPATH">
+<content type="string" default="/sdk"/>
+<shortdesc lang="en">VMware vCenter service path</shortdesc>
+<longdesc lang="en">
+The VMware vCenter services path
+</longdesc>
+</parameter>
+<parameter name="VI_CREDSTORE" required="1">
+<content type="string"/>
+<shortdesc lang="en">VMware vCenter credentials store file</shortdesc>
+<longdesc lang="en">
+VMware vCenter credentials store file
+</longdesc>
+</parameter>
+<parameter name="RESETPOWERON">
+<content type="string" default="1"/>
+<shortdesc lang="en">PowerOnVM on reset</shortdesc>
+<longdesc lang="en">
+Enable/disable a PowerOnVM on reset when the target virtual machine is off
+Allowed values: 0, 1
+</longdesc>
+</parameter>
+<parameter name="PERL_LWP_SSL_VERIFY_HOSTNAME">
+<content type="string"/>
+<shortdesc lang="en">Enable or disable SSL hostname verification</shortdesc>
+<longdesc lang="en">
+To disable SSL hostname verification set this option to 0.
+To enable hostname verification, set this option to 1.
+This option is actually part of the LWP Perl library.
+See LWP(3pm) for more information.
+</longdesc>
+</parameter>
+</parameters>} . "\n";
+ }
+ else { dielog("Invalid command specified: $command\n"); }
+}
+
+# Command belongs to the group of commands that require connecting to VMware vCenter
+elsif ($command ~~ @netCommands) {
+
+ eval { require VMware::VIRuntime; }
+ or dielog("Missing perl module VMware::VIRuntime. Download and install 'VMware Infrastructure (VI) Perl Toolkit', available at http://www.vmware.com/support/developer/viperltoolkit/ \n");
+
+ # A valid VI_CREDSTORE is required to avoid interactive prompt
+ ( exists $ENV{'VI_CREDSTORE'} ) || dielog("VI_CREDSTORE not specified\n");
+
+ # HOSTLIST is mandatory
+ exists $ENV{'HOSTLIST'} || dielog("HOSTLIST not specified\n");
+
+ # Parse HOSTLIST to %host_to_vm and %vm_to_host
+ my @hostlist = split(';', $ENV{'HOSTLIST'});
+ my %host_to_vm = ();
+ my %vm_to_host = ();
+ foreach my $host (@hostlist) {
+ my @config = split(/=/, $host);
+ my $key = $config[0]; my $value = $config[1];
+ if (!defined($value)) { $value = $config[0]; }
+ $host_to_vm{$key} = $value;
+ $vm_to_host{(lc $value)} = $key;
+ }
+
+ eval {
+ # VI API: reads options from the environment variables into appropriate data structures for validation.
+ Opts::parse();
+ # VI API: ensures that input values from environment variable are complete, consistent and valid.
+ Opts::validate();
+ # VI API: establishes a session with the VirtualCenter Management Server or ESX Server Web service
+ Util::connect();
+ };
+ if ($@) {
+ # This is just a placeholder for any error handling procedure
+ dielog($@);
+ }
+
+ # Command belongs to the group of commands that performs actions on Virtual Machines
+ if ($command ~~ @actionCommands) {
+
+ my $targetHost = $ARGV[1] || dielog("No target specified\n");
+
+ # Require that specified target host exists in the specified HOSTLIST
+ if (exists $host_to_vm{$targetHost}) {
+
+ my $vm;
+ my $esx;
+ eval {
+ # VI API: searches the inventory tree for a VirtualMachine managed entity whose name matches
+ # the name of the virtual machine assigned to the target host in HOSTLIST
+ $vm = Vim::find_entity_view(view_type => "VirtualMachine", filter => { name => qr/^\Q$host_to_vm{$targetHost}\E/i });
+ if (!defined $vm) {
+ dielog("Machine $targetHost was not found");
+ }
+
+ # VI API: retrieves the properties of the managed object reference runtime.host of the VirtualMachine
+ # managed entity obtained by the previous command
+ # NOTE: This is essentially a workaround to vSphere Perl SDK
+ # to allow pointing to the right HostSystem. This is probably
+ # done by changing the current HostSystem in the Web Service
+ # session context. WARNING: Do not use the same session for any
+ # other concurrent operation.
+ $esx = Vim::get_view(mo_ref => $vm->{"runtime"}{"host"})->name;
+ };
+ if ($@) {
+ if (ref($@) eq "SoapFault") { dielog("$@->detail\n"); }
+ dielog($@);
+ }
+
+ my $powerState = $vm->get_property('runtime.powerState')->val;
+ if ($powerState eq "suspended") {
+ # This implementation assumes that suspending a cluster node can cause
+ # severe failures on shared resources, thus any failover operation should
+ # be blocked.
+ dielog("Machine $esx:$vm->{'name'} is in a suspended state\n");
+ }
+
+ eval {
+ if ($command eq "reset") {
+ if ($powerState eq "poweredOn") {
+ $vm->ResetVM();
+ system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been reset");
+ } else {
+ system("ha_log.sh", "warn", "Tried to ResetVM $esx:$vm->{'name'} that was $powerState");
+ # Start a virtual machine on reset only if explicitly allowed by RESETPOWERON
+ if ($powerState eq "poweredOff" && (! exists $ENV{'RESETPOWERON'} || $ENV{'RESETPOWERON'} ne 0)) {
+ $vm->PowerOnVM();
+ system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been powered on");
+ } else {
+ dielog("Could not complete $esx:$vm->{'name'} power cycle");
+ }
+ }
+ }
+ elsif ($command eq "off") {
+ if ($powerState eq "poweredOn") {
+ $vm->PowerOffVM();
+ system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been powered off");
+ } else {
+ system("ha_log.sh", "warn", "Tried to PowerOffVM $esx:$vm->{'name'} that was $powerState");
+
+ }
+ }
+ elsif ($command eq "on") {
+ if ($powerState eq "poweredOff") {
+ $vm->PowerOnVM();
+ system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been powered on");
+ } else {
+ system("ha_log.sh", "warn", "Tried to PowerOnVM $esx:$vm->{'name'} that was $powerState");
+ }
+ }
+ else { dielog("Invalid command specified: $command\n"); }
+ };
+ if ($@) {
+ if (ref($@) eq "SoapFault") { dielog("$@->detail\n"); }
+ dielog($@);
+ }
+
+ } else { dielog("Invalid target specified\n"); }
+ } else {
+ # Command belongs to the group of commands that lookup the status of VMware vCenter and/or virtual machines
+ if ($command eq "status") {
+ # we already connect to the vcenter, no need to do
+ # anything else in status
+ ;
+ }
+ elsif ($command eq "gethosts") {
+ foreach my $key (keys(%host_to_vm)) {
+ print "$key \n";
+ }
+ }
+ elsif ($command eq "listvms") {
+ eval {
+ # VI API: Searches the inventory tree for all VirtualMachine managed objects
+ my $vms = Vim::find_entity_views(view_type => "VirtualMachine");
+ if (defined $vms) {
+ printf(STDERR "%-50s %-20s\n", "VM Name", "Power state");
+ print STDERR "-" x 70 . "\n";
+ foreach my $vm (@$vms) {
+ my $powerState = $vm->get_property('runtime.powerState')->val;
+ printf("%-50s %-20s\n", $vm->{name}, $powerState);
+ }
+ }
+ };
+ }
+ else { dielog("Invalid command specified: $command\n"); }
+ }
+ eval {
+ Util::disconnect();
+ };
+ if ($@) {
+ # This is just a placeholder for any error handling procedure
+ dielog($@);
+ }
+}
+else { dielog("Invalid command specified: $command\n"); }
+
+exit(0);
diff --git a/lib/plugins/stonith/external/vmware b/lib/plugins/stonith/external/vmware
new file mode 100644
index 0000000..55966ba
--- /dev/null
+++ b/lib/plugins/stonith/external/vmware
@@ -0,0 +1,216 @@
+#!/usr/bin/perl
+# External STONITH module for VMWare Server Guests
+#
+# Copyright (c) 2004 SUSE LINUX AG - Andrew Beekhof <abeekhof@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+sub supply_default
+{
+ my $name = $_[0];
+ my $value = $_[1];
+
+ if ( defined $ENV{$name} ) {
+ #print "Set: $name=$ENV{$name}\n";
+ } else {
+ $ENV{$name} = $value;
+ #print "Default: $name=$ENV{$name}\n";
+ }
+}
+
+sub vmware_command
+{
+ my $config = $_[0];
+ my $action = $_[1];
+ my @lines;
+
+ my $device = $ENV{'device_host'};
+
+ if ( $device =~ /localhost/ ) {
+ @lines = readpipe "vmware-cmd $config $action";
+
+ } else {
+ @lines = readpipe "ssh $device \"vmware-cmd \\\"$config\\\" $action\"";
+ }
+
+ #print @lines;
+ return @lines;
+}
+
+sub is_host_active
+{
+ my $config = config_for_host($_[0]);
+ my @lines = vmware_command($config, "getstate");
+ foreach $line (@lines) {
+ if ( $line =~ /getstate.* = on/ ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+sub supported_hosts
+{
+ my $line;
+ my @lines;
+
+ if ( defined $ENV{'host_map'} ) {
+ @lines = split(/;/, $ENV{'host_map'});
+ foreach $line (@lines){
+ @config = split(/=/, $line);
+ $host = $config[0];
+ if ( is_host_active($host) == 1 ) {
+ print "$host\n";
+ }
+ }
+
+ } else {
+ @lines = vmware_command("-l");
+ foreach $line (@lines){
+ my @elements = split(/\//, $line);
+ $host = $elements[$#elements-1];
+ if ( is_host_active($host) == 1 ) {
+ print "$host\n";
+ }
+ }
+ }
+}
+
+sub config_for_host
+{
+ my $line;
+ my @lines;
+ my $target = $_[0];
+
+ if ( defined $ENV{'host_map'} ) {
+ @lines = split(/;/, $ENV{'host_map'});
+ foreach $line (@lines){
+ if ( $line =~ /^$target=/ ) {
+ @config = split(/=/, $line);
+ return $config[1];
+ }
+ }
+
+ } else {
+ @lines = vmware_command("-l");
+
+ foreach $line (@lines){
+ if ( $line =~ /\/$target\// ) {
+ chop($line);
+ return $line;
+ }
+ }
+ }
+}
+
+$command = $ARGV[0];
+if ( defined $ARGV[1] ) {
+ $targetHost = $ARGV[1];
+}
+
+supply_default("device_host", "localhost");
+
+if ( $command =~ /^gethosts$/ ) {
+ supported_hosts;
+
+} elsif ( $command =~ /^getconfignames$/ ) {
+ print "device_host\n";
+
+} elsif ( $command =~ /^getinfo-devid$/ ) {
+ print "VMware Server STONITH device\n";
+} elsif ( $command =~ /^getinfo-devname$/ ) {
+ print "VMware Server STONITH device\n";
+} elsif ( $command =~ /^getinfo-devdescr$/ ) {
+ print "VMware Server STONITH device\n";
+} elsif ( $command =~ /^getinfo-devurl$/ ) {
+ print "http://www.vmware.com/";
+
+} elsif ( $command =~ /^on$/ ) {
+ $config = config_for_host($targetHost);
+ my @lines = vmware_command($config, "start hard");
+ print @lines;
+
+} elsif ( $command =~ /^off$/ ) {
+ $config = config_for_host($targetHost);
+ my @lines = vmware_command($config, "stop hard");
+ print @lines;
+
+} elsif ( $command =~ /^reset$/ ) {
+ $config = config_for_host($targetHost);
+ my @lines = vmware_command($config, "reset hard");
+ print @lines;
+
+} elsif ( $command =~ /^status$/ ) {
+ my $rc = 7;
+ my $device = $ENV{'device_host'};
+ if ( $device =~ /localhost/ ) {
+ $rc = 0;
+ # TODO: Check for the vmware process
+ print "Local version: always running\n";
+
+ } else {
+ print "Remote version: running ping\n";
+ @lines = readpipe "ping -c1 $device";
+ print @lines;
+
+ foreach $line ( @lines ) {
+ if ( $line =~ /0% packet loss/ ) {
+ $rc = 0;
+ last;
+ }
+ }
+ }
+ exit($rc);
+
+} elsif ( $command =~ /^getinfo-xml$/ ) {
+ $metadata = <<END;
+<parameters>
+<parameter name="host_map" unique="0" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Host Map
+</shortdesc>
+<longdesc lang="en">
+A mapping of hostnames to config paths supported by this device.
+Eg. host1=/config/path/1;host2=/config/path/2;host3=/config/path/3;
+</longdesc>
+</parameter>
+<parameter name="device_host" unique="0" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Device Host
+</shortdesc>
+<longdesc lang="en">
+The machine _hosting_ the virtual machines
+</longdesc>
+</parameter>
+</parameters>
+END
+
+print $metadata;
+
+} else {
+ print "Command $command: not supported\n";
+ exit(3); # Not implemented
+}
+
+
+exit(0);
diff --git a/lib/plugins/stonith/external/xen0 b/lib/plugins/stonith/external/xen0
new file mode 100644
index 0000000..ef1ee40
--- /dev/null
+++ b/lib/plugins/stonith/external/xen0
@@ -0,0 +1,253 @@
+#!/bin/sh
+#
+# External STONITH module for Xen Dom0 through ssh.
+#
+# Description: Uses Xen Dom0 Domain as a STONITH device
+# to control DomUs.
+#
+#
+# Author: Serge Dubrouski (sergeyfd@gmail.com)
+# Inspired by Lars Marowsky-Bree's external/ssh agent.
+#
+# Copyright 2007 Serge Dubrouski <sergeyfd@gmail.com>
+# License: GNU General Public License (GPL)
+#
+
+STOP_COMMAND="xm destroy"
+START_COMMAND="xm create"
+DUMP_COMMAND="xm dump-core"
+DEFAULT_XEN_DIR="/etc/xen"
+SSH_COMMAND="/usr/bin/ssh -q -x -n"
+
+# Rewrite the hostlist to accept "," as a delimeter for hostnames too.
+hostlist=`echo $hostlist | tr ',' ' '`
+
+CheckIfDead() {
+ for j in 1 2 3 4 5
+ do
+ if ! ping -w1 -c1 "$1" >/dev/null 2>&1
+ then
+ return 0
+ fi
+ sleep 1
+ done
+
+ return 1
+}
+
+CheckHostList() {
+ if [ "x" = "x$hostlist" ]
+ then
+ ha_log.sh err "hostlist isn't set"
+ exit 1
+ fi
+}
+
+CheckDom0() {
+ if [ "x" = "x$dom0" ]
+ then
+ ha_log.sh err "dom0 isn't set"
+ exit 1
+ fi
+}
+
+RunCommand() {
+ CheckHostList
+ CheckDom0
+
+ for h in $hostlist
+ do
+ CIFS=$IFS
+ IFS=:
+ read node cfg << -!
+$h
+-!
+ IFS=$CIFS
+
+ if [ "x" = "x$node" ]
+ then
+ ha_log.sh err "Syntax error in host list"
+ exit 1
+ fi
+
+ if [ "x" = "x$cfg" ]
+ then
+ cfg="${DEFAULT_XEN_DIR}/${node}.cfg"
+ fi
+
+ if [ "$node" != "$1" ]
+ then
+ continue
+ fi
+
+ case $2 in
+ stop)
+ kill_node=`$SSH_COMMAND $dom0 "grep ^[[:space:]]*name $cfg" | cut -f 2 -d '=' | sed -e 's,",,g'`
+ if [ "x" = "x$kill_node" ]
+ then
+ ha_log.sh err "Couldn't find a node name to stop"
+ exit 1
+ fi
+
+ if [ "x$run_dump" != "x" ]
+ then
+ #Need to run core dump
+ if [ "x$dump_dir" != "x" ]
+ then
+ #Dump with the specified core file
+ TIMESTAMP=`date +%Y-%m%d-%H%M.%S`
+ DOMAINNAME=`printf "%s" $kill_node`
+ COREFILE=$dump_dir/$TIMESTAMP-$DOMAINNAME.core
+ $SSH_COMMAND $dom0 "(mkdir -p $dump_dir; $DUMP_COMMAND $kill_node $COREFILE) >/dev/null 2>&1"
+ else
+ $SSH_COMMAND $dom0 "$DUMP_COMMAND $kill_node >/dev/null 2>&1"
+ fi
+ fi
+ $SSH_COMMAND $dom0 "(sleep 2; $STOP_COMMAND $kill_node) >/dev/null 2>&1 &"
+ break;;
+ start)
+ $SSH_COMMAND $dom0 "(sleep 2; $START_COMMAND $cfg) >/dev/null 2>&1 &"
+ break;;
+ esac
+ exit 0
+ done
+}
+
+
+# Main code
+
+case $1 in
+gethosts)
+ CheckHostList
+
+ for h in $hostlist ; do
+ CIFS=$IFS
+ IFS=:
+ read node cfg << -!
+$h
+-!
+ IFS=$CIFS
+
+ echo $node
+ done
+ exit 0
+ ;;
+on)
+ RunCommand $2 start
+ exit $?
+ ;;
+off)
+ if RunCommand $2 stop
+ then
+ if CheckIfDead $2
+ then
+ exit 0
+ fi
+ fi
+
+ exit 1
+ ;;
+reset)
+ RunCommand $2 stop
+
+ if CheckIfDead $2
+ then
+ RunCommand $2 start
+ exit 0
+ fi
+
+ exit 1
+ ;;
+status)
+ CheckHostList
+
+ for h in $hostlist
+ do
+ CIFS=$IFS
+ IFS=:
+ read node cfg << -!
+$h
+-!
+ IFS=$CIFS
+
+ echo $node
+ if ping -w1 -c1 "$node" 2>&1 | grep "unknown host"
+ then
+ exit 1
+ fi
+ done
+ exit 0
+ ;;
+getconfignames)
+ echo "hostlist dom0"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "xen0 STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "xen0 STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "ssh-based host reset for Xen DomU trough Dom0"
+ echo "Fine for testing, but not really suitable for production!"
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://openssh.org http://www.xensource.com/ http://linux-ha.org/wiki"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << SSHXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of controlled nodes in a format node[:config_file].
+For example: "node1:/opt/xen/node1.cfg node2"
+If config file isn't set it defaults to /etc/xen/{node_name}.cfg
+</longdesc>
+</parameter>
+<parameter name="dom0" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Dom0
+</shortdesc>
+<longdesc lang="en">
+Name of the Dom0 Xen node. Root user shall be able to ssh to that node.
+</longdesc>
+</parameter>
+<parameter name="run_dump" unique="0" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Run dump-core
+</shortdesc>
+<longdesc lang="en">
+If set plugin will call "xm dump-core" before killing DomU
+</longdesc>
+</parameter>
+<parameter name="dump_dir" unique="1" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Run dump-core with the specified directory
+</shortdesc>
+<longdesc lang="en">
+This parameter can indicate the dump destination.
+Should be set as a full path format, ex.) "/var/log/dump"
+The above example would dump the core, like;
+/var/log/dump/2009-0316-1403.37-domU.core
+</longdesc>
+</parameter>
+</parameters>
+SSHXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/xen0-ha-dom0-stonith-helper b/lib/plugins/stonith/external/xen0-ha-dom0-stonith-helper
new file mode 100755
index 0000000..b313f8b
--- /dev/null
+++ b/lib/plugins/stonith/external/xen0-ha-dom0-stonith-helper
@@ -0,0 +1,72 @@
+#!/bin/bash
+# Author: Lars Marowsky-Bree
+#
+# Copyright 2008 Lars Marowsky-Bree
+# License: GNU General Public License (GPL)
+
+# This is not an external/stonith plugin by itself, but instead a helper
+# script which gets installed in Dom0.
+
+# TODO:
+# - Error handling
+# - How to handle if the DomU resource doesn't exist?
+# - Does this truly work with split-brain?
+# - Is the handling of non-existent resources adequate?
+# ...
+# Basically: more testing. This is proof-of-concept and works, but deserves
+# validation.
+
+CMD="$1"
+DOMU="$2"
+TIMEOUT="$3"
+
+# Make sure the timeout is an integer:
+if [ "0$TIMEOUT" -eq 0 ]; then
+ TIMEOUT=300
+fi
+
+SetTargetRole() {
+ local new_role="$1"
+ crm_resource -r $DOMU --meta -p target_role -v $new_role
+
+ local timeout="$TIMEOUT"
+
+ # We only need to wait for "stopped".
+ if [ "$new_role" != "stopped" ]; then
+ return 0
+ fi
+
+ while [ $timeout -gt 0 ]; do
+ local rc
+ crm_resource -W -r $DOMU 2>&1 | grep -q "is NOT running"
+ rc=$?
+ if [ $rc -eq 0 ]; then
+ return 0
+ fi
+ timeout=$[timeout-1];
+ sleep 1
+ done
+ return 1
+}
+
+
+case $CMD in
+on) SetTargetRole started
+ exit $?
+ ;;
+off) SetTargetRole stopped
+ exit $?
+ ;;
+reset) SetTargetRole stopped || exit 1
+ SetTargetRole started
+ exit $?
+ ;;
+status) exit 0
+ ;;
+*) ha_log.sh err "Called with unknown command: $CMD"
+ exit 1
+ ;;
+esac
+
+exit 1
+
diff --git a/lib/plugins/stonith/external/xen0-ha.in b/lib/plugins/stonith/external/xen0-ha.in
new file mode 100755
index 0000000..cb42cbc
--- /dev/null
+++ b/lib/plugins/stonith/external/xen0-ha.in
@@ -0,0 +1,96 @@
+#!/bin/bash
+#
+# This STONITH script integrates a cluster running within DomUs
+# with the CRM/Pacemaker cluster running in Dom0.
+#
+# Author: Lars Marowsky-Bree
+# Copyright: 2008 Lars Marowsky-Bree
+# License: GNU General Public License (GPL)
+#
+
+SSH_COMMAND="@SSH@ -q -x -n"
+HVM_HELPER="@stonith_plugindir@/xen0-ha-dom0-stonith-helper"
+
+# Rewrite the hostlist to accept "," as a delimeter for hostnames too.
+hostlist=`echo $hostlist | tr ',' ' '`
+
+# Runs a command on the host, waiting for it to return
+RunHVMCommand() {
+ $SSH_COMMAND $dom0_cluster_ip "$HVM_HELPER $1 $2 $stop_timeout"
+}
+
+# Main code
+case $1 in
+gethosts)
+ echo $hostlist
+ exit 0
+ ;;
+on|off|reset|status)
+ RunHVMCommand $1 $2
+ exit $?
+ ;;
+getconfignames)
+ echo "hostlist dom0_cluster_ip timeout"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "xen0-ha DomU/Dom0 device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "xen0-ha DomU/Dom0 external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "Allows STONITH to control DomUs managed by a CRM/Pacemaker Dom0."
+ echo "Requires Xen + CRM/Pacemaker at both layers."
+ echo "Proof-of-concept code!"
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://linux-ha.org/wiki/DomUClusters"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << SSHXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of controlled DomUs, separated by whitespace.
+These must be configured as Xen RA resources with a name with a matching
+id.
+For example: "xen-1 xen-2 xen-3"
+</longdesc>
+</parameter>
+<parameter name="dom0_cluster_ip" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Dom0 cluster ip
+</shortdesc>
+<longdesc lang="en">
+The cluster IP address associated with Dom0.
+Root user must be able to ssh to that node.
+</longdesc>
+</parameter>
+<parameter name="stop_timeout">
+<content type="integer" />
+<shortdesc lang="en">
+Stop timeout
+</shortdesc>
+<longdesc lang="en">
+The timeout, in seconds, for which to wait for Dom0 to report that the
+DomU has been stopped, before aborting with a failure.
+</longdesc>
+</parameter>
+</parameters>
+SSHXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/ibmhmc.c b/lib/plugins/stonith/ibmhmc.c
new file mode 100644
index 0000000..d33fea9
--- /dev/null
+++ b/lib/plugins/stonith/ibmhmc.c
@@ -0,0 +1,1261 @@
+/*
+ * Stonith module for IBM Hardware Management Console (HMC)
+ *
+ * Author: Huang Zhen <zhenh@cn.ibm.com>
+ * Support for HMC V4+ added by Dave Blaschke <debltc@us.ibm.com>
+ *
+ * Copyright (c) 2004 International Business Machines
+ *
+ * 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
+ *
+ */
+
+/*
+ *
+ * This code has been tested in following environment:
+ *
+ * Hardware Management Console (HMC): Release 3, Version 2.4
+ * - Both FullSystemPartition and LPAR Partition:
+ * - p630 7028-6C4 two LPAR partitions
+ * - p650 7038-6M2 one LPAR partition and FullSystemPartition
+ *
+ * Hardware Management Console (HMC): Version 4, Release 2.1
+ * - OP720 1000-6CA three LPAR partitions
+ *
+ * Note: Only SSH access to the HMC devices are supported.
+ *
+ * This command would make a nice status command:
+ *
+ * lshmc -r -F ssh
+ *
+ * The following V3 command will get the list of systems we control and their
+ * mode:
+ *
+ * lssyscfg -r sys -F name:mode --all
+ *
+ * 0 indicates full system partition
+ * 255 indicates the system is partitioned
+ *
+ * The following V4 command will get the list of systems we control:
+ *
+ * lssyscfg -r sys -F name
+ *
+ * The following V3 command will get the list of partitions for a given managed
+ * system running partitioned:
+ *
+ * lssyscfg -m managed-system -r lpar -F name --all
+ *
+ * Note that we should probably only consider partitions whose boot mode
+ * is normal (1). (that's my guess, anyway...)
+ *
+ * The following V4 command will get the list of partitions for a given managed
+ * system running partitioned:
+ *
+ * lssyscfg -m managed-system -r lpar -F name
+ *
+ * The following V3 commands provide the reset/on/off actions:
+ *
+ * FULL SYSTEM:
+ * on: chsysstate -m %1 -r sys -o on -n %1 -c full
+ * off: chsysstate -m %1 -r sys -o off -n %1 -c full -b norm
+ * reset:chsysstate -m %1 -r sys -o reset -n %1 -c full -b norm
+ *
+ * Partitioned SYSTEM:
+ * on: chsysstate -m %1 -r lpar -o on -n %2
+ * off: reset_partition -m %1 -p %2 -t hard
+ * reset:do off action above, followed by on action...
+ *
+ * where %1 is managed-system, %2 is-lpar name
+ *
+ * The following V4 commands provide the reset/on/off actions:
+ *
+ * on: chsysstate -m %1 -r lpar -o on -n %2 -f %3
+ * off: chsysstate -m %1 -r lpar -o shutdown -n %2 --immed
+ * reset:chsysstate -m %1 -r lpar -o shutdown -n %2 --immed --restart
+ *
+ * where %1 is managed-system, %2 is lpar-name, %3 is profile-name
+ *
+ * Of course, to do all this, we need to track which partition name goes with
+ * which managed system's name, and which systems on the HMC are partitioned
+ * and which ones aren't...
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "IBM HMC"
+
+#include "stonith_plugin_common.h"
+
+#ifndef SSH_CMD
+# define SSH_CMD "ssh"
+#endif
+#ifndef HMCROOT
+# define HMCROOT "hscroot"
+#endif
+
+#define PIL_PLUGIN ibmhmc
+#define PIL_PLUGIN_S "ibmhmc"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#define MAX_HOST_NAME_LEN (256*4)
+#define MAX_CMD_LEN 2048
+#define FULLSYSTEMPARTITION "FullSystemPartition"
+#define MAX_POWERON_RETRY 10
+#define MAX_HMC_NAME_LEN 256
+
+#define ST_MANSYSPAT "managedsyspat"
+#define NOPASS "nopass"
+
+#define STATE_UNKNOWN -1
+#define STATE_OFF 0
+#define STATE_ON 1
+#define STATE_INVALID 2
+
+#define HMCURL "http://publib-b.boulder.ibm.com/redbooks.nsf/RedbookAbstracts"\
+ "/SG247038.html"
+
+static StonithPlugin * ibmhmc_new(const char *);
+static void ibmhmc_destroy(StonithPlugin *);
+static const char * ibmhmc_getinfo(StonithPlugin * s, int InfoType);
+static const char * const * ibmhmc_get_confignames(StonithPlugin* p);
+static int ibmhmc_status(StonithPlugin * );
+static int ibmhmc_reset_req(StonithPlugin * s,int request,const char* host);
+static char ** ibmhmc_hostlist(StonithPlugin *);
+static int ibmhmc_set_config(StonithPlugin *, StonithNVpair*);
+
+static struct stonith_ops ibmhmcOps = {
+ ibmhmc_new, /* Create new STONITH object */
+ ibmhmc_destroy, /* Destroy STONITH object */
+ ibmhmc_getinfo, /* Return STONITH info string */
+ ibmhmc_get_confignames, /* Return configuration parameters */
+ ibmhmc_set_config, /* Set configuration */
+ ibmhmc_status, /* Return STONITH device status */
+ ibmhmc_reset_req, /* Request a reset */
+ ibmhmc_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &ibmhmcOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ char * idinfo;
+ char * hmc;
+ GList* hostlist;
+ int hmcver;
+ char * password;
+ char ** mansyspats;
+};
+
+static const char * pluginid = "HMCDevice-Stonith";
+static const char * NOTpluginID = "IBM HMC device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_MANSYSPAT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_MANSYSPAT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_MANSYSPAT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "White-space delimited list of patterns used to match managed system names; if last character is '*', all names that begin with the pattern are matched" \
+ XML_PARM_LONGDESC_END
+
+#define XML_MANSYSPAT_PARM \
+ XML_PARAMETER_BEGIN(ST_MANSYSPAT, "string", "0", "0") \
+ XML_MANSYSPAT_SHORTDESC \
+ XML_MANSYSPAT_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_OPTPASSWD_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "Password for " HMCROOT " if passwordless ssh access to HMC has NOT been setup (to do so, it is necessary to create a public/private key pair with empty passphrase - see \"Configure the OpenSSH Client\" in the redbook at " HMCURL " for more details)" \
+ XML_PARM_LONGDESC_END
+
+#define XML_OPTPASSWD_PARM \
+ XML_PARAMETER_BEGIN(ST_PASSWD, "string", "0", "0") \
+ XML_PASSWD_SHORTDESC \
+ XML_OPTPASSWD_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *ibmhmcXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_MANSYSPAT_PARM
+ XML_OPTPASSWD_PARM
+ XML_PARAMETERS_END;
+
+static int get_hmc_hostlist(struct pluginDevice* dev);
+static void free_hmc_hostlist(struct pluginDevice* dev);
+static int get_hmc_mansyspats(struct pluginDevice* dev, const char* mansyspats);
+static void free_hmc_mansyspats(struct pluginDevice* dev);
+static char* do_shell_cmd(const char* cmd, int* status, const char* password);
+static int check_hmc_status(struct pluginDevice* dev);
+static int get_num_tokens(char *str);
+static gboolean pattern_match(char **patterns, char *string);
+/* static char* do_shell_cmd_fake(const char* cmd, int* status); */
+
+static int
+ibmhmc_status(StonithPlugin *s)
+{
+ struct pluginDevice* dev = NULL;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ dev = (struct pluginDevice*) s;
+
+ return check_hmc_status(dev);
+}
+
+
+/*
+ * Return the list of hosts configured for this HMC device
+ */
+
+static char **
+ibmhmc_hostlist(StonithPlugin *s)
+{
+ int j;
+ struct pluginDevice* dev;
+ int numnames = 0;
+ char** ret = NULL;
+ GList* node = NULL;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+ dev = (struct pluginDevice*) s;
+
+ /* refresh the hostlist */
+ free_hmc_hostlist(dev);
+ if (S_OK != get_hmc_hostlist(dev)){
+ LOG(PIL_CRIT, "unable to obtain list of managed systems in %s"
+ , __FUNCTION__);
+ return NULL;
+ }
+
+ numnames = g_list_length(dev->hostlist);
+ if (numnames < 0) {
+ LOG(PIL_CRIT, "unconfigured stonith object in %s"
+ , __FUNCTION__);
+ return(NULL);
+ }
+
+ ret = (char **)MALLOC((numnames+1)*sizeof(char*));
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return ret;
+ }
+
+ memset(ret, 0, (numnames+1)*sizeof(char*));
+ for (node = g_list_first(dev->hostlist), j = 0
+ ; NULL != node
+ ; j++, node = g_list_next(node)) {
+ char* host = strchr((char*)node->data, '/');
+ ret[j] = STRDUP(++host);
+ if (ret[j] == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ stonith_free_hostlist(ret);
+ return NULL;
+ }
+ strdown(ret[j]);
+ }
+ return ret;
+}
+
+
+static const char * const *
+ibmhmc_get_confignames(StonithPlugin* p)
+{
+ static const char * names[] = {ST_IPADDR, NULL};
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ return names;
+}
+
+
+/*
+ * Reset the given host, and obey the request type.
+ * We should reset without power cycle for the non-partitioned case
+ */
+
+static int
+ibmhmc_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ GList* node = NULL;
+ struct pluginDevice* dev = NULL;
+ char off_cmd[MAX_CMD_LEN];
+ char on_cmd[MAX_CMD_LEN];
+ char reset_cmd[MAX_CMD_LEN];
+ gchar** names = NULL;
+ int i;
+ int is_lpar = FALSE;
+ int status;
+ char* pch;
+ char* output = NULL;
+ char state_cmd[MAX_CMD_LEN];
+ int state = STATE_UNKNOWN;
+
+ status = 0;
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, host=%s\n", __FUNCTION__, host);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ if (NULL == host) {
+ LOG(PIL_CRIT, "invalid argument to %s", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ dev = (struct pluginDevice*) s;
+
+ for (node = g_list_first(dev->hostlist)
+ ; NULL != node
+ ; node = g_list_next(node)) {
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: node->data=%s\n"
+ , __FUNCTION__, (char*)node->data);
+ }
+
+ if ((pch = strchr((char*)node->data, '/')) != NULL
+ && 0 == strcasecmp(++pch, host)) {
+ break;
+ }
+ }
+
+ if (!node) {
+ LOG(PIL_CRIT
+ , "Host %s is not configured in this STONITH module. "
+ "Please check your configuration information.", host);
+ return (S_OOPS);
+ }
+
+ names = g_strsplit((char*)node->data, "/", 2);
+ /* names[0] will be the name of managed system */
+ /* names[1] will be the name of the lpar partition */
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: names[0]=%s, names[1]=%s\n"
+ , __FUNCTION__, names[0], names[1]);
+ }
+
+ if (dev->hmcver < 4) {
+ if (0 == strcasecmp(names[1], FULLSYSTEMPARTITION)) {
+ is_lpar = FALSE;
+
+ snprintf(off_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -r sys -m %s -o off -n %s -c full"
+ , dev->hmc, dev->hmc, names[0]);
+
+ snprintf(on_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -r sys -m %s -o on -n %s -c full -b norm"
+ , dev->hmc, names[0], names[0]);
+
+ snprintf(reset_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -r sys -m %s -o reset -n %s -c full -b norm"
+ , dev->hmc, names[0], names[0]);
+
+ *state_cmd = 0;
+ }else{
+ is_lpar = TRUE;
+
+ snprintf(off_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s reset_partition"
+ " -m %s -p %s -t hard"
+ , dev->hmc, names[0], names[1]);
+
+ snprintf(on_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -r lpar -m %s -o on -n %s"
+ , dev->hmc, names[0], names[1]);
+
+ *reset_cmd = 0;
+
+ snprintf(state_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lssyscfg"
+ " -r lpar -m %s -F state -n %s"
+ , dev->hmc, names[0], names[1]);
+ }
+ }else{
+ is_lpar = TRUE;
+
+ snprintf(off_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -m %s -r lpar -o shutdown -n \"%s\" --immed"
+ , dev->hmc, names[0], names[1]);
+
+ snprintf(on_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lssyscfg"
+ " -m %s -r lpar -F \"default_profile\""
+ " --filter \"lpar_names=%s\""
+ , dev->hmc, names[0], names[1]);
+
+ output = do_shell_cmd(on_cmd, &status, dev->password);
+ if (output == NULL) {
+ LOG(PIL_CRIT, "command %s failed", on_cmd);
+ return (S_OOPS);
+ }
+ if ((pch = strchr(output, '\n')) != NULL) {
+ *pch = 0;
+ }
+ snprintf(on_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -m %s -r lpar -o on -n %s -f %s"
+ , dev->hmc, names[0], names[1], output);
+ FREE(output);
+ output = NULL;
+
+ snprintf(reset_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -m %s -r lpar -o shutdown -n %s --immed --restart"
+ , dev->hmc, names[0], names[1]);
+
+ snprintf(state_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lssyscfg"
+ " -m %s -r lpar -F state --filter \"lpar_names=%s\""
+ , dev->hmc, names[0], names[1]);
+ }
+ g_strfreev(names);
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: off_cmd=%s, on_cmd=%s,"
+ "reset_cmd=%s, state_cmd=%s\n"
+ , __FUNCTION__, off_cmd, on_cmd, reset_cmd, state_cmd);
+ }
+
+ output = do_shell_cmd(state_cmd, &status, dev->password);
+ if (output == NULL) {
+ LOG(PIL_CRIT, "command %s failed", on_cmd);
+ return S_OOPS;
+ }
+ if ((pch = strchr(output, '\n')) != NULL) {
+ *pch = 0;
+ }
+ if (strcmp(output, "Running") == 0
+ || strcmp(output, "Starting") == 0
+ || strcmp(output, "Open Firmware") == 0) {
+ state = STATE_ON;
+ }else if (strcmp(output, "Shutting Down") == 0
+ || strcmp(output, "Not Activated") == 0
+ || strcmp(output, "Ready") == 0) {
+ state = STATE_OFF;
+ }else if (strcmp(output, "Not Available") == 0
+ || strcmp(output, "Error") == 0) {
+ state = STATE_INVALID;
+ }
+ FREE(output);
+ output = NULL;
+
+ if (state == STATE_INVALID) {
+ LOG(PIL_CRIT, "host %s in invalid state", host);
+ return S_OOPS;
+ }
+
+ switch (request) {
+ case ST_POWERON:
+ if (state == STATE_ON) {
+ LOG(PIL_INFO, "host %s already on", host);
+ return S_OK;
+ }
+
+ output = do_shell_cmd(on_cmd, &status, dev->password);
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s failed", on_cmd);
+ return S_OOPS;
+ }
+ break;
+ case ST_POWEROFF:
+ if (state == STATE_OFF) {
+ LOG(PIL_INFO, "host %s already off", host);
+ return S_OK;
+ }
+
+ output = do_shell_cmd(off_cmd, &status, dev->password);
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s failed", off_cmd);
+ return S_OOPS;
+ }
+ break;
+ case ST_GENERIC_RESET:
+ if (dev->hmcver < 4) {
+ if (is_lpar) {
+ if (state == STATE_ON) {
+ output = do_shell_cmd(off_cmd
+ , &status, dev->password);
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s "
+ "failed", off_cmd);
+ return S_OOPS;
+ }
+ }
+ for (i = 0; i < MAX_POWERON_RETRY; i++) {
+ char *output2;
+ output2 = do_shell_cmd(on_cmd
+ , &status, dev->password);
+ if (output2 != NULL) {
+ FREE(output2);
+ }
+ if (0 != status) {
+ sleep(1);
+ }else{
+ break;
+ }
+ }
+ if (MAX_POWERON_RETRY == i) {
+ LOG(PIL_CRIT, "command %s failed"
+ , on_cmd);
+ return S_OOPS;
+ }
+ }else{
+ output = do_shell_cmd(reset_cmd
+ , &status, dev->password);
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s failed" , reset_cmd);
+ return S_OOPS;
+ }
+ break;
+ }
+ }else{
+ if (state == STATE_ON) {
+ output = do_shell_cmd(reset_cmd
+ , &status, dev->password);
+ }else{
+ output = do_shell_cmd(on_cmd
+ , &status, dev->password);
+ }
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s failed", reset_cmd);
+ return S_OOPS;
+ }
+ }
+ break;
+ default:
+ return S_INVAL;
+ }
+
+ if (output != NULL) {
+ FREE(output);
+ }
+
+ LOG(PIL_INFO, "Host %s %s %d.", host, __FUNCTION__, request);
+
+ return S_OK;
+}
+
+
+/*
+ * Parse the information in the given configuration file,
+ * and stash it away...
+ */
+
+static int
+ibmhmc_set_config(StonithPlugin * s, StonithNVpair* list)
+{
+ struct pluginDevice* dev = NULL;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+ char get_hmcver[MAX_CMD_LEN];
+ char firstchar;
+ int firstnum;
+ char* output = NULL;
+ int status;
+ const char *mansyspats;
+ int len;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__);
+ }
+
+ dev = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: ipaddr=%s\n", __FUNCTION__
+ , namestocopy[0].s_value);
+ }
+
+ if (get_num_tokens(namestocopy[0].s_value) == 1) {
+ /* name=value pairs on command line, look for managedsyspat */
+ mansyspats = OurImports->GetValue(list, ST_MANSYSPAT);
+ if (mansyspats != NULL) {
+ if (get_hmc_mansyspats(dev, mansyspats) != S_OK) {
+ FREE(namestocopy[0].s_value);
+ return S_OOPS;
+ }
+ }
+ /* look for password */
+ dev->password = STRDUP(OurImports->GetValue(list, ST_PASSWD));
+ dev->hmc = namestocopy[0].s_value;
+ }else{
+ /* -p or -F option with args "ipaddr [managedsyspat]..." */
+ char *pch = namestocopy[0].s_value;
+
+ /* skip over ipaddr and null-terminate */
+ pch += strcspn(pch, WHITESPACE);
+ *pch = EOS;
+
+ /* skip over white-space up to next token */
+ pch++;
+ pch += strspn(pch, WHITESPACE);
+ if (get_hmc_mansyspats(dev, pch) != S_OK) {
+ FREE(namestocopy[0].s_value);
+ return S_OOPS;
+ }
+
+ dev->hmc = STRDUP(namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ }
+
+ /* check whether the HMC has ssh command enabled */
+ if (check_hmc_status(dev) != S_OK) {
+ LOG(PIL_CRIT, "HMC %s does not have remote "
+ "command execution using the ssh facility enabled", dev->hmc);
+ return S_BADCONFIG;
+ }
+
+ /* get the HMC's version info */
+ snprintf(get_hmcver, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lshmc -v | grep RM", dev->hmc);
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: get_hmcver=%s", __FUNCTION__, get_hmcver);
+ }
+
+ output = do_shell_cmd(get_hmcver, &status, dev->password);
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: output=%s\n", __FUNCTION__
+ , output ? output : "(nil)");
+ }
+ if (output == NULL) {
+ return S_BADCONFIG;
+ }
+
+ /* parse the HMC's version info (i.e. "*RM V4R2.1" or "*RM R3V2.6") */
+ if ((sscanf(output, "*RM %c%1d", &firstchar, &firstnum) == 2)
+ && ((firstchar == 'V') || (firstchar == 'R'))) {
+ dev->hmcver = firstnum;
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: HMC %s version is %d"
+ , __FUNCTION__, dev->hmc, dev->hmcver);
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: unable to determine HMC %s version"
+ , __FUNCTION__, dev->hmc);
+ FREE(output);
+ return S_BADCONFIG;
+ }
+
+ len = strlen(output+4) + sizeof(DEVICE) + 1;
+ if (dev->idinfo != NULL) {
+ FREE(dev->idinfo);
+ dev->idinfo = NULL;
+ }
+ dev->idinfo = MALLOC(len * sizeof(char));
+ if (dev->idinfo == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ FREE(output);
+ return S_OOPS;
+ }
+ snprintf(dev->idinfo, len, "%s %s", DEVICE, output+4);
+ FREE(output);
+
+ if (S_OK != get_hmc_hostlist(dev)){
+ LOG(PIL_CRIT, "unable to obtain list of managed systems in %s"
+ , __FUNCTION__);
+ return S_BADCONFIG;
+ }
+
+ return S_OK;
+}
+
+
+static const char*
+ibmhmc_getinfo(StonithPlugin* s, int reqtype)
+{
+ struct pluginDevice* dev;
+ const char* ret;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ dev = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = dev->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = dev->hmc;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "IBM Hardware Management Console (HMC)\n"
+ "Use for IBM i5, p5, pSeries and OpenPower systems "
+ "managed by HMC\n"
+ " Optional parameter name " ST_MANSYSPAT " is "
+ "white-space delimited list of\n"
+ "patterns used to match managed system names; if last "
+ "character is '*',\n"
+ "all names that begin with the pattern are matched\n"
+ " Optional parameter name " ST_PASSWD " is password "
+ "for " HMCROOT " if passwordless\n"
+ "ssh access to HMC has NOT been setup (to do so, it "
+ "is necessary to create\n"
+ "a public/private key pair with empty passphrase - "
+ "see \"Configure the\n"
+ "OpenSSH client\" in the redbook for more details)";
+ break;
+
+ case ST_DEVICEURL:
+ ret = HMCURL;
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = ibmhmcXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+
+/*
+ * HMC Stonith destructor...
+ */
+
+static void
+ibmhmc_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* dev;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s : called\n", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ dev = (struct pluginDevice *)s;
+
+ dev->pluginid = NOTpluginID;
+ if (dev->hmc) {
+ FREE(dev->hmc);
+ dev->hmc = NULL;
+ }
+ if (dev->password) {
+ FREE(dev->password);
+ dev->password = NULL;
+ }
+ if (dev->idinfo) {
+ FREE(dev->idinfo);
+ dev->idinfo = NULL;
+ }
+ free_hmc_hostlist(dev);
+ free_hmc_mansyspats(dev);
+
+ FREE(dev);
+}
+
+
+static StonithPlugin *
+ibmhmc_new(const char *subplugin)
+{
+ struct pluginDevice* dev = ST_MALLOCT(struct pluginDevice);
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__);
+ }
+
+ if (dev == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return(NULL);
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ dev->pluginid = pluginid;
+ dev->hmc = NULL;
+ dev->password = NULL;
+ dev->hostlist = NULL;
+ dev->mansyspats = NULL;
+ dev->hmcver = -1;
+ REPLSTR(dev->idinfo, DEVICE);
+ if (dev->idinfo == NULL) {
+ FREE(dev);
+ return(NULL);
+ }
+ dev->sp.s_ops = &ibmhmcOps;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: returning successfully\n", __FUNCTION__);
+ }
+
+ return((void *)dev);
+}
+
+static int
+get_hmc_hostlist(struct pluginDevice* dev)
+{
+ int i, j, status;
+ char* output = NULL;
+ char get_syslist[MAX_CMD_LEN];
+ char host[MAX_HOST_NAME_LEN];
+ gchar** syslist = NULL;
+ gchar** name_mode = NULL;
+ char get_lpar[MAX_CMD_LEN];
+ gchar** lparlist = NULL;
+ char* pch;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, dev->hmc=%s\n", __FUNCTION__
+ , dev->hmc);
+ }
+
+ if (dev->hmc == NULL || *dev->hmc == 0){
+ return S_BADCONFIG;
+ }
+
+ /* get the managed system's names of the hmc */
+ if (dev->hmcver < 4) {
+ snprintf(get_syslist, MAX_CMD_LEN, SSH_CMD " -l " HMCROOT
+ " %s lssyscfg -r sys -F name:mode --all", dev->hmc);
+ }else{
+ snprintf(get_syslist, MAX_CMD_LEN, SSH_CMD
+ " -l " HMCROOT " %s lssyscfg -r sys -F name", dev->hmc);
+ }
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: get_syslist=%s", __FUNCTION__, get_syslist);
+ }
+
+ output = do_shell_cmd(get_syslist, &status, dev->password);
+ if (output == NULL) {
+ return S_BADCONFIG;
+ }
+ syslist = g_strsplit(output, "\n", 0);
+ FREE(output);
+
+ /* for each managed system */
+ for (i = 0; syslist[i] != NULL && syslist[i][0] != 0; i++) {
+ if (dev->hmcver < 4) {
+ name_mode = g_strsplit(syslist[i], ":", 2);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: name_mode0=%s, name_mode1=%s\n"
+ , __FUNCTION__, name_mode[0], name_mode[1]);
+ }
+
+ if (dev->mansyspats != NULL
+ && !pattern_match(dev->mansyspats, name_mode[0])) {
+ continue;
+ }
+
+ /* if it is in fullsystempartition */
+ if (NULL != name_mode[1]
+ && 0 == strncmp(name_mode[1], "0", 1)) {
+ /* add the FullSystemPartition */
+ snprintf(host, MAX_HOST_NAME_LEN
+ , "%s/FullSystemPartition", name_mode[0]);
+ dev->hostlist = g_list_append(dev->hostlist
+ , STRDUP(host));
+ }else if (NULL != name_mode[1]
+ && 0 == strncmp(name_mode[1], "255", 3)){
+ /* get its lpars */
+ snprintf(get_lpar, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT
+ " %s lssyscfg -m %s -r lpar -F name --all"
+ , dev->hmc, name_mode[0]);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: get_lpar=%s\n"
+ , __FUNCTION__, get_lpar);
+ }
+
+ output = do_shell_cmd(get_lpar
+ , &status, dev->password);
+ if (output == NULL) {
+ g_strfreev(name_mode);
+ g_strfreev(syslist);
+ return S_BADCONFIG;
+ }
+ lparlist = g_strsplit(output, "\n", 0);
+ FREE(output);
+
+ /* for each lpar */
+ for (j = 0
+ ; NULL != lparlist[j] && 0 != lparlist[j][0]
+ ; j++) {
+ /* skip the full system partition */
+ if (0 == strncmp(lparlist[j]
+ , FULLSYSTEMPARTITION
+ , strlen(FULLSYSTEMPARTITION))) {
+ continue;
+ }
+ /* add the lpar */
+ snprintf(host, MAX_HOST_NAME_LEN
+ , "%s/%s", name_mode[0]
+ , lparlist[j]);
+ dev->hostlist =
+ g_list_append(dev->hostlist
+ , STRDUP(host));
+ }
+ g_strfreev(lparlist);
+ }
+ g_strfreev(name_mode);
+ }else{
+ if (dev->mansyspats != NULL
+ && !pattern_match(dev->mansyspats, syslist[i])) {
+ continue;
+ }
+
+ /* get its state */
+ snprintf(get_lpar, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT
+ " %s lssyscfg -m %s -r sys -F state"
+ , dev->hmc, syslist[i]);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: get_lpar=%s\n"
+ , __FUNCTION__, get_lpar);
+ }
+
+ output = do_shell_cmd(get_lpar, &status, dev->password);
+ if (output == NULL) {
+ g_strfreev(syslist);
+ return S_BADCONFIG;
+ }
+ if ((pch = strchr(output, '\n')) != NULL) {
+ *pch = 0;
+ }
+ if (!strcmp(output, "No Connection")){
+ FREE(output);
+ continue;
+ }
+ FREE(output);
+
+ /* get its lpars */
+ snprintf(get_lpar, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT
+ " %s lssyscfg -m %s -r lpar -F name"
+ , dev->hmc, syslist[i]);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: get_lpar=%s\n"
+ , __FUNCTION__, get_lpar);
+ }
+
+ output = do_shell_cmd(get_lpar, &status, dev->password);
+ if (output == NULL) {
+ g_strfreev(syslist);
+ return S_BADCONFIG;
+ }
+ lparlist = g_strsplit(output, "\n", 0);
+ FREE(output);
+
+ /* for each lpar */
+ for (j = 0
+ ; NULL != lparlist[j] && 0 != lparlist[j][0]
+ ; j++) {
+ /* add the lpar */
+ snprintf(host, MAX_HOST_NAME_LEN
+ , "%s/%s", syslist[i],lparlist[j]);
+ dev->hostlist = g_list_append(dev->hostlist
+ , STRDUP(host));
+ }
+ g_strfreev(lparlist);
+ }
+ }
+ g_strfreev(syslist);
+
+ return S_OK;
+}
+
+static void
+free_hmc_hostlist(struct pluginDevice* dev)
+{
+ if (dev->hostlist) {
+ GList* node;
+ while (NULL != (node=g_list_first(dev->hostlist))) {
+ dev->hostlist = g_list_remove_link(dev->hostlist, node);
+ FREE(node->data);
+ g_list_free(node);
+ }
+ dev->hostlist = NULL;
+ }
+}
+
+static int
+get_hmc_mansyspats(struct pluginDevice * dev, const char *mansyspats)
+{
+ char *patscopy;
+ int numpats;
+ int i;
+ char *tmp;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, mansyspats=%s\n"
+ , __FUNCTION__, mansyspats);
+ }
+
+ patscopy = STRDUP(mansyspats);
+ if (patscopy == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return S_OOPS;
+ }
+
+ numpats = get_num_tokens(patscopy);
+ if (numpats > 0) {
+ dev->mansyspats = MALLOC((numpats+1)*sizeof(char *));
+ if (dev->mansyspats == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory"
+ , __FUNCTION__);
+ FREE(patscopy);
+ return S_OOPS;
+ }
+
+ memset(dev->mansyspats, 0, (numpats+1)*sizeof(char *));
+
+ /* White-space split the output here */
+ i = 0;
+ tmp = strtok(patscopy, WHITESPACE);
+ while (tmp != NULL) {
+ dev->mansyspats[i] = STRDUP(tmp);
+ if (dev->mansyspats[i] == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory"
+ , __FUNCTION__);
+ free_hmc_mansyspats(dev);
+ dev->mansyspats = NULL;
+ FREE(patscopy);
+ return S_OOPS;
+ }
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: adding pattern %s\n"
+ , __FUNCTION__, dev->mansyspats[i]);
+ }
+
+ /* no patterns necessary if all specified */
+ if (strcmp(dev->mansyspats[i], "*") == 0) {
+ stonith_free_hostlist(dev->mansyspats);
+ dev->mansyspats = NULL;
+ break;
+ }
+
+ i++;
+ tmp = strtok(NULL, WHITESPACE);
+ }
+ }
+ FREE(patscopy);
+ return S_OK;
+}
+
+static void
+free_hmc_mansyspats(struct pluginDevice* dev)
+{
+ if (dev->mansyspats) {
+ stonith_free_hostlist(dev->mansyspats);
+ dev->mansyspats = NULL;
+ }
+}
+
+static char*
+do_shell_cmd(const char* cmd, int* status, const char* password)
+{
+ const int BUFF_LEN=4096;
+ int read_len = 0;
+ char buff[BUFF_LEN];
+ char cmd_password[MAX_CMD_LEN];
+ char* data = NULL;
+ GString* g_str_tmp = NULL;
+
+ FILE* file;
+ if (NULL == password) {
+ file = popen(cmd, "r");
+ } else {
+ snprintf(cmd_password, MAX_CMD_LEN
+ ,"umask 077;"
+ "if [ ! -d " HA_VARRUNDIR "/heartbeat/rsctmp/ibmhmc ];"
+ "then mkdir " HA_VARRUNDIR "/heartbeat/rsctmp/ibmhmc 2>/dev/null;"
+ "fi;"
+ "export ibmhmc_tmp=`mktemp -p " HA_VARRUNDIR "/heartbeat/rsctmp/ibmhmc/`;"
+ "echo \"echo '%s'\">$ibmhmc_tmp;"
+ "chmod +x $ibmhmc_tmp;"
+ "unset SSH_AGENT_SOCK SSH_AGENT_PID;"
+ "SSH_ASKPASS=$ibmhmc_tmp DISPLAY=ibmhmc_foo setsid %s;"
+ "rm $ibmhmc_tmp -f;"
+ "unset ibmhmc_tmp"
+ ,password, cmd);
+ file = popen(cmd_password, "r");
+ }
+ if (NULL == file) {
+ return NULL;
+ }
+
+ g_str_tmp = g_string_new("");
+ while(!feof(file)) {
+ memset(buff, 0, BUFF_LEN);
+ read_len = fread(buff, 1, BUFF_LEN, file);
+ if (0 < read_len) {
+ g_string_append(g_str_tmp, buff);
+ }else{
+ sleep(1);
+ }
+ }
+ data = (char*)MALLOC(g_str_tmp->len+1);
+ if (data != NULL) {
+ data[0] = data[g_str_tmp->len] = 0;
+ strncpy(data, g_str_tmp->str, g_str_tmp->len);
+ }
+ g_string_free(g_str_tmp, TRUE);
+ *status = pclose(file);
+ return data;
+}
+
+static int
+check_hmc_status(struct pluginDevice* dev)
+{
+ int status;
+ char check_status[MAX_CMD_LEN];
+ char* output = NULL;
+ int rc = S_OK;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, hmc=%s\n", __FUNCTION__, dev->hmc);
+ }
+
+ snprintf(check_status, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lshmc -r -F ssh", dev->hmc);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: check_status %s\n", __FUNCTION__
+ , check_status);
+ }
+
+ output = do_shell_cmd(check_status, &status, dev->password);
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: status=%d, output=%s\n", __FUNCTION__
+ , status, output ? output : "(nil)");
+ }
+
+ if (NULL == output || strncmp(output, "enable", 6) != 0) {
+ rc = S_BADCONFIG;
+ }
+ if (NULL != output) {
+ FREE(output);
+ }
+ return rc;
+}
+
+static int
+get_num_tokens(char *str)
+{
+ int namecount = 0;
+
+ while (*str != EOS) {
+ str += strspn(str, WHITESPACE);
+ if (*str == EOS)
+ break;
+ str += strcspn(str, WHITESPACE);
+ namecount++;
+ }
+ return namecount;
+}
+
+static gboolean
+pattern_match(char **patterns, char *string)
+{
+ char **pattern;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, string=%s\n", __FUNCTION__, string);
+ }
+
+ for (pattern = patterns; *pattern; pattern++) {
+ int patlen = strlen(*pattern);
+
+ if (pattern[0][patlen-1] == '*') {
+ /* prefix match */
+ if (strncmp(string, *pattern, patlen-1) == 0) {
+ return TRUE;
+ }
+ }else{
+ /* exact match */
+ if (strcmp(string, *pattern) == 0) {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+static char*
+do_shell_cmd_fake(const char* cmd, int* status)
+{
+ printf("%s()\n", __FUNCTION__);
+ printf("cmd:%s\n", cmd);
+ *status=0;
+ return NULL;
+}
+*/
diff --git a/lib/plugins/stonith/ipmi_os_handler.c b/lib/plugins/stonith/ipmi_os_handler.c
new file mode 100644
index 0000000..bdb6d6e
--- /dev/null
+++ b/lib/plugins/stonith/ipmi_os_handler.c
@@ -0,0 +1,257 @@
+/*
+ * This program is largely based on the ipmicmd.c program that's part of OpenIPMI package.
+ *
+ * Copyright Intel Corp.
+ * Yixiong.Zou@intel.com
+ *
+ * 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 <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <OpenIPMI/os_handler.h>
+#include <OpenIPMI/selector.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+
+#include <OpenIPMI/ipmi_int.h>
+
+#include <time.h>
+
+extern selector_t *os_sel;
+
+#if 0
+static void check_no_locks(os_handler_t *handler);
+#define CHECK_NO_LOCKS(handler) check_no_locks(handler)
+#else
+#define CHECK_NO_LOCKS(handler) do {} while(0)
+#endif
+
+struct os_hnd_fd_id_s
+{
+ int fd;
+ void *cb_data;
+ os_data_ready_t data_ready;
+ os_handler_t *handler;
+};
+
+static void
+fd_handler(int fd, void *data)
+{
+
+ os_hnd_fd_id_t *fd_data = (os_hnd_fd_id_t *) data;
+
+ CHECK_NO_LOCKS(fd_data->handler);
+ fd_data->data_ready(fd, fd_data->cb_data, fd_data);
+ CHECK_NO_LOCKS(fd_data->handler);
+}
+
+static int
+add_fd(os_handler_t *handler,
+ int fd,
+ os_data_ready_t data_ready,
+ void *cb_data,
+ os_hnd_fd_id_t **id)
+{
+ os_hnd_fd_id_t *fd_data;
+
+ fd_data = ipmi_mem_alloc(sizeof(*fd_data));
+ if (!fd_data)
+ return ENOMEM;
+
+ fd_data->fd = fd;
+ fd_data->cb_data = cb_data;
+ fd_data->data_ready = data_ready;
+ fd_data->handler = handler;
+ sel_set_fd_handlers(os_sel, fd, fd_data, fd_handler, NULL, NULL, NULL);
+ sel_set_fd_read_handler(os_sel, fd, SEL_FD_HANDLER_ENABLED);
+ sel_set_fd_write_handler(os_sel, fd, SEL_FD_HANDLER_DISABLED);
+ sel_set_fd_except_handler(os_sel, fd, SEL_FD_HANDLER_DISABLED);
+
+ *id = fd_data;
+ return 0;
+}
+
+static int
+remove_fd(os_handler_t *handler, os_hnd_fd_id_t *fd_data)
+{
+ sel_clear_fd_handlers(os_sel, fd_data->fd);
+ sel_set_fd_read_handler(os_sel, fd_data->fd, SEL_FD_HANDLER_DISABLED);
+ ipmi_mem_free(fd_data);
+ return 0;
+}
+
+struct os_hnd_timer_id_s
+{
+ void *cb_data;
+ os_timed_out_t timed_out;
+ sel_timer_t *timer;
+ int running;
+ os_handler_t *handler;
+};
+
+static void
+timer_handler(selector_t *sel,
+ sel_timer_t *timer,
+ void *data)
+{
+ os_hnd_timer_id_t *timer_data = (os_hnd_timer_id_t *) data;
+ void *cb_data;
+ os_timed_out_t timed_out;
+
+ CHECK_NO_LOCKS(timer_data->handler);
+ timed_out = timer_data->timed_out;
+ cb_data = timer_data->cb_data;
+ timer_data->running = 0;
+ timed_out(cb_data, timer_data);
+ CHECK_NO_LOCKS(timer_data->handler);
+}
+
+static int
+start_timer(os_handler_t *handler,
+ os_hnd_timer_id_t *id,
+ struct timeval *timeout,
+ os_timed_out_t timed_out,
+ void *cb_data)
+{
+ struct timeval now;
+
+ if (id->running)
+ return EBUSY;
+
+ id->running = 1;
+ id->cb_data = cb_data;
+ id->timed_out = timed_out;
+
+ gettimeofday(&now, NULL);
+ now.tv_sec += timeout->tv_sec;
+ now.tv_usec += timeout->tv_usec;
+ while (now.tv_usec >= 1000000) {
+ now.tv_usec -= 1000000;
+ now.tv_sec += 1;
+ }
+
+ return sel_start_timer(id->timer, &now);
+}
+
+static int
+stop_timer(os_handler_t *handler, os_hnd_timer_id_t *timer_data)
+{
+ return sel_stop_timer(timer_data->timer);
+}
+
+static int
+alloc_timer(os_handler_t *handler,
+ os_hnd_timer_id_t **id)
+{
+ os_hnd_timer_id_t *timer_data;
+ int rv;
+
+ timer_data = ipmi_mem_alloc(sizeof(*timer_data));
+ if (!timer_data)
+ return ENOMEM;
+
+ timer_data->running = 0;
+ timer_data->timed_out = NULL;
+ timer_data->handler = handler;
+
+ rv = sel_alloc_timer(os_sel, timer_handler, timer_data,
+ &(timer_data->timer));
+ if (rv) {
+ ipmi_mem_free(timer_data);
+ return rv;
+ }
+
+ *id = timer_data;
+ return 0;
+}
+
+static int
+free_timer(os_handler_t *handler, os_hnd_timer_id_t *timer_data)
+{
+ sel_free_timer(timer_data->timer);
+ ipmi_mem_free(timer_data);
+ return 0;
+}
+
+static int
+get_random(os_handler_t *handler, void *data, unsigned int len)
+{
+ int fd = open("/dev/urandom", O_RDONLY);
+ int rv;
+
+ if (fd == -1)
+ return errno;
+
+ rv = read(fd, data, len);
+
+ close(fd);
+ return rv;
+}
+
+static void
+sui_log(os_handler_t *handler,
+ enum ipmi_log_type_e log_type,
+ char *format,
+ ...)
+{
+ return;
+}
+
+static void
+sui_vlog(os_handler_t *handler,
+ enum ipmi_log_type_e log_type,
+ char *format,
+ va_list ap)
+{
+ return;
+}
+
+
+os_handler_t ipmi_os_cb_handlers =
+{
+ .add_fd_to_wait_for = add_fd,
+ .remove_fd_to_wait_for = remove_fd,
+
+ .start_timer = start_timer,
+ .stop_timer = stop_timer,
+ .alloc_timer = alloc_timer,
+ .free_timer = free_timer,
+
+ .create_lock = NULL,
+ .destroy_lock = NULL,
+ .is_locked = NULL,
+ .lock = NULL,
+ .unlock = NULL,
+ .create_rwlock = NULL,
+ .destroy_rwlock = NULL,
+ .read_lock = NULL,
+ .write_lock = NULL,
+ .read_unlock = NULL,
+ .write_unlock = NULL,
+ .is_readlocked = NULL,
+ .is_writelocked = NULL,
+
+ .get_random = get_random,
+
+ .log = sui_log,
+ .vlog = sui_vlog
+};
+
+
diff --git a/lib/plugins/stonith/ipmilan.c b/lib/plugins/stonith/ipmilan.c
new file mode 100644
index 0000000..1efdfee
--- /dev/null
+++ b/lib/plugins/stonith/ipmilan.c
@@ -0,0 +1,587 @@
+/*
+ * Stonith module for ipmi lan Stonith device
+ *
+ * Copyright (c) 2003 Intel Corp.
+ * Yixiong Zou <yixiong.zou@intel.com>
+ *
+ * Mangled by Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005.
+ * And passed the compiling with OpenIPMI-1.4.8.
+ *
+ * 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
+ *
+ */
+
+
+/*
+ * See README.ipmi for information regarding this plugin.
+ *
+ */
+
+#define DEVICE "IPMI Over LAN"
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN ipmilan
+#define PIL_PLUGIN_S "ipmilan"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include <OpenIPMI/ipmi_types.h>
+#include <OpenIPMI/ipmi_auth.h>
+
+#include "ipmilan.h"
+
+static StonithPlugin * ipmilan_new(const char *);
+static void ipmilan_destroy(StonithPlugin *);
+static const char * const * ipmilan_get_confignames(StonithPlugin *);
+static int ipmilan_set_config(StonithPlugin *, StonithNVpair *);
+static const char * ipmilan_getinfo(StonithPlugin * s, int InfoType);
+static int ipmilan_status(StonithPlugin * );
+static int ipmilan_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** ipmilan_hostlist(StonithPlugin *);
+
+static struct stonith_ops ipmilanOps ={
+ ipmilan_new, /* Create new STONITH object */
+ ipmilan_destroy, /* Destroy STONITH object */
+ ipmilan_getinfo, /* Return STONITH info string */
+ ipmilan_get_confignames,/* Get configuration parameter names */
+ ipmilan_set_config, /* Set configuration */
+ ipmilan_status, /* Return STONITH device status */
+ ipmilan_reset_req, /* Request a reset */
+ ipmilan_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug);
+const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &ipmilanOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * ipmilan STONITH device.
+ *
+ * ipmilanHostInfo is a double linked list. Where the prev of the head always
+ * points to the tail. This is a little wierd. But it saves me from looping
+ * around to find the tail when destroying the list.
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ int hostcount;
+ struct ipmilanHostInfo * hostlist;
+};
+
+static const char * pluginid = "IPMI-LANDevice-Stonith";
+static const char * NOTpluginid = "IPMI-LAN device has been destroyed";
+
+#define ST_HOSTNAME "hostname"
+#define ST_PORT "port"
+#define ST_AUTH "auth"
+#define ST_PRIV "priv"
+#define ST_RESET_METHOD "reset_method"
+
+#include "stonith_config_xml.h"
+
+#define XML_HOSTNAME_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_HOSTNAME \
+ XML_PARM_SHORTDESC_END
+
+#define XML_HOSTNAME_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The hostname of the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_HOSTNAME_PARM \
+ XML_PARAMETER_BEGIN(ST_HOSTNAME, "string", "1", "1") \
+ XML_HOSTNAME_SHORTDESC \
+ XML_HOSTNAME_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_PORT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_PORT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PORT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The port number to where the IPMI message is sent" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PORT_PARM \
+ XML_PARAMETER_BEGIN(ST_PORT, "string", "1", "0") \
+ XML_PORT_SHORTDESC \
+ XML_PORT_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_AUTH_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_AUTH \
+ XML_PARM_SHORTDESC_END
+
+#define XML_AUTH_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The authorization type of the IPMI session (\"none\", \"straight\", \"md2\", or \"md5\")" \
+ XML_PARM_LONGDESC_END
+
+#define XML_AUTH_PARM \
+ XML_PARAMETER_BEGIN(ST_AUTH, "string", "1", "0") \
+ XML_AUTH_SHORTDESC \
+ XML_AUTH_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_PRIV_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_PRIV \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PRIV_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The privilege level of the user (\"operator\" or \"admin\")" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PRIV_PARM \
+ XML_PARAMETER_BEGIN(ST_PRIV, "string", "1", "0") \
+ XML_PRIV_SHORTDESC \
+ XML_PRIV_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_RESET_METHOD_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_RESET_METHOD \
+ XML_PARM_SHORTDESC_END
+
+#define XML_RESET_METHOD_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "How to reset the host (\"power_cycle\" or \"hard_reset\")" \
+ XML_PARM_LONGDESC_END
+
+#define XML_RESET_METHOD_PARM \
+ XML_PARAMETER_BEGIN(ST_RESET_METHOD, "string", "0", "0") \
+ XML_RESET_METHOD_SHORTDESC \
+ XML_RESET_METHOD_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *ipmilanXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTNAME_PARM
+ XML_IPADDR_PARM
+ XML_PORT_PARM
+ XML_AUTH_PARM
+ XML_PRIV_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * Check the status of the IPMI Lan STONITH device.
+ *
+ * NOTE: not sure what we should do here since each host is configured
+ * seperately.
+ *
+ * Two options:
+ * 1) always return S_OK.
+ * 2) using IPMI ping to confirm the status for every host that's
+ * configured.
+ *
+ * For now I choose the option 1 hoping that I can get by. Maybe we should
+ * change it to option 2 later.
+ */
+
+static int
+ipmilan_status(StonithPlugin *s)
+{
+ struct pluginDevice * nd;
+ struct ipmilanHostInfo * node;
+ int ret, rv;
+ int i;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ ret = S_OK;
+
+ nd = (struct pluginDevice *)s;
+ for( i=0, node = nd->hostlist;
+ i < nd->hostcount; i++, node = node->next ) {
+ rv = do_ipmi_cmd(node, ST_IPMI_STATUS);
+ if (rv) {
+ LOG(PIL_INFO, "Host %s ipmilan status failure."
+ , node->hostname);
+ ret = S_ACCESS;
+ } else {
+ LOG(PIL_INFO, "Host %s ipmilan status OK."
+ , node->hostname);
+ }
+
+ }
+
+ return ret;
+}
+
+/*
+ * This function returns the list of hosts that's configured.
+ *
+ * The detailed configuration is disabled because the STONITH command can be
+ * run by anyone so there is a security risk if that to be exposed.
+ */
+
+static char *
+get_config_string(struct pluginDevice * nd, int index)
+{
+ struct ipmilanHostInfo * host;
+ int i;
+
+ if (index >= nd->hostcount || index < 0) {
+ return (NULL);
+ }
+
+ host = nd->hostlist;
+ for (i = 0; i < index; i++) {
+ host = host->next;
+ }
+
+ return STRDUP(host->hostname);
+}
+
+
+/*
+ * Return the list of hosts configured for this ipmilan device
+ *
+ */
+
+static char **
+ipmilan_hostlist(StonithPlugin *s)
+{
+ int numnames = 0;
+ char ** ret = NULL;
+ struct pluginDevice* nd;
+ int j;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ nd = (struct pluginDevice*) s;
+ if (nd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in ipmi_hostlist");
+ return(NULL);
+ }
+ numnames = nd->hostcount;
+
+ ret = (char **)MALLOC((numnames + 1)*sizeof(char*));
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return (ret);
+ }
+
+ memset(ret, 0, (numnames + 1)*sizeof(char*));
+
+ for (j = 0; j < numnames; ++j) {
+ ret[j] = get_config_string(nd, j);
+ if (!ret[j]) {
+ stonith_free_hostlist(ret);
+ ret = NULL;
+ break;
+ }
+ strdown(ret[j]);
+ }
+
+ return(ret);
+}
+
+/*
+ * Parse the config information, and stash it away...
+ *
+ * The buffer for each string is MAX_IPMI_STRING_LEN bytes long.
+ * Right now it is set to 64. Hope this is enough.
+ *
+ */
+
+#define MAX_IPMI_STRING_LEN 64
+
+/*
+ * Reset the given host on this StonithPlugin device.
+ */
+static int
+ipmilan_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = 0;
+ struct pluginDevice * nd;
+ struct ipmilanHostInfo * node;
+ int i;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ nd = (struct pluginDevice *)s;
+ for( i=0, node = nd->hostlist;
+ i < nd->hostcount; i++, node = node->next ) {
+ if (strcasecmp(node->hostname, host) == 0) {
+ break;
+ }
+ }
+
+ if (i >= nd->hostcount) {
+ LOG(PIL_CRIT, "Host %s is not configured in this STONITH "
+ " module. Please check your configuration file.", host);
+ return (S_OOPS);
+ }
+
+ rc = do_ipmi_cmd(node, request);
+ if (!rc) {
+ LOG(PIL_INFO, "Host %s ipmilan-reset.", host);
+ } else {
+ LOG(PIL_INFO, "Host %s ipmilan-reset error. Error = %d."
+ , host, rc);
+ }
+ return rc;
+}
+
+/*
+ * Get configuration parameter names
+ */
+static const char * const *
+ipmilan_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] =
+ { ST_HOSTNAME, ST_IPADDR, ST_PORT, ST_AUTH,
+ ST_PRIV, ST_LOGIN, ST_PASSWD, ST_RESET_METHOD, NULL};
+ return ret;
+}
+
+/*
+ * Set the configuration parameters
+ */
+static int
+ipmilan_set_config(StonithPlugin* s, StonithNVpair * list)
+{
+ struct pluginDevice* nd;
+ int rc;
+ struct ipmilanHostInfo * tmp;
+ const char *reset_opt;
+
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTNAME, NULL}
+ , {ST_IPADDR, NULL}
+ , {ST_PORT, NULL}
+ , {ST_AUTH, NULL}
+ , {ST_PRIV, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ nd = (struct pluginDevice *)s;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (nd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+
+ tmp = ST_MALLOCT(struct ipmilanHostInfo);
+ tmp->hostname = namestocopy[0].s_value;
+ tmp->ipaddr = namestocopy[1].s_value;
+ tmp->portnumber = atoi(namestocopy[2].s_value);
+ FREE(namestocopy[2].s_value);
+ if (namestocopy[3].s_value == NULL) {
+ LOG(PIL_CRIT, "ipmilan auth type is NULL. See "
+ "README.ipmilan for allowed values");
+ return S_OOPS;
+ } else if (strcmp(namestocopy[3].s_value, "none") == 0) {
+ tmp->authtype = 0;
+ } else if (strcmp(namestocopy[3].s_value, "md2") == 0) {
+ tmp->authtype = 1;
+ } else if (strcmp(namestocopy[3].s_value, "md5") == 0) {
+ tmp->authtype = 2;
+ } else if (strcmp(namestocopy[3].s_value, "key") == 0 ||
+ strcmp(namestocopy[3].s_value, "password") == 0 ||
+ strcmp(namestocopy[3].s_value, "straight") == 0) {
+ tmp->authtype = 4;
+ } else {
+ LOG(PIL_CRIT, "ipmilan auth type '%s' invalid. See "
+ "README.ipmilan for allowed values", namestocopy[3].s_value);
+ return S_OOPS;
+ }
+ FREE(namestocopy[3].s_value);
+ if (namestocopy[4].s_value == NULL) {
+ LOG(PIL_CRIT, "ipmilan priv value is NULL. See "
+ "README.ipmilan for allowed values");
+ return S_OOPS;
+ } else if (strcmp(namestocopy[4].s_value, "operator") == 0) {
+ tmp->privilege = 3;
+ } else if (strcmp(namestocopy[4].s_value, "admin") == 0) {
+ tmp->privilege = 4;
+ } else {
+ LOG(PIL_CRIT, "ipmilan priv value '%s' invalid. See "
+ "README.ipmilan for allowed values", namestocopy[4].s_value);
+ return(S_OOPS);
+ }
+ FREE(namestocopy[4].s_value);
+ tmp->username = namestocopy[5].s_value;
+ tmp->password = namestocopy[6].s_value;
+ reset_opt = OurImports->GetValue(list, ST_RESET_METHOD);
+ if (!reset_opt || !strcmp(reset_opt, "power_cycle")) {
+ tmp->reset_method = 0;
+ } else if (!strcmp(reset_opt, "hard_reset")) {
+ tmp->reset_method = 1;
+ } else {
+ LOG(PIL_CRIT, "ipmilan reset_method '%s' invalid", reset_opt);
+ return S_OOPS;
+ }
+
+ if (nd->hostlist == NULL ) {
+ nd->hostlist = tmp;
+ nd->hostlist->prev = tmp;
+ nd->hostlist->next = tmp;
+ } else {
+ tmp->prev = nd->hostlist->prev;
+ tmp->next = nd->hostlist;
+ nd->hostlist->prev->next = tmp;
+ nd->hostlist->prev = tmp;
+ }
+ nd->hostcount++;
+
+ return(S_OK);
+}
+
+static const char *
+ipmilan_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice * nd;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ nd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = nd->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = nd->hostlist ? nd->hostlist->hostname : NULL;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "IPMI LAN STONITH device\n";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.intel.com/design/servers/ipmi/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = ipmilanXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * ipmilan StonithPlugin destructor...
+ *
+ * The hostlist is a link list. So have to iterate through.
+ */
+static void
+ipmilan_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+ struct ipmilanHostInfo * host;
+ int i;
+
+ VOIDERRIFWRONGDEV(s);
+
+ nd = (struct pluginDevice *)s;
+
+ nd->pluginid = NOTpluginid;
+
+ if (nd->hostlist) {
+ host = nd->hostlist->prev;
+ for (i = 0; i < nd->hostcount; i++) {
+ struct ipmilanHostInfo * host_prev = host->prev;
+
+ FREE(host->hostname);
+ FREE(host->ipaddr);
+ FREE(host->username);
+ FREE(host->password);
+
+ FREE(host);
+ host = host_prev;
+ }
+ }
+
+ nd->hostcount = -1;
+ FREE(nd);
+ ipmi_leave();
+}
+
+/* Create a new ipmilan StonithPlugin device. Too bad this function can't be static */
+static StonithPlugin *
+ipmilan_new(const char *subplugin)
+{
+ struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice);
+
+ if (nd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ LOG(PIL_WARN, "The ipmilan stonith plugin is deprecated! Please use external/ipmi.");
+ memset(nd, 0, sizeof(*nd));
+ nd->pluginid = pluginid;
+ nd->hostlist = NULL;
+ nd->hostcount = 0;
+ nd->idinfo = DEVICE;
+ nd->sp.s_ops = &ipmilanOps;
+ return(&(nd->sp));
+}
diff --git a/lib/plugins/stonith/ipmilan.h b/lib/plugins/stonith/ipmilan.h
new file mode 100644
index 0000000..fb548f0
--- /dev/null
+++ b/lib/plugins/stonith/ipmilan.h
@@ -0,0 +1,41 @@
+/*
+ * Stonith module for ipmi lan Stonith device
+ *
+ * Copyright (c) 2003 Intel Corp.
+ * Yixiong Zou <yixiong.zou@intel.com>
+ *
+ * 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
+ *
+ */
+
+#define ST_IPMI_STATUS 4
+#include <time.h>
+
+struct ipmilanHostInfo {
+ char * hostname;
+ char * ipaddr;
+ int portnumber;
+ int authtype;
+ int privilege;
+ char * username;
+ char * password;
+ int reset_method;
+
+ struct ipmilanHostInfo * prev;
+ struct ipmilanHostInfo * next;
+};
+
+int do_ipmi_cmd(struct ipmilanHostInfo * host, int request);
+void ipmi_leave(void);
diff --git a/lib/plugins/stonith/ipmilan_command.c b/lib/plugins/stonith/ipmilan_command.c
new file mode 100644
index 0000000..a3de493
--- /dev/null
+++ b/lib/plugins/stonith/ipmilan_command.c
@@ -0,0 +1,399 @@
+/*
+ * This program is largely based on the ipmicmd.c program that's part of OpenIPMI package.
+ *
+ * Copyright Intel Corp.
+ * Yixiong.Zou@intel.com
+ *
+ *
+ * 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 <stdio.h>
+
+#include <stdlib.h> /* malloc() */
+#include <unistd.h> /* getopt() */
+#include <string.h> /* strerror() */
+#include <netdb.h> /* gethostbyname() */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <OpenIPMI/ipmiif.h>
+#include <OpenIPMI/selector.h>
+#include <OpenIPMI/ipmi_conn.h>
+#include <OpenIPMI/ipmi_lan.h>
+#include <OpenIPMI/ipmi_smi.h>
+#include <OpenIPMI/ipmi_auth.h>
+#include <OpenIPMI/ipmi_msgbits.h>
+#include <OpenIPMI/ipmi_posix.h>
+#include <OpenIPMI/ipmi_debug.h>
+
+#include "ipmilan.h"
+#include <stonith/stonith.h>
+#include <clplumbing/cl_log.h>
+
+#include <pils/plugin.h>
+extern const PILPluginImports* PluginImports;
+
+/* #define DUMP_MSG 0 */
+#define OPERATION_TIME_OUT 10
+
+os_handler_t *os_hnd=NULL;
+selector_t *os_sel;
+static ipmi_con_t *con;
+extern os_handler_t ipmi_os_cb_handlers;
+static int reset_method;
+
+static int request_done = 0;
+static int op_done = 0;
+
+typedef enum ipmi_status {
+ /*
+ IPMI_CONNECTION_FAILURE,
+ IPMI_SEND_FAILURE,
+ IPMI_BAD_REQUEST,
+ IPMI_REQUEST_FAILED,
+ IPMI_TIME_OUT,
+ */
+ IPMI_RUNNING = 99,
+} ipmi_status_t;
+
+static ipmi_status_t gstatus;
+
+typedef enum chassis_control_request {
+ POWER_DOWN = 0X00,
+ POWER_UP = 0X01,
+ POWER_CYCLE = 0X02,
+ HARD_RESET = 0X03,
+ PULSE_DIAGNOSTIC_INTERRUPT = 0X04,
+ SOFT_SHUTDOWN = 0X05
+} chassis_control_request_t;
+
+void dump_msg_data(ipmi_msg_t *msg, ipmi_addr_t *addr, const char *type);
+int rsp_handler(ipmi_con_t *ipmi, ipmi_msgi_t *rspi);
+
+void send_ipmi_cmd(ipmi_con_t *con, int request);
+
+void timed_out(selector_t *sel, sel_timer_t *timer, void *data);
+
+void
+timed_out(selector_t *sel, sel_timer_t *timer, void *data)
+{
+ PILCallLog(PluginImports->log,PIL_CRIT, "IPMI operation timed out... :(\n");
+ gstatus = S_TIMEOUT;
+}
+
+void
+dump_msg_data(ipmi_msg_t *msg, ipmi_addr_t *addr, const char *type)
+{
+ ipmi_system_interface_addr_t *smi_addr = NULL;
+ int i;
+ ipmi_ipmb_addr_t *ipmb_addr = NULL;
+
+ if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
+ smi_addr = (struct ipmi_system_interface_addr *) addr;
+
+ fprintf(stderr, "%2.2x %2.2x %2.2x %2.2x ",
+ addr->channel,
+ msg->netfn,
+ smi_addr->lun,
+ msg->cmd);
+ } else if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE)
+ || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) {
+ ipmb_addr = (struct ipmi_ipmb_addr *) addr;
+
+ fprintf(stderr, "%2.2x %2.2x %2.2x %2.2x ",
+ addr->channel,
+ msg->netfn,
+ ipmb_addr->lun,
+ msg->cmd);
+ }
+
+ for (i = 0; i < msg->data_len; i++) {
+ if (((i%16) == 0) && (i != 0)) {
+ printf("\n ");
+ }
+ fprintf(stderr, "%2.2x ", msg->data[i]);
+ }
+ fprintf(stderr, "\n");
+}
+
+/*
+ * This function gets called after the response comes back
+ * from the IPMI device.
+ *
+ * Some IPMI device does not return success, 0x00, to the
+ * remote node when the power-reset was issued.
+ *
+ * The host who sent the ipmi cmd might get a 0xc3,
+ * a timeout instead. This creates problems for
+ * STONITH operation, where status is critical. :(
+ *
+ * Right now I am only checking 0xc3 as the return.
+ * If your IPMI device returns some wired code after
+ * reset, you might want to add it in this code block.
+ *
+ */
+
+int
+rsp_handler(ipmi_con_t *ipmi, ipmi_msgi_t *rspi)
+{
+ int rv;
+ long request;
+
+ /*dump_msg_data(&rspi->msg, &rspi->addr, "response");*/
+ request = (long) rspi->data1;
+
+ op_done = 1;
+ if( !rspi || !(rspi->msg.data) ) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "No data received\n");
+ gstatus = S_RESETFAIL;
+ return IPMI_MSG_ITEM_NOT_USED;
+ }
+ rv = rspi->msg.data[0];
+ /* some IPMI device might not issue 0x00, success, for reset command.
+ instead, a 0xc3, timeout, is returned. */
+ if (rv == 0x00) {
+ gstatus = S_OK;
+ } else if((rv == 0xc3 || rv == 0xff) && request == ST_GENERIC_RESET) {
+ PILCallLog(PluginImports->log,PIL_WARN ,
+ "IPMI reset request failed: %x, but we assume that it succeeded\n", rv);
+ gstatus = S_OK;
+ } else {
+ PILCallLog(PluginImports->log,PIL_INFO
+ , "IPMI request %ld failed: %x\n", request, rv);
+ gstatus = S_RESETFAIL;
+ }
+ return IPMI_MSG_ITEM_NOT_USED;
+}
+
+void
+send_ipmi_cmd(ipmi_con_t *con, int request)
+{
+ ipmi_addr_t addr;
+ unsigned int addr_len;
+ ipmi_msg_t msg;
+ struct ipmi_system_interface_addr *si;
+ int rv;
+ ipmi_msgi_t *rspi;
+ /* chassis control command request is only 1 byte long */
+ unsigned char cc_data = POWER_CYCLE;
+
+ si = (void *) &addr;
+ si->lun = 0x00;
+ si->channel = IPMI_BMC_CHANNEL;
+ si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ addr_len = sizeof(*si);
+
+ msg.netfn = IPMI_CHASSIS_NETFN;
+ msg.cmd = IPMI_CHASSIS_CONTROL_CMD;
+ msg.data = &cc_data;
+ msg.data_len = 1;
+
+ switch (request) {
+ case ST_POWERON:
+ cc_data = POWER_UP;
+ break;
+
+ case ST_POWEROFF:
+ cc_data = POWER_DOWN;
+ break;
+
+ case ST_GENERIC_RESET:
+ cc_data = (reset_method ? POWER_CYCLE : HARD_RESET);
+ break;
+
+ case ST_IPMI_STATUS:
+ msg.netfn = IPMI_APP_NETFN;
+ msg.cmd = IPMI_GET_DEVICE_ID_CMD;
+ msg.data_len = 0;
+ break;
+
+ default:
+ gstatus = S_INVAL;
+ return;
+ }
+
+ gstatus = S_ACCESS;
+ rspi = calloc(1, sizeof(ipmi_msgi_t));
+ if (NULL == rspi) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Error sending IPMI command: Out of memory\n");
+ } else {
+ rspi->data1 = (void *) (long) request;
+ rv = con->send_command(con, &addr, addr_len, &msg, rsp_handler, rspi);
+ if (rv == -1) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Error sending IPMI command: %x\n", rv);
+ } else {
+ request_done = 1;
+ }
+ }
+
+ return;
+}
+
+static void
+con_changed_handler(ipmi_con_t *ipmi, int err, unsigned int port_num,
+ int still_connected, void *cb_data)
+{
+ int * request;
+
+ if (err) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Unable to setup connection: %x\n", err);
+ return;
+ }
+
+ if( !request_done ) {
+ request = (int *) cb_data;
+ send_ipmi_cmd(ipmi, *request);
+ }
+}
+
+static int
+setup_ipmi_conn(struct ipmilanHostInfo * host, int *request)
+{
+ int rv;
+
+ struct hostent *ent;
+ struct in_addr lan_addr[2];
+ int lan_port[2];
+ int num_addr = 1;
+ int authtype = 0;
+ int privilege = 0;
+ char username[17];
+ char password[17];
+
+ /*DEBUG_MSG_ENABLE();*/
+
+ os_hnd = ipmi_posix_get_os_handler();
+ if (!os_hnd) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_smi_setup_con: Unable to allocate os handler");
+ return 1;
+ }
+
+ rv = sel_alloc_selector(os_hnd, &os_sel);
+ if (rv) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Could not allocate selector\n");
+ return rv;
+ }
+
+ ipmi_posix_os_handler_set_sel(os_hnd, os_sel);
+
+ rv = ipmi_init(os_hnd);
+ if (rv) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_init erro: %d ", rv);
+ return rv;
+ }
+
+ ent = gethostbyname(host->ipaddr);
+ if (!ent) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "gethostbyname failed: %s\n", strerror(h_errno));
+ return 1;
+ }
+
+ memcpy(&lan_addr[0], ent->h_addr_list[0], ent->h_length);
+ lan_port[0] = host->portnumber;
+ lan_port[1] = 0;
+
+ authtype = host->authtype;
+ privilege = host->privilege;
+
+ memcpy(username, host->username, sizeof(username));
+ memcpy(password, host->password, sizeof(password));
+
+ reset_method = host->reset_method;
+
+ rv = ipmi_lan_setup_con(lan_addr, lan_port, num_addr,
+ authtype, privilege,
+ username, strlen(username),
+ password, strlen(password),
+ os_hnd, os_sel,
+ &con);
+
+ if (rv) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_lan_setup_con: %s\n", strerror(rv));
+ return S_ACCESS;
+ }
+
+#if OPENIPMI_VERSION_MAJOR < 2
+ con->set_con_change_handler(con, con_changed_handler, request);
+#else
+ con->add_con_change_handler(con, con_changed_handler, request);
+#endif
+
+ gstatus = IPMI_RUNNING;
+
+ rv = con->start_con(con);
+ if (rv) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Could not start IPMI connection: %x\n", rv);
+ gstatus = S_BADCONFIG;
+ return rv;
+ }
+ return S_OK;
+}
+
+void
+ipmi_leave()
+{
+ if( con && con->close_connection ) {
+ con->close_connection(con);
+ con = NULL;
+ }
+ if( os_sel ) {
+ sel_free_selector(os_sel);
+ os_sel = NULL;
+ }
+}
+
+int
+do_ipmi_cmd(struct ipmilanHostInfo * host, int request)
+{
+ int rv;
+ sel_timer_t * timer;
+ struct timeval timeout;
+
+ request_done = 0;
+ op_done = 0;
+
+ if( !os_hnd ) {
+ rv = setup_ipmi_conn(host, &request);
+ if( rv ) {
+ return rv;
+ }
+ } else {
+ send_ipmi_cmd(con, request);
+ }
+
+ gettimeofday(&timeout, NULL);
+ timeout.tv_sec += OPERATION_TIME_OUT;
+ timeout.tv_usec += 0;
+
+ sel_alloc_timer(os_sel, timed_out, NULL, &timer);
+ sel_start_timer(timer, &timeout);
+
+ while (!op_done) {
+ rv = sel_select(os_sel, NULL, 0, NULL, NULL);
+ if (rv == -1) {
+ break;
+ }
+ }
+
+ sel_free_timer(timer);
+ return gstatus;
+}
+
+#if OPENIPMI_VERSION_MAJOR < 2
+void
+posix_vlog(char *format, enum ipmi_log_type_e log_type, va_list ap)
+{
+}
+#endif
diff --git a/lib/plugins/stonith/ipmilan_test.c b/lib/plugins/stonith/ipmilan_test.c
new file mode 100644
index 0000000..47859a0
--- /dev/null
+++ b/lib/plugins/stonith/ipmilan_test.c
@@ -0,0 +1,63 @@
+/*
+ * Stonith module for ipmi lan Stonith device
+ *
+ * Copyright (c) 2003 Intel Corp.
+ * Yixiong Zou <yixiong.zou@intel.com>
+ *
+ * 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
+ *
+ */
+
+/*
+ * A quick test program to verify that IPMI host is setup correctly.
+ *
+ * You will need to modify the values in user, pass, ip, and port.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "ipmilan.h"
+#include <OpenIPMI/ipmi_auth.h>
+
+int main(int argc, char * argv[])
+{
+ struct ipmilanHostInfo host;
+ int request = 2;
+ int rv;
+
+ char user[] = "joe";
+ char pass[] = "blow";
+ char ip[] = "192.168.1.7";
+
+ host.hostname = NULL;
+ host.portnumber = 999;
+ host.authtype = IPMI_AUTHTYPE_NONE;
+ host.privilege = IPMI_PRIVILEGE_ADMIN;
+
+ host.ipaddr = ip;
+ memcpy(host.username, user, sizeof(user));
+ memcpy(host.password, pass, sizeof(pass));
+ /*
+ memset(host.username, 0, sizeof(host.username));
+ memset(host.password, 0, sizeof(host.password));
+ */
+
+ rv = do_ipmi_cmd(&host, request);
+ if (rv)
+ printf("rv = %d, operation failed. \n", rv);
+ else
+ printf("operation succeeded. \n");
+ return rv;
+}
diff --git a/lib/plugins/stonith/meatware.c b/lib/plugins/stonith/meatware.c
new file mode 100644
index 0000000..029ba35
--- /dev/null
+++ b/lib/plugins/stonith/meatware.c
@@ -0,0 +1,351 @@
+/*
+ * Stonith module for Human Operator Stonith device
+ *
+ * Copyright (c) 2001 Gregor Binder <gbinder@sysfive.com>
+ *
+ * This module is largely based on the "NULL Stonith device", written
+ * by Alan Robertson <alanr@unix.sh>, using code by David C. Teigland
+ * <teigland@sistina.com> originally appeared in the GFS stomith
+ * meatware agent.
+ *
+ * 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 "Meatware STONITH device"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN meatware
+#define PIL_PLUGIN_S "meatware"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * meatware_new(const char *);
+static void meatware_destroy(StonithPlugin *);
+static int meatware_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * meatware_get_confignames(StonithPlugin *);
+static const char * meatware_getinfo(StonithPlugin * s, int InfoType);
+static int meatware_status(StonithPlugin * );
+static int meatware_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** meatware_hostlist(StonithPlugin *);
+
+static struct stonith_ops meatwareOps ={
+ meatware_new, /* Create new STONITH object */
+ meatware_destroy, /* Destroy STONITH object */
+ meatware_getinfo, /* Return STONITH info string */
+ meatware_get_confignames,/* Return STONITH info string */
+ meatware_set_config, /* Get configuration from NVpairs */
+ meatware_status, /* Return STONITH device status */
+ meatware_reset_req, /* Request a reset */
+ meatware_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &meatwareOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * Meatware STONITH device.
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist;
+ int hostcount;
+};
+
+static const char * pluginid = "MeatwareDevice-Stonith";
+static const char * NOTpluginID = "Meatware device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *meatwareXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+static int
+meatware_status(StonithPlugin *s)
+{
+ ERRIFWRONGDEV(s,S_OOPS);
+ return S_OK;
+}
+
+
+/*
+ * Return the list of hosts configured for this Meat device
+ */
+
+static char **
+meatware_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ ERRIFWRONGDEV(s,NULL);
+ nd = (struct pluginDevice*) s;
+ if (nd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in Meatware_list_hosts");
+ return(NULL);
+ }
+
+ return OurImports->CopyHostList((const char * const *)nd->hostlist);
+}
+
+/*
+ * Parse the config information, and stash it away...
+ */
+
+static int
+Meat_parse_config_info(struct pluginDevice* nd, const char * info)
+{
+ LOG(PIL_INFO , "parse config info info=%s",info);
+ if (nd->hostcount >= 0) {
+ return(S_OOPS);
+ }
+
+ nd->hostlist = OurImports->StringToHostList(info);
+ if (nd->hostlist == NULL) {
+ LOG(PIL_CRIT,"StringToHostList() failed");
+ return S_OOPS;
+ }
+ for (nd->hostcount = 0; nd->hostlist[nd->hostcount]; nd->hostcount++) {
+ strdown(nd->hostlist[nd->hostcount]);
+ }
+ return(S_OK);
+}
+
+
+/*
+ * Indicate that host must be power cycled manually.
+ */
+static int
+meatware_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int fd, rc;
+ const char * meatpipe_pr = HA_VARRUNDIR "/meatware"; /* if you intend to
+ change this, modify
+ meatclient.c as well */
+
+ char line[256], meatpipe[256];
+ char resp_addr[50], resp_mw[50], resp_result[50];
+
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ snprintf(meatpipe, 256, "%s.%s", meatpipe_pr, host);
+ umask(0);
+ unlink(meatpipe);
+
+ rc = mkfifo(meatpipe, (S_IRUSR | S_IWUSR));
+
+ if (rc < 0) {
+ LOG(PIL_CRIT, "cannot create FIFO for Meatware_reset_host");
+ return S_OOPS;
+ }
+
+ LOG(PIL_CRIT, "OPERATOR INTERVENTION REQUIRED to reset %s.", host);
+ LOG(PIL_CRIT, "Run \"meatclient -c %s\" AFTER power-cycling the "
+ "machine.", host);
+
+ fd = open(meatpipe, O_RDONLY);
+
+ if (fd < 0) {
+ LOG(PIL_CRIT, "cannot open FIFO for Meatware_reset_host");
+ return S_OOPS;
+ }
+
+ alarm(600);
+ memset(line, 0, 256);
+ rc = read(fd, line, 256);
+ alarm(0);
+
+ if (rc < 0) {
+ LOG(PIL_CRIT, "read error on FIFO for Meatware_reset_host");
+ return S_OOPS;
+ }
+
+ memset(resp_mw, 0, 50);
+ memset(resp_result, 0, 50);
+ memset(resp_addr, 0, 50);
+
+ if (sscanf(line, "%s %s %s", resp_mw, resp_result, resp_addr) < 3) {
+ LOG(PIL_CRIT, "Format error - failed to Meatware-reset node %s",
+ host);
+ return S_RESETFAIL;
+ }
+
+ strdown(resp_addr);
+
+ if (strncmp(resp_mw, "meatware", 8) ||
+ strncmp(resp_result, "reply", 5) ||
+ strncasecmp(resp_addr, host, strlen(resp_addr))) {
+ LOG(PIL_CRIT, "failed to Meatware-reset node %s", host);
+ return S_RESETFAIL;
+ }else{
+ LOG(PIL_INFO, "node Meatware-reset: %s", host);
+ unlink(meatpipe);
+ return S_OK;
+ }
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+meatware_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+
+ struct pluginDevice* nd;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ nd = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ rc = Meat_parse_config_info(nd, namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ return rc;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+meatware_get_confignames(StonithPlugin* p)
+{
+ static const char * MeatwareParams[] = {ST_HOSTLIST, NULL };
+ return MeatwareParams;
+}
+
+/*
+ * Return STONITH info string
+ */
+static const char *
+meatware_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* nd;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ nd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = nd->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = "Your Name Here";
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Human (meatware) intervention STONITH device.\n"
+ "This STONITH agent prompts a human to reset a machine.\n"
+ "The human tells it when the reset was completed.";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = meatwareXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Meat Stonith destructor...
+ */
+static void
+meatware_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ VOIDERRIFWRONGDEV(s);
+ nd = (struct pluginDevice *)s;
+
+ nd->pluginid = NOTpluginID;
+ if (nd->hostlist) {
+ stonith_free_hostlist(nd->hostlist);
+ nd->hostlist = NULL;
+ }
+ nd->hostcount = -1;
+ FREE(nd);
+}
+
+/* Create a new Meatware Stonith device. */
+
+static StonithPlugin *
+meatware_new(const char *subplugin)
+{
+ struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice);
+
+ if (nd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(nd, 0, sizeof(*nd));
+ nd->pluginid = pluginid;
+ nd->hostlist = NULL;
+ nd->hostcount = -1;
+ nd->idinfo = DEVICE;
+ nd->sp.s_ops = &meatwareOps;
+
+ return &(nd->sp);
+}
diff --git a/lib/plugins/stonith/null.c b/lib/plugins/stonith/null.c
new file mode 100644
index 0000000..0d0cf04
--- /dev/null
+++ b/lib/plugins/stonith/null.c
@@ -0,0 +1,260 @@
+/*
+ * Stonith module for NULL Stonith device
+ *
+ * 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 "NULL STONITH device"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN null
+#define PIL_PLUGIN_S "null"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist;
+ int hostcount;
+};
+
+static StonithPlugin* null_new(const char *);
+static void null_destroy(StonithPlugin *);
+static int null_set_config(StonithPlugin*
+, StonithNVpair*);
+static const char * const * null_get_confignames(StonithPlugin*);
+static const char * null_getinfo(StonithPlugin * s, int InfoType);
+static int null_status(StonithPlugin * );
+static int null_reset_req(StonithPlugin * s
+, int request, const char * host);
+static char ** null_hostlist(StonithPlugin *);
+
+static struct stonith_ops nullOps ={
+ null_new, /* Create new STONITH object */
+ null_destroy, /* Destroy STONITH object */
+ null_getinfo, /* Return STONITH info string */
+ null_get_confignames, /* Return list of config params */
+ null_set_config, /* configure fron NV pairs */
+ null_status, /* Return STONITH device status */
+ null_reset_req, /* Request a reset */
+ null_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &nullOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * Null STONITH device. We are very agreeable, but don't do much :-)
+ */
+
+
+static const char * pluginid = "nullDevice-Stonith";
+static const char * NOTpluginID = "Null device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *nullXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+static int
+null_status(StonithPlugin *s)
+{
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ return S_OK;
+}
+
+
+/*
+ * Return the list of hosts configured for this NULL device
+ */
+
+static char **
+null_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* nd = (struct pluginDevice*)s;
+
+ ERRIFWRONGDEV(s, NULL);
+ return OurImports->CopyHostList((const char * const *)nd->hostlist);
+}
+
+
+/*
+ * Pretend to reset the given host on this Stonith device.
+ * (we don't even error check the "request" type)
+ */
+static int
+null_reset_req(StonithPlugin * s, int request, const char * host)
+{
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ /* Real devices need to pay attention to the "request" */
+ /* (but we don't care ;-)) */
+
+ LOG(PIL_INFO, "Host null-reset: %s", host);
+ return S_OK;
+}
+
+
+static const char * const *
+null_get_confignames(StonithPlugin* p)
+{
+ static const char * NullParams[] = {ST_HOSTLIST, NULL };
+ return NullParams;
+}
+
+/*
+ * Parse the config information in the given string,
+ * and stash it away...
+ */
+static int
+null_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ struct pluginDevice* nd = (struct pluginDevice*) s;
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ nd->hostlist = OurImports->StringToHostList(namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ if (nd->hostlist == NULL) {
+ LOG(PIL_CRIT,"StringToHostList() failed");
+ return S_OOPS;
+ }
+ for (nd->hostcount = 0; nd->hostlist[nd->hostcount]
+ ; nd->hostcount++) {
+ strdown(nd->hostlist[nd->hostcount]);
+ }
+ return nd->hostcount ? S_OK : S_BADCONFIG;
+}
+
+static const char *
+null_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* nd = (struct pluginDevice*) s;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = nd->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = "(nil)";
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "Dummy (do-nothing) STONITH device\n"
+ "FOR TESTING ONLY!";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = nullXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * NULL Stonith destructor...
+ */
+static void
+null_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ VOIDERRIFWRONGDEV(s);
+ nd = (struct pluginDevice *)s;
+
+ nd->pluginid = NOTpluginID;
+ if (nd->hostlist) {
+ stonith_free_hostlist(nd->hostlist);
+ nd->hostlist = NULL;
+ }
+ nd->hostcount = -1;
+ FREE(s);
+}
+
+/* Create a new Null Stonith device.
+ * Too bad this function can't be static
+ */
+static StonithPlugin *
+null_new(const char *subplugin)
+{
+ struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice);
+
+ if (nd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(nd, 0, sizeof(*nd));
+ nd->pluginid = pluginid;
+ nd->idinfo = DEVICE;
+ nd->sp.s_ops = &nullOps;
+ return (StonithPlugin *)nd;
+}
diff --git a/lib/plugins/stonith/nw_rpc100s.c b/lib/plugins/stonith/nw_rpc100s.c
new file mode 100644
index 0000000..5ba0827
--- /dev/null
+++ b/lib/plugins/stonith/nw_rpc100s.c
@@ -0,0 +1,779 @@
+/*
+ * Stonith module for Night/Ware RPC100S
+ *
+ * Original code from baytech.c by
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * Modifications for NW RPC100S
+ * 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 "NW RPC100S Power Switch"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN nw_rpc100s
+#define PIL_PLUGIN_S "nw_rpc100s"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#define MAX_CFGLINE 256
+#include <pils/plugin.h>
+
+static StonithPlugin * nw_rpc100s_new(const char *);
+static void nw_rpc100s_destroy(StonithPlugin *);
+static int nw_rpc100s_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * nw_rpc100s_get_confignames(StonithPlugin *);
+static const char * nw_rpc100s_getinfo(StonithPlugin * s, int InfoType);
+static int nw_rpc100s_status(StonithPlugin * );
+static int nw_rpc100s_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** nw_rpc100s_hostlist(StonithPlugin *);
+
+static struct stonith_ops nw_rpc100sOps ={
+ nw_rpc100s_new, /* Create new STONITH object */
+ nw_rpc100s_destroy, /* Destroy STONITH object */
+ nw_rpc100s_getinfo, /* Return STONITH info string */
+ nw_rpc100s_get_confignames,/* Return STONITH info string */
+ nw_rpc100s_set_config, /* Get configuration from NVpairs */
+ nw_rpc100s_status, /* Return STONITH device status */
+ nw_rpc100s_reset_req, /* Request a reset */
+ nw_rpc100s_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
+ , &nw_rpc100sOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ The Nightware RPS-100S is manufactured by:
+
+ Micro Energetics Corp
+ +1 703 250-3000
+ http://www.nightware.com/
+
+ Thank you to David Hicks of Micro Energetics Corp. for providing
+ a demo unit to write this software.
+
+ This switch has a very simple protocol,
+ You issue a command and it gives a response.
+ Sample commands are conveniently documented on a sticker on the
+ bottom of the device.
+
+ The switch accepts a single command of the form
+
+ //0,yyy,zzz[/m][/h]<CR>
+
+ Where yyy is the wait time before activiting the relay.
+ zzz is the relay time.
+
+ The default is that the relay is in a default state of ON, which
+ means that usually yyy is the number of seconds to wait
+ before shutting off the power and zzz is the number of seconds the
+ power remains off. There is a dip switch to change the default
+ state to 'OFF'. Don't set this switch. It will screw up this code.
+
+ An asterisk can be used for zzz to specify an infinite switch time.
+ The /m /and /h command options will convert the specified wait and
+ switch times to either minutewes or hours.
+
+ A response is either
+ <cr><lf>OK<cr><lf>
+ or
+ <cr><lf>Invalid Entry<cr><lf>
+
+
+ As far as THIS software is concerned, we have to implement 4 commands:
+
+ status --> //0,0,BOGUS; # Not a real command, this is just a
+ # probe to see if switch is alive
+ open(on) --> //0,0,0; # turn power to default state (on)
+ close(off) --> //0,0,*; # leave power off indefinitely
+ reboot --> //0,0,10; # immediately turn power off for 10 seconds.
+
+ and expect the response 'OK' to confirm that the unit is operational.
+*/
+
+
+
+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
+ */
+
+ char * node; /* Name of the node that this is controlling */
+
+};
+
+/* This string is used to identify this type of object in the config file */
+static const char * pluginid = "NW_RPC100S";
+static const char * NOTrpcid = "NW RPC100S device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *nw_rpc100sXML =
+ XML_PARAMETERS_BEGIN
+ XML_TTYDEV_PARM
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * Different expect strings that we get from the NW_RPC100S
+ * Remote Power Controllers...
+ */
+
+static struct Etoken NWtokOK[] = { {"OK", 0, 0}, {NULL,0,0}};
+static struct Etoken NWtokInvalidEntry[] = { {"Invalid Entry", 0, 0}, {NULL,0,0}};
+/* Accept either a CR/NL or an NL/CR */
+static struct Etoken NWtokCRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};
+
+static int RPCConnect(struct pluginDevice * ctx);
+static int RPCDisconnect(struct pluginDevice * ctx);
+
+static int RPCReset(struct pluginDevice*, int unitnum, const char * rebootid);
+#if defined(ST_POWERON)
+static int RPCOn(struct pluginDevice*, int unitnum, const char * rebootid);
+#endif
+#if defined(ST_POWEROFF)
+static int RPCOff(struct pluginDevice*, int unitnum, const char * rebootid);
+#endif
+static int RPCNametoOutlet ( struct pluginDevice * ctx, const char * host );
+
+/*static int RPC_parse_config_info(struct pluginDevice* ctx, const char * info);*/
+
+
+#define SENDCMD(cmd, timeout) { \
+ int return_val = RPCSendCommand(ctx, cmd, timeout); \
+ if (return_val != S_OK) { \
+ return return_val; \
+ } \
+ }
+
+/*
+ * RPCSendCommand - send a command to the specified outlet
+ */
+static int
+RPCSendCommand (struct pluginDevice *ctx, const char *command, int timeout)
+{
+ char writebuf[64]; /* All commands are short.
+ They should be WAY LESS
+ than 64 chars long!
+ */
+ int return_val; /* system call result */
+ fd_set rfds, wfds, xfds;
+ /* list of FDs for select() */
+ struct timeval tv; /* */
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&xfds);
+
+ snprintf (writebuf, sizeof(writebuf), "%s\r", command);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Sending %s", 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 RPCSendCommand() */
+
+/*
+ * RPCReset - Reset (power-cycle) the given outlet number
+ *
+ * This device can only control one power outlet - unitnum is ignored.
+ *
+ */
+static int
+RPCReset(struct pluginDevice* ctx, int unitnum, const char * rebootid)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Calling RPCReset (%s)", pluginid);
+ }
+
+ 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("//0,0,10;\r\n", 12);
+
+ /* Expect "OK" */
+ EXPECT(ctx->fd, NWtokOK, 5);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got OK");
+ }
+ EXPECT(ctx->fd, NWtokCRNL, 2);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got NL");
+ }
+
+ return(S_OK);
+
+} /* end RPCReset() */
+
+
+#if defined(ST_POWERON)
+/*
+ * RPCOn - Turn OFF the given outlet number
+ */
+static int
+RPCOn(struct pluginDevice* ctx, int unitnum, const char * host)
+{
+
+ if (ctx->fd < 0) {
+ LOG(PIL_CRIT, "%s: device %s is not open!", pluginid
+ , ctx->device);
+ return S_OOPS;
+ }
+
+ /* send the "On" command */
+ SENDCMD("//0,0,0;\r\n", 10);
+
+ /* Expect "OK" */
+ EXPECT(ctx->fd, NWtokOK, 5);
+ EXPECT(ctx->fd, NWtokCRNL, 2);
+
+ return(S_OK);
+
+} /* end RPCOn() */
+#endif
+
+
+#if defined(ST_POWEROFF)
+/*
+ * RPCOff - Turn Off the given outlet number
+ */
+static int
+RPCOff(struct pluginDevice* ctx, int unitnum, const char * host)
+{
+
+ if (ctx->fd < 0) {
+ LOG(PIL_CRIT, "%s: device %s is not open!", pluginid
+ , ctx->device);
+ return S_OOPS;
+ }
+
+ /* send the "Off" command */
+ SENDCMD("//0,0,*;\r\n", 10);
+
+ /* Expect "OK" */
+ EXPECT(ctx->fd, NWtokOK, 5);
+ EXPECT(ctx->fd, NWtokCRNL, 2);
+
+ return(S_OK);
+
+} /* end RPCOff() */
+#endif
+
+
+/*
+ * nw_rpc100s_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
+nw_rpc100s_status(StonithPlugin *s)
+{
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Calling nw_rpc100s_status (%s)", pluginid);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ctx = (struct pluginDevice*) s;
+ if (RPCConnect(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(RPCDisconnect(ctx));
+}
+
+/*
+ * nw_rpc100s_hostlist - API entry point to return the list of hosts
+ * for the devices on this NW_RPC100S 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 **
+nw_rpc100s_hostlist(StonithPlugin *s)
+{
+ char ** ret = NULL; /* list to return */
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Calling nw_rpc100s_hostlist (%s)", pluginid);
+ }
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ ctx = (struct pluginDevice*) s;
+
+ ret = OurImports->StringToHostList(ctx->node);
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ } else {
+ strdown(ret[0]);
+ }
+
+ return(ret);
+} /* end si_hostlist() */
+
+/*
+ * Parse the given configuration information, and stash it away...
+ *
+ * <info> contains the parameters specific to this type of object
+ *
+ * The format of <parameters> 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)
+ *
+ * stonith nodea NW_RPC100S /dev/ttyS0 nodeb 0
+ * stonith nodeb NW_RPC100S /dev/ttyS0 nodea 0 nodec 1
+ *
+ * Another possible configuration is for 2 stonith devices accessible
+ * through 2 different serial ports on nodeb:
+ *
+ * stonith nodeb NW_RPC100S /dev/ttyS0 nodea 0
+ * stonith nodeb NW_RPC100S /dev/ttyS1 nodec 0
+ */
+
+/*static int
+RPC_parse_config_info(struct pluginDevice* ctx, const char * info)
+{
+}*/
+
+
+/*
+ * RPCConnect -
+ *
+ * Connect to the given NW_RPC100S device.
+ * Side Effects
+ * 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
+RPCConnect(struct pluginDevice * ctx)
+{
+
+ /* 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;
+ }
+
+ }
+
+
+ /* Send a BOGUS string */
+ SENDCMD("//0,0,BOGUS;\r\n", 10);
+
+ /* Should reply with "Invalid Command" */
+ if (Debug) {
+ LOG(PIL_DEBUG, "Waiting for \"Invalid Entry\"");
+ }
+ EXPECT(ctx->fd, NWtokInvalidEntry, 12);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got Invalid Entry");
+ }
+ EXPECT(ctx->fd, NWtokCRNL, 2);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got NL");
+ }
+
+ return(S_OK);
+}
+
+static int
+RPCDisconnect(struct pluginDevice * ctx)
+{
+
+ 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;
+}
+
+/*
+ * RPCNametoOutlet - Map a hostname to an outlet number on this stonith device.
+ *
+ * Returns:
+ * 0 on success ( the outlet number on the RPS10 - there is only one )
+ * -1 on failure (host not found in the config file)
+ *
+ */
+static int
+RPCNametoOutlet ( struct pluginDevice * ctx, const char * host )
+{
+ int rc = -1;
+
+ if (!strcasecmp(ctx->node, host)) {
+ rc = 0;
+ }
+
+ return rc;
+}
+
+
+/*
+ * nw_rpc100s_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
+nw_rpc100s_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = S_OK;
+ int lorc = S_OK;
+ int outletnum = -1;
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Calling nw_rpc100s_reset (%s)", pluginid);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ctx = (struct pluginDevice*) s;
+
+ if ((rc = RPCConnect(ctx)) != S_OK) {
+ return(rc);
+ }
+
+ outletnum = RPCNametoOutlet(ctx, host);
+ LOG(PIL_DEBUG, "zk:outletname=%d", outletnum);
+
+ if (outletnum < 0) {
+ LOG(PIL_WARN, "%s doesn't control host [%s]"
+ , ctx->device, host);
+ RPCDisconnect(ctx);
+ return(S_BADHOST);
+ }
+
+ switch(request) {
+
+#if defined(ST_POWERON)
+ case ST_POWERON:
+ rc = RPCOn(ctx, outletnum, host);
+ break;
+#endif
+#if defined(ST_POWEROFF)
+ case ST_POWEROFF:
+ rc = RPCOff(ctx, outletnum, host);
+ break;
+#endif
+ case ST_GENERIC_RESET:
+ rc = RPCReset(ctx, outletnum, host);
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+
+ lorc = RPCDisconnect(ctx);
+
+ return(rc != S_OK ? rc : lorc);
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+nw_rpc100s_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ struct pluginDevice* ctx;
+ StonithNamesToGet namestocopy [] =
+ { {ST_TTYDEV, NULL}
+ , {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ if (s->isconfigured) {
+ return S_OOPS;
+ }
+
+ ctx = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ ctx->device = namestocopy[0].s_value;
+ ctx->node = namestocopy[1].s_value;
+
+ return S_OK;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+nw_rpc100s_get_confignames(StonithPlugin* p)
+{
+ static const char * RpcParams[] = {ST_TTYDEV , ST_HOSTLIST, NULL };
+ return RpcParams;
+}
+
+
+
+/*
+ * nw_rpc100s_getinfo - API entry point to retrieve something from the handle
+ */
+static const char *
+nw_rpc100s_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* ctx;
+ const char * ret;
+
+ 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 = "Micro Energetics Night/Ware RPC100S";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.microenergeticscorp.com/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = nw_rpc100sXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * nw_rpc100s_destroy - API entry point to destroy a NW_RPC100S Stonith object.
+ */
+static void
+nw_rpc100s_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* ctx;
+
+ VOIDERRIFWRONGDEV(s);
+
+ ctx = (struct pluginDevice *)s;
+
+ ctx->pluginid = NOTrpcid;
+
+ /* close the fd if open and set ctx->fd to invalid */
+ RPCDisconnect(ctx);
+
+ if (ctx->device != NULL) {
+ FREE(ctx->device);
+ ctx->device = NULL;
+ }
+ if (ctx->node != NULL) {
+ FREE(ctx->node);
+ ctx->node = NULL;
+ }
+ FREE(ctx);
+}
+
+/*
+ * nw_rpc100s_new - API entry point called to create a new NW_RPC100S Stonith
+ * device object.
+ */
+static StonithPlugin *
+nw_rpc100s_new(const char *subplugin)
+{
+ struct pluginDevice* ctx = ST_MALLOCT(struct pluginDevice);
+
+ if (ctx == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->pluginid = pluginid;
+ ctx->fd = -1;
+ ctx->device = NULL;
+ ctx->node = NULL;
+ ctx->idinfo = DEVICE;
+ ctx->sp.s_ops = &nw_rpc100sOps;
+
+ return &(ctx->sp);
+}
diff --git a/lib/plugins/stonith/rcd_serial.c b/lib/plugins/stonith/rcd_serial.c
new file mode 100644
index 0000000..f1396a7
--- /dev/null
+++ b/lib/plugins/stonith/rcd_serial.c
@@ -0,0 +1,602 @@
+/*
+ * Stonith module for RCD_SERIAL Stonith device
+ *
+ * Original code from null.c by
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * Copious borrowings from nw_rpc100s.c by
+ * Copyright (c) 2000 Computer Generation Incorporated
+ * Eric Z. Ayers <eric.ayers@compgen.com>
+ *
+ * and from apcsmart.c by
+ * Copyright (c) 2000 Andreas Piesk <a.piesk@gmx.net>
+ *
+ * Modifications for RC Delayed Serial Ciruit by
+ * Copyright (c) 2002 John Sutton <john@scl.co.uk>
+ *
+ * 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 "RC Delayed Serial"
+#include "stonith_plugin_common.h"
+#include "stonith_signal.h"
+
+#define PIL_PLUGIN rcd_serial
+#define PIL_PLUGIN_S "rcd_serial"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+
+#define ST_DTRRTS "dtr_rts"
+#define ST_MSDURATION "msduration"
+#define MAX_RCD_SERIALLINE 512
+
+#include <pils/plugin.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+static StonithPlugin* rcd_serial_new(const char *);
+static void rcd_serial_destroy(StonithPlugin *);
+static int rcd_serial_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * rcd_serial_get_confignames(StonithPlugin *);
+static const char * rcd_serial_getinfo(StonithPlugin * s, int InfoType);
+static int rcd_serial_status(StonithPlugin * );
+static int rcd_serial_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** rcd_serial_hostlist(StonithPlugin *);
+
+static struct stonith_ops rcd_serialOps ={
+ rcd_serial_new, /* Create new STONITH object */
+ rcd_serial_destroy, /* Destroy STONITH object */
+ rcd_serial_getinfo, /* Return STONITH info string */
+ rcd_serial_get_confignames,/* Return STONITH info string */
+ rcd_serial_set_config, /* Get configuration from NVpairs */
+ rcd_serial_status, /* Return STONITH device status */
+ rcd_serial_reset_req, /* Request a reset */
+ rcd_serial_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &rcd_serialOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/* ------------------- RCD specific stuff -------------- */
+
+/*
+ A diagram of a circuit suitable for use with this plugin is in
+ README.rcd_serial which should be somewhere in the distribution (if Alan
+ includes it ;-) and/or at http://www.scl.co.uk/rcd_serial/ (if I remember
+ to put it there ;-).
+
+ Once you've got this built, you can test things using the stonith command
+ as follows:
+
+ stonith -L
+ will show a list of plugin types, including rcd_serial
+
+ stonith -t rcd_serial testhost
+ will show required parameters
+
+ In these 3 you can either pass the params after the -p option or you can
+ put them in a config file and use -F configname instead of -p "param ...".
+
+ stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" -S
+ will show the status of the device
+
+ stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" -l
+ will list the single host testhost
+
+ stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" testhost
+ will reset testhost (provided testhost has its reset pins
+ suitably wired to the RTS signal coming out of port /dev/ttyS0
+ and that 1.5s is enough time to cause a reset ;-)
+*/
+
+/*
+ Define RCD_NOPAUSE if you are using the serial port for some purpose
+ _in_addition_ to using it as a stonith device. For example, I use one
+ of the input pins on the same serial port for monitoring the state of a
+ power supply. Periodically, a cron job has to open the port to read the
+ state of this input and thus has to clear down the output pins DTR and RTS
+ in order to avoid causing a spurious stonith reset. Now, if it should
+ happen that just at the same time as we are _really_ trying to do a stonith
+ reset, this cron job starts up, then the stonith reset won't occur ;-(.
+ To avoid this (albeit unlikely) outcome, you should #define RCD_NOPAUSE.
+ The effect of this is that instead of setting the line high just once and
+ then falling into a pause until an alarm goes off, rather, the program falls
+ into a loop which is continuously setting the line high. That costs us a bit
+ of CPU as compared with sitting in a pause, but hey, how often is this code
+ going to get exercised! Never, we hope...
+*/
+#undef RCD_NOPAUSE
+
+#ifdef RCD_NOPAUSE
+static int RCD_alarmcaught;
+#endif
+
+/*
+ * own prototypes
+ */
+
+static void RCD_alarm_handler(int sig);
+static int RCD_open_serial_port(char *device);
+static int RCD_close_serial_port(char *device, int fd);
+
+static void
+RCD_alarm_handler(int sig) {
+#if !defined(HAVE_POSIX_SIGNALS)
+ if (sig) {
+ signal(sig, SIG_DFL);
+ }else{
+ signal(sig, RCD_alarm_handler);
+ }
+#else
+ struct sigaction sa;
+ sigset_t sigmask;
+
+ /* Maybe a bit naughty but it works and it saves duplicating all */
+ /* this setup code - if handler called with 0 for sig, we install */
+ /* ourself as handler. */
+ if (sig) {
+ sa.sa_handler = (void (*)(int))SIG_DFL;
+ }else{
+ sa.sa_handler = RCD_alarm_handler;
+ }
+
+ sigemptyset(&sigmask);
+ sa.sa_mask = sigmask;
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, NULL);
+#endif
+
+#ifdef RCD_NOPAUSE
+ RCD_alarmcaught = 1;
+#endif
+ return;
+}
+
+static int
+RCD_open_serial_port(char *device) {
+ int fd;
+ int status;
+ int bothbits;
+
+ if (OurImports->TtyLock(device) < 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: ttylock failed.", __FUNCTION__);
+ }
+ return -1;
+ }
+
+ bothbits = TIOCM_RTS | TIOCM_DTR;
+
+ if ((fd = open(device, O_RDONLY | O_NDELAY)) != -1) {
+ /*
+ Opening the device always sets DTR & CTS high.
+ Clear them down immediately.
+ */
+ status = ioctl(fd, TIOCMBIC, &bothbits);
+ /* If there was an error clearing bits, set the fd to -1
+ * ( indicates error ) */
+ if (status != 0 ) {
+ fd = -1;
+ }
+ }
+
+ return fd;
+}
+
+static int
+RCD_close_serial_port(char *device, int fd) {
+ int rc = close(fd);
+ if (device != NULL) {
+ OurImports->TtyUnlock(device);
+ }
+ return rc;
+}
+
+/*
+ * RCD_Serial STONITH device.
+ */
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist; /* name of single host we can reset */
+ int hostcount; /* i.e. 1 after initialisation */
+ char * device; /* serial device name */
+ char * signal; /* either rts or dtr */
+ long msduration; /* how long (ms) to assert the signal */
+};
+
+static const char * pluginid = "RCD_SerialDevice-Stonith";
+static const char * NOTrcd_serialID = "RCD_Serial device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_DTRRTS_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_DTRRTS \
+ XML_PARM_SHORTDESC_END
+
+#define XML_DTRRTS_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The hardware handshaking technique to use with " ST_TTYDEV "(\"dtr\" or \"rts\")" \
+ XML_PARM_LONGDESC_END
+
+#define XML_DTRRTS_PARM \
+ XML_PARAMETER_BEGIN(ST_DTRRTS, "string", "1", "0") \
+ XML_DTRRTS_SHORTDESC \
+ XML_DTRRTS_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_MSDURATION_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_MSDURATION \
+ XML_PARM_SHORTDESC_END
+
+#define XML_MSDURATION_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The delay duration (in milliseconds) between the assertion of the control signal on " ST_TTYDEV " and the closing of the reset switch" \
+ XML_PARM_LONGDESC_END
+
+#define XML_MSDURATION_PARM \
+ XML_PARAMETER_BEGIN(ST_MSDURATION, "string", "1", "0") \
+ XML_MSDURATION_SHORTDESC \
+ XML_MSDURATION_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *rcd_serialXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_TTYDEV_PARM
+ XML_DTRRTS_PARM
+ XML_MSDURATION_PARM
+ XML_PARAMETERS_END;
+
+static int
+rcd_serial_status(StonithPlugin *s)
+{
+ struct pluginDevice* rcd;
+ int fd;
+ const char * err;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ rcd = (struct pluginDevice*) s;
+
+ /*
+ All we can do is make sure the serial device exists and
+ can be opened and closed without error.
+ */
+
+ if ((fd = RCD_open_serial_port(rcd->device)) == -1) {
+ err = strerror(errno);
+ LOG(PIL_CRIT, "%s: open of %s failed - %s",
+ __FUNCTION__, rcd->device, err);
+ return(S_OOPS);
+ }
+
+ if (RCD_close_serial_port(rcd->device, fd) != 0) {
+ err = strerror(errno);
+ LOG(PIL_CRIT, "%s: close of %s failed - %s",
+ __FUNCTION__, rcd->device, err);
+ return(S_OOPS);
+ }
+
+ return S_OK;
+}
+
+
+/*
+ * Return the list of hosts configured for this RCD_SERIAL device
+ */
+static char **
+rcd_serial_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* rcd;
+
+ ERRIFWRONGDEV(s,NULL);
+ rcd = (struct pluginDevice*) s;
+ if (rcd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in RCD_SERIAL_list_hosts");
+ return(NULL);
+ }
+
+ return OurImports->CopyHostList((const char * const *)rcd->hostlist);
+}
+
+/*
+ * At last, we really do it! I don't know what the request argument
+ * is so am just ignoring it...
+ */
+static int
+rcd_serial_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice* rcd;
+ int fd;
+ int sigbit;
+ struct itimerval timer;
+ const char * err;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ rcd = (struct pluginDevice *) s;
+
+ /* check that host matches */
+ if (strcasecmp(host, rcd->hostlist[0])) {
+ LOG(PIL_CRIT, "%s: host '%s' not in hostlist.",
+ __FUNCTION__, host);
+ return(S_BADHOST);
+ }
+
+ /* Set the appropriate bit for the signal */
+ sigbit = *(rcd->signal)=='r' ? TIOCM_RTS : TIOCM_DTR;
+
+ /* Set up the timer */
+ timer.it_interval.tv_sec = 0;
+ timer.it_interval.tv_usec = 0;
+ timer.it_value.tv_sec = rcd->msduration / 1000;
+ timer.it_value.tv_usec = (rcd->msduration % 1000) * 1000;
+
+ /* Open the device */
+ if ((fd = RCD_open_serial_port(rcd->device)) == -1) {
+#ifdef HAVE_STRERROR
+ err = strerror(errno);
+#else
+ err = sys_errlist[errno];
+#endif
+ LOG(PIL_CRIT, "%s: open of %s failed - %s",
+ __FUNCTION__, rcd->device, err);
+ return(S_OOPS);
+ }
+
+ /* Start the timer */
+ RCD_alarm_handler(0);
+#ifdef RCD_NOPAUSE
+ RCD_alarmcaught = 0;
+#endif
+ setitimer(ITIMER_REAL, &timer, 0);
+
+ /* Set the line high */
+ ioctl(fd, TIOCMBIS, &sigbit);
+
+ /* Wait for the alarm signal */
+#ifdef RCD_NOPAUSE
+ while(!RCD_alarmcaught) ioctl(fd, TIOCMBIS, &sigbit);
+#else
+ pause();
+#endif
+
+ /* Clear the line low */
+ ioctl(fd, TIOCMBIC, &sigbit);
+
+ /* Close the port */
+ if (RCD_close_serial_port(rcd->device, fd) != 0) {
+ err = strerror(errno);
+ LOG(PIL_CRIT, "%s: close of %s failed - %s",
+ __FUNCTION__, rcd->device, err);
+ return(S_OOPS);
+ }
+
+ LOG(PIL_INFO,"Host rcd_serial-reset: %s", host);
+ return S_OK;
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+rcd_serial_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ struct pluginDevice* rcd;
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTLIST, NULL}
+ , {ST_TTYDEV, NULL}
+ , {ST_DTRRTS, NULL}
+ , {ST_MSDURATION, NULL}
+ , {NULL, NULL}
+ };
+ char *endptr;
+ int rc = 0;
+
+ LOG(PIL_DEBUG, "%s:called", __FUNCTION__);
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ if (s->isconfigured) {
+ return S_OOPS;
+ }
+
+ rcd = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+
+ if ((rcd->hostlist = (char **)MALLOC(2*sizeof(char*))) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__);
+ FREE(namestocopy[0].s_value);
+ FREE(namestocopy[1].s_value);
+ FREE(namestocopy[2].s_value);
+ FREE(namestocopy[3].s_value);
+ return S_OOPS;
+ }
+ rcd->hostlist[0] = namestocopy[0].s_value;
+ strdown(rcd->hostlist[0]);
+ rcd->hostlist[1] = NULL;
+ rcd->hostcount = 1;
+ rcd->device = namestocopy[1].s_value;
+ rcd->signal = namestocopy[2].s_value;
+ if (strcmp(rcd->signal, "rts") && strcmp(rcd->signal, "dtr")) {
+ LOG(PIL_CRIT, "%s: Invalid signal name '%s'",
+ pluginid, rcd->signal);
+ FREE(namestocopy[3].s_value);
+ return S_BADCONFIG;
+ }
+
+ errno = 0;
+ rcd->msduration = strtol(namestocopy[3].s_value, &endptr, 0);
+ if (((errno == ERANGE)
+ && (rcd->msduration == LONG_MIN || rcd->msduration == LONG_MAX))
+ || *endptr != 0 || rcd->msduration < 1) {
+ LOG(PIL_CRIT, "%s: Invalid msduration '%s'",
+ pluginid, namestocopy[3].s_value);
+ FREE(namestocopy[3].s_value);
+ return S_BADCONFIG;
+ }
+ FREE(namestocopy[3].s_value);
+
+ return S_OK;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+rcd_serial_get_confignames(StonithPlugin* p)
+{
+ static const char * RcdParams[] = {ST_HOSTLIST, ST_TTYDEV
+ , ST_DTRRTS, ST_MSDURATION, NULL };
+ return RcdParams;
+}
+
+/*
+ * Return STONITH info string
+ */
+static const char *
+rcd_serial_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* rcd;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ rcd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = rcd->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = rcd->device;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "RC Delayed Serial STONITH Device\n"
+ "This device can be constructed cheaply from"
+ " readily available components,\n"
+ "with sufficient expertise and testing.\n"
+ "See README.rcd_serial for circuit diagram.\n";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.scl.co.uk/rcd_serial/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = rcd_serialXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * RCD_SERIAL Stonith destructor...
+ */
+static void
+rcd_serial_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* rcd;
+
+ VOIDERRIFWRONGDEV(s);
+
+ rcd = (struct pluginDevice *)s;
+
+ rcd->pluginid = NOTrcd_serialID;
+ if (rcd->hostlist) {
+ stonith_free_hostlist(rcd->hostlist);
+ rcd->hostlist = NULL;
+ }
+ rcd->hostcount = -1;
+ if (rcd->device) {
+ FREE(rcd->device);
+ }
+ if (rcd->signal) {
+ FREE(rcd->signal);
+ }
+ FREE(rcd);
+}
+
+/*
+ * Create a new RCD_Serial Stonith device.
+ * Too bad this function can't be static. (Hmm, weird, it _is_ static?)
+ */
+static StonithPlugin *
+rcd_serial_new(const char *subplugin)
+{
+ struct pluginDevice* rcd = ST_MALLOCT(struct pluginDevice);
+
+ if (rcd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(rcd, 0, sizeof(*rcd));
+
+ rcd->pluginid = pluginid;
+ rcd->hostlist = NULL;
+ rcd->hostcount = -1;
+ rcd->device = NULL;
+ rcd->signal = NULL;
+ rcd->msduration = 0;
+ rcd->idinfo = DEVICE;
+ rcd->sp.s_ops = &rcd_serialOps;
+
+ return &(rcd->sp);
+}
diff --git a/lib/plugins/stonith/rhcs.c b/lib/plugins/stonith/rhcs.c
new file mode 100644
index 0000000..293a081
--- /dev/null
+++ b/lib/plugins/stonith/rhcs.c
@@ -0,0 +1,1035 @@
+/*
+ * Stonith module for RedHat Cluster Suite fencing plugins
+ *
+ * Copyright (c) 2001 SuSE Linux AG
+ * Portions Copyright (c) 2004, tummy.com, ltd.
+ *
+ * Based on ssh.c, Authors: Joachim Gleissner <jg@suse.de>,
+ * Lars Marowsky-Bree <lmb@suse.de>
+ * Modified for external.c: Scott Kleihege <scott@tummy.com>
+ * Reviewed, tested, and config parsing: Sean Reifschneider <jafo@tummy.com>
+ * And overhauled by Lars Marowsky-Bree <lmb@suse.de>, so the circle
+ * closes...
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ * Changed to allow full-featured external plugins by Dave Blaschke
+ * <debltc@us.ibm.com>
+ * Modified for rhcs.c: Dejan Muhamedagic <dejan@suse.de>
+ *
+ * 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>
+
+#include <dirent.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/xmlreader.h>
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN rhcs
+#define PIL_PLUGIN_S "rhcs"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+
+#include <pils/plugin.h>
+
+static StonithPlugin * rhcs_new(const char *);
+static void rhcs_destroy(StonithPlugin *);
+static int rhcs_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * rhcs_get_confignames(StonithPlugin *);
+static const char * rhcs_getinfo(StonithPlugin * s, int InfoType);
+static int rhcs_status(StonithPlugin * );
+static int rhcs_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** rhcs_hostlist(StonithPlugin *);
+
+static struct stonith_ops rhcsOps ={
+ rhcs_new, /* Create new STONITH object */
+ rhcs_destroy, /* Destroy STONITH object */
+ rhcs_getinfo, /* Return STONITH info string */
+ rhcs_get_confignames, /* Return STONITH info string */
+ rhcs_set_config, /* Get configuration from NVpairs */
+ rhcs_status, /* Return STONITH device status */
+ rhcs_reset_req, /* Request a reset */
+ rhcs_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &rhcsOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * RHCS STONITH device
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ GHashTable * cmd_opts;
+ char * subplugin;
+ char ** confignames;
+ char * hostlist;
+ char * outputbuf;
+ xmlDoc * metadata;
+};
+
+static const char * pluginid = "RHCSDevice-Stonith";
+static const char * NOTpluginID = "RHCS device has been destroyed";
+
+/* Prototypes */
+
+/* Run the command with op and return the exit status + the output
+ * (NULL -> discard output) */
+static int rhcs_run_cmd(struct pluginDevice *sd, const char *op,
+ const char *host, char **output);
+/* Just free up the configuration and the memory, if any */
+static void rhcs_unconfig(struct pluginDevice *sd);
+
+static int
+rhcs_status(StonithPlugin *s)
+{
+ struct pluginDevice * sd;
+ const char * op = "monitor";
+ int rc;
+ char * output = NULL;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ rc = rhcs_run_cmd(sd, op, NULL, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ }
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ }
+ if (output) {
+ FREE(output);
+ }
+ return rc;
+}
+
+static int
+get_num_tokens(char *str)
+{
+ int namecount = 0;
+
+ if (!str)
+ return namecount;
+ while (*str != EOS) {
+ str += strspn(str, WHITESPACE);
+ if (*str == EOS)
+ break;
+ str += strcspn(str, WHITESPACE);
+ namecount++;
+ }
+ return namecount;
+}
+
+static char **
+rhcs_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+ const char * op = "gethosts";
+ int i, namecount;
+ char ** ret;
+ char * tmp;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(NULL);
+ }
+
+ namecount = get_num_tokens(sd->hostlist);
+ ret = MALLOC((namecount+1)*sizeof(char *));
+ if (!ret) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return NULL;
+ }
+ memset(ret, 0, (namecount+1)*sizeof(char *));
+
+ /* White-space split the sd->hostlist here */
+ i = 0;
+ tmp = strtok(sd->hostlist, WHITESPACE);
+ while (tmp != NULL) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s host %s",
+ __FUNCTION__, sd->subplugin, tmp);
+ }
+ ret[i] = STRDUP(tmp);
+ if (!ret[i]) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ stonith_free_hostlist(ret);
+ return NULL;
+ }
+ i++;
+ tmp = strtok(NULL, WHITESPACE);
+ }
+
+ if (i == 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' returned an empty hostlist",
+ __FUNCTION__, sd->subplugin, op);
+ stonith_free_hostlist(ret);
+ ret = NULL;
+ }
+
+ return(ret);
+}
+
+static int
+rhcs_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice * sd;
+ const char * op;
+ int rc;
+ char * output = NULL;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Host rhcs-reset initiating on %s", host);
+ }
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ switch (request) {
+ case ST_GENERIC_RESET:
+ op = "reboot";
+ break;
+
+ case ST_POWEROFF:
+ op = "off";
+ break;
+
+ case ST_POWERON:
+ op = "on";
+ break;
+
+ default:
+ LOG(PIL_CRIT, "%s: Unknown stonith request %d",
+ __FUNCTION__, request);
+ return S_OOPS;
+ break;
+ }
+
+ rc = rhcs_run_cmd(sd, op, host, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' for host %s failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, host, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ FREE(output);
+ }
+ return S_RESETFAIL;
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ if (output) {
+ LOG(PIL_INFO, "plugin output: %s", output);
+ FREE(output);
+ }
+ return S_OK;
+ }
+
+}
+
+static int
+rhcs_parse_config_info(struct pluginDevice* sd, StonithNVpair * info)
+{
+ char * key;
+ char * value;
+ StonithNVpair * nv;
+
+ sd->hostlist = NULL;
+ sd->cmd_opts = g_hash_table_new(g_str_hash, g_str_equal);
+
+ /* TODO: Maybe treat "" as delimeters too so
+ * whitespace can be passed to the plugins... */
+ for (nv = info; nv->s_name; nv++) {
+ if (!nv->s_name || !nv->s_value) {
+ continue;
+ }
+ key = STRDUP(nv->s_name);
+ if (!key) {
+ goto err_mem;
+ }
+ value = STRDUP(nv->s_value);
+ if (!value) {
+ FREE(key);
+ goto err_mem;
+ }
+ if (!strcmp(key,"hostlist")) {
+ sd->hostlist = value;
+ FREE(key);
+ } else {
+ g_hash_table_insert(sd->cmd_opts, key, value);
+ }
+ }
+
+ return(S_OK);
+
+err_mem:
+ LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__);
+ rhcs_unconfig(sd);
+
+ return(S_OOPS);
+}
+
+static gboolean
+let_remove_eachitem(gpointer key, gpointer value, gpointer user_data)
+{
+ if (key) {
+ FREE(key);
+ }
+ if (value) {
+ FREE(value);
+ }
+ return TRUE;
+}
+
+static void
+rhcs_unconfig(struct pluginDevice *sd) {
+ if (sd->cmd_opts) {
+ g_hash_table_foreach_remove(sd->cmd_opts,
+ let_remove_eachitem, NULL);
+ g_hash_table_destroy(sd->cmd_opts);
+ sd->cmd_opts = NULL;
+ }
+ if (sd->hostlist) {
+ FREE(sd->hostlist);
+ sd->hostlist = NULL;
+ }
+ if (sd->metadata) {
+ xmlFreeDoc(sd->metadata);
+ xmlCleanupParser();
+ sd->metadata = NULL;
+ }
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+rhcs_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ struct pluginDevice * sd;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ /* make sure that command has not already been set */
+ if (s->isconfigured) {
+ return(S_OOPS);
+ }
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+#if 0
+ /* the required parameters may be acquired from the metadata
+ * */
+ if (sd->confignames == NULL) {
+ /* specified by name=value pairs, check required parms */
+ if (rhcs_get_confignames(s) == NULL) {
+ return(S_OOPS);
+ }
+
+ for (p = sd->confignames; *p; p++) {
+ if (OurImports->GetValue(list, *p) == NULL) {
+ LOG(PIL_INFO, "Cannot get parameter %s from "
+ "StonithNVpair", *p);
+ }
+ }
+ }
+#endif
+
+ return rhcs_parse_config_info(sd, list);
+}
+
+
+/* Only interested in regular files starting with fence_ that are also executable */
+static int
+rhcs_exec_select(const struct dirent *dire)
+{
+ struct stat statf;
+ char filename[FILENAME_MAX];
+ int rc;
+
+ rc = snprintf(filename, FILENAME_MAX, "%s/%s",
+ STONITH_RHCS_PLUGINDIR, dire->d_name);
+ if (rc <= 0 || rc >= FILENAME_MAX) {
+ return 0;
+ }
+
+ if ((stat(filename, &statf) == 0) &&
+ (S_ISREG(statf.st_mode)) &&
+ (statf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) {
+ if (statf.st_mode & (S_IWGRP|S_IWOTH)) {
+ LOG(PIL_WARN, "Executable file %s ignored "
+ "(writable by group/others)", filename);
+ return 0;
+ }else{
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static xmlDoc *
+load_metadata(struct pluginDevice * sd)
+{
+ xmlDoc *doc = NULL;
+ const char *op = "metadata";
+ int rc;
+ char *ret = NULL;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ rc = rhcs_run_cmd(sd, op, NULL, &ret);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (ret) {
+ LOG(PIL_CRIT, "plugin output: %s", ret);
+ FREE(ret);
+ }
+ goto err;
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+
+ doc = xmlParseMemory(ret, strlen(ret));
+ if (!doc) {
+ LOG(PIL_CRIT, "%s: could not parse metadata",
+ __FUNCTION__);
+ goto err;
+ }
+ sd->metadata = doc;
+
+err:
+ if (ret) {
+ FREE(ret);
+ }
+ return doc;
+}
+
+static const char *skip_attrs[] = {
+ "action", "verbose", "debug", "version", "help", "separator",
+ NULL
+};
+/* XML stuff */
+typedef int (*node_proc)
+ (xmlNodeSet *nodes, struct pluginDevice *sd);
+
+static int
+proc_xpath(const char *xpathexp, struct pluginDevice *sd, node_proc fun)
+{
+ xmlXPathObject *xpathObj = NULL;
+ xmlXPathContext *xpathCtx = NULL;
+ int rc = 1;
+
+ if (!sd->metadata && !load_metadata(sd)) {
+ LOG(PIL_INFO, "%s: no metadata", __FUNCTION__);
+ return 1;
+ }
+
+ /* Create xpath evaluation context */
+ xpathCtx = xmlXPathNewContext(sd->metadata);
+ if(xpathCtx == NULL) {
+ LOG(PIL_CRIT, "%s: unable to create new XPath context", __FUNCTION__);
+ return 1;
+ }
+ /* Evaluate xpath expression */
+ xpathObj = xmlXPathEvalExpression((const xmlChar*)xpathexp, xpathCtx);
+ if(xpathObj == NULL) {
+ LOG(PIL_CRIT, "%s: unable to evaluate expression %s",
+ __FUNCTION__, xpathexp);
+ goto err;
+ }
+
+ if (sd->outputbuf != NULL) {
+ FREE(sd->outputbuf);
+ sd->outputbuf = NULL;
+ }
+ rc = fun(xpathObj->nodesetval, sd);
+err:
+ if (xpathObj)
+ xmlXPathFreeObject(xpathObj);
+ if (xpathCtx)
+ xmlXPathFreeContext(xpathCtx);
+ return rc;
+}
+
+static int
+load_confignames(xmlNodeSet *nodes, struct pluginDevice *sd)
+{
+ xmlChar *attr;
+ const char * const*skip;
+ xmlNode *cur;
+ int i, j, namecount;
+
+ namecount = nodes->nodeNr;
+ if (!namecount) {
+ LOG(PIL_INFO, "%s: no configuration parameters", __FUNCTION__);
+ return 1;
+ }
+ sd->confignames = (char **)MALLOC((namecount+1)*sizeof(char *));
+ if (sd->confignames == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return 1;
+ }
+
+ /* now copy over confignames */
+ j = 0;
+ for (i = 0; i < nodes->nodeNr; i++) {
+ cur = nodes->nodeTab[i];
+ attr = xmlGetProp(cur, (const xmlChar*)"name");
+ for (skip = skip_attrs; *skip; skip++) {
+ if (!strcmp(*skip,(char *)attr))
+ goto skip;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s configname %s",
+ __FUNCTION__, sd->subplugin, (char *)attr);
+ }
+ sd->confignames[j++] = strdup((char *)attr);
+ xmlFree(attr);
+ skip:
+ continue;
+ }
+ sd->confignames[j] = NULL;
+
+ return 0;
+}
+
+static int
+dump_content(xmlNodeSet *nodes, struct pluginDevice *sd)
+{
+ xmlChar *content = NULL;
+ xmlNode *cur;
+ int rc = 1;
+
+ if (!nodes || !nodes->nodeTab || !nodes->nodeTab[0]) {
+ LOG(PIL_WARN, "%s: %s no nodes",
+ __FUNCTION__, sd->subplugin);
+ return 1;
+ }
+ cur = nodes->nodeTab[0];
+ content = xmlNodeGetContent(cur);
+ if (content && strlen((char *)content) > 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s found content for %s",
+ __FUNCTION__, sd->subplugin, cur->name);
+ }
+ sd->outputbuf = STRDUP((char *)content);
+ rc = !(*sd->outputbuf);
+ } else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s no content for %s",
+ __FUNCTION__, sd->subplugin, cur->name);
+ }
+ rc = 1;
+ }
+
+ if (content)
+ xmlFree(content);
+ return rc;
+}
+
+static int
+dump_params_xml(xmlNodeSet *nodes, struct pluginDevice *sd)
+{
+ int len = 0;
+ xmlNode *cur;
+ xmlBuffer *xml_buffer = NULL;
+ int rc = 0;
+
+ xml_buffer = xmlBufferCreate();
+ if (!xml_buffer) {
+ LOG(PIL_CRIT, "%s: failed to create xml buffer", __FUNCTION__);
+ return 1;
+ }
+ cur = nodes->nodeTab[0];
+ len = xmlNodeDump(xml_buffer, sd->metadata, cur, 0, TRUE);
+ if (len <= 0) {
+ LOG(PIL_CRIT, "%s: could not dump xml for %s",
+ __FUNCTION__, (char *)xmlGetProp(cur, (const xmlChar*)"name"));
+ rc = 1;
+ goto err;
+ }
+ sd->outputbuf = STRDUP((char *)xml_buffer->content);
+err:
+ xmlBufferFree(xml_buffer);
+ return rc;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+rhcs_get_confignames(StonithPlugin* p)
+{
+ struct pluginDevice * sd;
+ int i;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ sd = (struct pluginDevice *)p;
+
+ if (sd->subplugin != NULL) {
+ if (!sd->metadata && !load_metadata(sd)) {
+ return NULL;
+ }
+ proc_xpath("/resource-agent/parameters/parameter", sd, load_confignames);
+ } else {
+ /* return list of subplugins in rhcs directory */
+ struct dirent ** files = NULL;
+ int dircount;
+
+ /* get the rhcs plugin's confignames (list of subplugins) */
+ dircount = scandir(STONITH_RHCS_PLUGINDIR, &files,
+ SCANSEL_CAST rhcs_exec_select, NULL);
+ if (dircount < 0) {
+ return NULL;
+ }
+
+ sd->confignames = (char **)MALLOC((dircount+1)*sizeof(char *));
+ if (!sd->confignames) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return NULL;
+ }
+
+ for (i = 0; i < dircount; i++) {
+ sd->confignames[i] = STRDUP(files[i]->d_name+strlen("fence_"));
+ free(files[i]);
+ files[i] = NULL;
+ }
+ free(files);
+ sd->confignames[dircount] = NULL;
+ }
+
+ return (const char * const *)sd->confignames;
+}
+
+/*
+ * Return STONITH info string
+ */
+static const char *
+fake_op(struct pluginDevice * sd, const char *op)
+{
+ const char *pfx = "RHCS plugin ";
+ char *ret = NULL;
+
+ LOG(PIL_INFO, "rhcs plugins don't really support %s", op);
+ ret = MALLOC(strlen(pfx) + strlen(op) + 1);
+ strcpy(ret, pfx);
+ strcat(ret, op);
+ sd->outputbuf = ret;
+ return(ret);
+}
+
+static const char *
+rhcs_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* sd;
+ const char * op;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+ sd = (struct pluginDevice *)s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(NULL);
+ }
+
+ if (!sd->metadata && !load_metadata(sd)) {
+ return NULL;
+ }
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ op = "getinfo-devid";
+ return fake_op(sd, op);
+ break;
+
+ case ST_DEVICENAME:
+ if (!proc_xpath("/resource-agent/shortdesc", sd, dump_content)) {
+ return sd->outputbuf;
+ } else {
+ op = "getinfo-devname";
+ return fake_op(sd, op);
+ }
+ break;
+
+ case ST_DEVICEDESCR:
+ if (!proc_xpath("/resource-agent/longdesc", sd, dump_content)) {
+ return sd->outputbuf;
+ } else {
+ op = "getinfo-devdescr";
+ return fake_op(sd, op);
+ }
+ break;
+
+ case ST_DEVICEURL:
+ op = "getinfo-devurl";
+ return fake_op(sd, op);
+ break;
+
+ case ST_CONF_XML:
+ if (!proc_xpath("/resource-agent/parameters", sd, dump_params_xml)) {
+ return sd->outputbuf;
+ }
+ break;
+
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+/*
+ * RHCS Stonith destructor...
+ */
+static void
+rhcs_destroy(StonithPlugin *s)
+{
+ struct pluginDevice * sd;
+ char ** p;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd = (struct pluginDevice *)s;
+
+ sd->pluginid = NOTpluginID;
+ rhcs_unconfig(sd);
+ if (sd->confignames != NULL) {
+ for (p = sd->confignames; *p; p++) {
+ FREE(*p);
+ }
+ FREE(sd->confignames);
+ sd->confignames = NULL;
+ }
+ if (sd->subplugin != NULL) {
+ FREE(sd->subplugin);
+ sd->subplugin = NULL;
+ }
+ if (sd->outputbuf != NULL) {
+ FREE(sd->outputbuf);
+ sd->outputbuf = NULL;
+ }
+ FREE(sd);
+}
+
+/* Create a new rhcs Stonith device */
+static StonithPlugin *
+rhcs_new(const char *subplugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ if (subplugin != NULL) {
+ sd->subplugin = STRDUP(subplugin);
+ if (sd->subplugin == NULL) {
+ FREE(sd);
+ return(NULL);
+ }
+ }
+ sd->sp.s_ops = &rhcsOps;
+ return &(sd->sp);
+}
+
+#define MAXLINE 512
+
+static void
+printparam_to_fd(int fd, const char *key, const char *value)
+{
+ char arg[MAXLINE];
+ int cnt;
+
+ cnt = snprintf(arg, MAXLINE, "%s=%s\n", key, value);
+ if (cnt <= 0 || cnt >= MAXLINE) {
+ LOG(PIL_CRIT, "%s: param/value pair too large", __FUNCTION__);
+ return;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "set rhcs plugin param '%s=%s'", key, value);
+ }
+ if (write(fd, arg, cnt) < 0) {
+ LOG(PIL_CRIT, "%s: write: %m", __FUNCTION__);
+ }
+}
+
+static void
+rhcs_print_var(gpointer key, gpointer value, gpointer user_data)
+{
+ printparam_to_fd(GPOINTER_TO_UINT(user_data), (char *)key, (char *)value);
+}
+
+/* Run the command with op as command line argument(s) and return the exit
+ * status + the output */
+
+static int
+rhcs_run_cmd(struct pluginDevice *sd, const char *op, const char *host, char **output)
+{
+ const int BUFF_LEN=4096;
+ char buff[BUFF_LEN];
+ int read_len = 0;
+ int rc;
+ char * data = NULL;
+ char cmd[FILENAME_MAX+64];
+ struct stat buf;
+ int slen;
+ int pid, status;
+ int fd1[2]; /* our stdout/their stdin */
+ int fd2[2]; /* our stdin/their stdout and stderr */
+
+ rc = snprintf(cmd, FILENAME_MAX, "%s/fence_%s",
+ STONITH_RHCS_PLUGINDIR, sd->subplugin);
+ if (rc <= 0 || rc >= FILENAME_MAX) {
+ LOG(PIL_CRIT, "%s: external command too long.", __FUNCTION__);
+ return -1;
+ }
+
+ if (stat(cmd, &buf) != 0) {
+ LOG(PIL_CRIT, "%s: stat(2) of %s failed: %s",
+ __FUNCTION__, cmd, strerror(errno));
+ return -1;
+ }
+
+ if (!S_ISREG(buf.st_mode)
+ || (!(buf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) {
+ LOG(PIL_CRIT, "%s: %s found NOT to be executable.",
+ __FUNCTION__, cmd);
+ return -1;
+ }
+
+ if (buf.st_mode & (S_IWGRP|S_IWOTH)) {
+ LOG(PIL_CRIT, "%s: %s found to be writable by group/others, "
+ "NOT executing for security purposes.",
+ __FUNCTION__, cmd);
+ return -1;
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: Calling '%s'", __FUNCTION__, cmd );
+ }
+
+ if (pipe(fd1) || pipe(fd2))
+ goto err;
+
+ pid = fork();
+ if (pid < 0) {
+ LOG(PIL_CRIT, "%s: fork: %m", __FUNCTION__);
+ goto err;
+ }
+ if (pid) { /* parent */
+ close(fd1[0]);
+ close(fd2[1]);
+
+ if (sd->cmd_opts) {
+ printparam_to_fd(fd1[1], "agent", sd->subplugin);
+ printparam_to_fd(fd1[1], "action", op);
+ if( host )
+ printparam_to_fd(fd1[1], "nodename", host);
+ g_hash_table_foreach(sd->cmd_opts, rhcs_print_var,
+ GUINT_TO_POINTER(fd1[1]));
+ }
+ close(fd1[1]); /* we have nothing more to say */
+
+ fcntl(fd2[0], F_SETFL, fcntl(fd2[0], F_GETFL, 0) | O_NONBLOCK);
+ data = NULL;
+ slen=0;
+ data = MALLOC(1);
+ /* read stdout/stderr from the fence agent */
+ do {
+ data[slen]=EOS;
+ read_len = read(fd2[0], buff, BUFF_LEN);
+ if (read_len > 0) {
+ data=REALLOC(data, slen+read_len+1);
+ if (data == NULL) {
+ goto err;
+ }
+ memcpy(data+slen, buff, read_len);
+ slen += read_len;
+ data[slen] = EOS;
+ } else if (read_len < 0) {
+ if (errno == EAGAIN)
+ continue;
+ LOG(PIL_CRIT, "%s: read from pipe: %m", __FUNCTION__);
+ goto err;
+ }
+ } while (read_len);
+
+ if (!data) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ goto err;
+ }
+ close(fd2[0]);
+ waitpid(pid, &status, 0);
+ if (!WIFEXITED(status)) {
+ LOG(PIL_CRIT, "%s: fence agent failed: %m", __FUNCTION__);
+ goto err;
+ } else {
+ rc = WEXITSTATUS(status);
+ if (rc) {
+ LOG(PIL_CRIT, "%s: fence agent exit code: %d",
+ __FUNCTION__, rc);
+ goto err;
+ }
+ }
+ } else { /* child */
+ close(fd1[1]);
+ close(fd2[0]);
+ close(STDIN_FILENO);
+ if (dup(fd1[0]) < 0)
+ goto err;
+ close(fd1[0]);
+ close(STDOUT_FILENO);
+ if (dup(fd2[1]) < 0)
+ goto err;
+ close(STDERR_FILENO);
+ if (dup(fd2[1]) < 0)
+ goto err;
+ close(fd2[1]);
+ rc = sd->cmd_opts ?
+ execlp(cmd, cmd, NULL) : execlp(cmd, cmd, "-o", op, NULL);
+ if (rc < 0) {
+ LOG(PIL_CRIT, "%s: Calling '%s' failed: %m",
+ __FUNCTION__, cmd);
+ }
+ goto err;
+ }
+
+ if (Debug && data) {
+ LOG(PIL_DEBUG, "%s: '%s' output: %s", __FUNCTION__, cmd, data);
+ }
+
+ if (output) {
+ *output = data;
+ } else {
+ FREE(data);
+ }
+
+ return 0;
+
+err:
+ if (data) {
+ FREE(data);
+ }
+ if (output) {
+ *output = NULL;
+ }
+
+ return(-1);
+
+}
diff --git a/lib/plugins/stonith/ribcl.py.in b/lib/plugins/stonith/ribcl.py.in
new file mode 100644
index 0000000..14e070c
--- /dev/null
+++ b/lib/plugins/stonith/ribcl.py.in
@@ -0,0 +1,101 @@
+#!@PYTHON@
+
+
+#
+# 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
+#
+
+import sys
+import socket
+from httplib import *
+from time import sleep
+
+
+argv = sys.argv
+
+
+try:
+ host = argv[1].split('.')[0]+'-rm'
+ cmd = argv[2]
+except IndexError:
+ print "Not enough arguments"
+ sys.exit(1)
+
+
+login = [ '<RIBCL VERSION="1.2">',
+ '<LOGIN USER_LOGIN="Administrator" PASSWORD="********">' ]
+
+
+logout = [ '</LOGIN>', '</RIBCL>' ]
+
+
+status = [ '<SERVER_INFO MODE="read">', '<GET_HOST_POWER_STATUS/>',
+ '</SERVER_INFO>' ]
+
+
+reset = [ '<SERVER_INFO MODE="write">', '<RESET_SERVER/>', '</SERVER_INFO>' ]
+
+
+off = [ '<SERVER_INFO MODE = "write">', '<SET_HOST_POWER HOST_POWER = "N"/>',
+ '</SERVER_INFO>' ]
+
+
+on = [ '<SERVER_INFO MODE = "write">', '<SET_HOST_POWER HOST_POWER = "Y"/>',
+ '</SERVER_INFO>' ]
+
+
+todo = { 'reset':reset, 'on':on, 'off':off, 'status':status }
+
+
+acmds=[]
+try:
+ if cmd == 'reset' and host.startswith('gfxcl'):
+ acmds.append(login + todo['off'] + logout)
+ acmds.append(login + todo['on'] + logout)
+ else:
+ acmds.append(login + todo[cmd] + logout)
+except KeyError:
+ print "Invalid command: "+ cmd
+ sys.exit(1)
+
+
+try:
+ for cmds in acmds:
+
+
+ c=HTTPSConnection(host)
+ c.send('<?xml version="1.0"?>\r\n')
+ c.sock.recv(1024)
+
+
+ for line in cmds:
+ c.send(line+'\r\n')
+ c.sock.recv(1024)
+
+
+ c.close()
+ sleep(1)
+
+
+except socket.gaierror, msg:
+ print msg
+ sys.exit(1)
+except socket.sslerror, msg:
+ print msg
+ sys.exit(1)
+except socket.error, msg:
+ print msg
+ sys.exit(1)
+
diff --git a/lib/plugins/stonith/riloe.c b/lib/plugins/stonith/riloe.c
new file mode 100644
index 0000000..a4a8312
--- /dev/null
+++ b/lib/plugins/stonith/riloe.c
@@ -0,0 +1,338 @@
+/*
+ * Stonith module for RILOE Stonith device
+ *
+ * Copyright (c) 2004 Alain St-Denis <alain.st-denis@ec.gc.ca>
+ *
+ * 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
+ *
+ */
+
+#define DEVICE "Compaq RILOE"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN riloe
+#define PIL_PLUGIN_S "riloe"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * riloe_new(const char *);
+static void riloe_destroy(StonithPlugin *);
+static int riloe_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * riloe_get_confignames(StonithPlugin * );
+static const char * riloe_getinfo(StonithPlugin * s, int InfoType);
+static int riloe_status(StonithPlugin * );
+static int riloe_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** riloe_hostlist(StonithPlugin *);
+
+static struct stonith_ops riloeOps ={
+ riloe_new, /* Create new STONITH object */
+ riloe_destroy, /* Destroy STONITH object */
+ riloe_getinfo, /* Return STONITH info string */
+ riloe_get_confignames, /* Return STONITH info string */
+ riloe_set_config, /* Get configuration from NVpairs */
+ riloe_status, /* Return STONITH device status */
+ riloe_reset_req, /* Request a reset */
+ riloe_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &riloeOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+#define RILOE_COMMAND STONITH_MODULES "/ribcl.py"
+
+/*
+ * Riloe STONITH device. We are very agreeable, but don't do much :-)
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist;
+ int hostcount;
+};
+
+static const char * pluginid = "RiloeDevice-Stonith";
+static const char * NOTriloeID = "Riloe device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *riloeXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+static int
+riloe_status(StonithPlugin *s)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ return S_OK;
+}
+
+
+/*
+ * Return the list of hosts configured for this RILOE device
+ */
+
+static char **
+riloe_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+ nd = (struct pluginDevice*) s;
+ if (nd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in %s", __FUNCTION__);
+ return(NULL);
+ }
+
+ return OurImports->CopyHostList((const char * const*)nd->hostlist);
+}
+
+/*
+ * Parse the config information, and stash it away...
+ */
+
+static int
+RILOE_parse_config_info(struct pluginDevice* nd, const char * info)
+{
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (nd->hostcount >= 0) {
+ return(S_OOPS);
+ }
+
+ nd->hostlist = OurImports->StringToHostList(info);
+ if (nd->hostlist == NULL) {
+ LOG(PIL_CRIT,"StringToHostList() failed");
+ return S_OOPS;
+ }
+ for (nd->hostcount = 0; nd->hostlist[nd->hostcount]; nd->hostcount++) {
+ strdown(nd->hostlist[nd->hostcount]);
+ }
+ return(S_OK);
+}
+
+
+/*
+ * Pretend to reset the given host on this Stonith device.
+ * (we don't even error check the "request" type)
+ */
+static int
+riloe_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ char cmd[4096];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ snprintf(cmd, sizeof(cmd), "%s %s reset", RILOE_COMMAND, host);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "command %s will be executed", cmd);
+ }
+
+ if (system(cmd) == 0) {
+ return S_OK;
+ } else {
+ LOG(PIL_CRIT, "command %s failed", cmd);
+ return(S_RESETFAIL);
+ }
+}
+
+/*
+ * Parse the information in the given string,
+ * and stash it away...
+ */
+static int
+riloe_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+ struct pluginDevice* nd;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ nd = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+
+ rc = RILOE_parse_config_info(nd , namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ return rc;
+}
+
+/*
+ * Return the Stonith plugin configuration parameter
+ */
+static const char* const *
+riloe_get_confignames(StonithPlugin* p)
+{
+ static const char * RiloeParams[] = {ST_HOSTLIST, NULL };
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ return RiloeParams;
+}
+
+/*
+ * Return STONITH info string
+ */
+
+static const char *
+riloe_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* nd;
+ const char * ret;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ nd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = nd->idinfo;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Compaq RILOE STONITH device\n"
+ "Very early version!";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.hp.com/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = riloeXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * RILOE Stonith destructor...
+ */
+static void
+riloe_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+ nd = (struct pluginDevice *)s;
+
+ nd->pluginid = NOTriloeID;
+ if (nd->hostlist) {
+ stonith_free_hostlist(nd->hostlist);
+ nd->hostlist = NULL;
+ }
+ nd->hostcount = -1;
+ FREE(nd);
+}
+
+/* Create a new Riloe Stonith device. Too bad this function can't be static */
+static StonithPlugin *
+riloe_new(const char *subplugin)
+{
+ struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (nd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(nd, 0, sizeof(*nd));
+ nd->pluginid = pluginid;
+ nd->hostlist = NULL;
+ nd->hostcount = -1;
+ nd->idinfo = DEVICE;
+ nd->sp.s_ops = &riloeOps;
+
+ return &(nd->sp);
+}
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);
+}
diff --git a/lib/plugins/stonith/ssh.c b/lib/plugins/stonith/ssh.c
new file mode 100644
index 0000000..e90c199
--- /dev/null
+++ b/lib/plugins/stonith/ssh.c
@@ -0,0 +1,351 @@
+/*
+ * Stonith module for SSH Stonith device
+ *
+ * Copyright (c) 2001 SuSE Linux AG
+ *
+ * Authors: Joachim Gleissner <jg@suse.de>, Lars Marowsky-Brée <lmb@suse.de>
+ *
+ * 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>
+
+#include <config.h>
+
+#define DEVICE "SSH STONITH device"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN ssh
+#define PIL_PLUGIN_S "ssh"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * ssh_new(const char *);
+static void ssh_destroy(StonithPlugin *);
+static const char * const * ssh_get_confignames(StonithPlugin *);
+static int ssh_set_config(StonithPlugin *, StonithNVpair*);
+static const char * ssh_get_info(StonithPlugin * s, int InfoType);
+static int ssh_status(StonithPlugin * );
+static int ssh_reset_req(StonithPlugin * s, int request
+, const char * host);
+static char ** ssh_hostlist(StonithPlugin *);
+
+static struct stonith_ops sshOps ={
+ ssh_new, /* Create new STONITH object */
+ ssh_destroy, /* Destroy STONITH object */
+ ssh_get_info, /* Return STONITH info string */
+ ssh_get_confignames, /* Return configuration parameters */
+ ssh_set_config, /* set configuration */
+ ssh_status, /* Return STONITH device status */
+ ssh_reset_req, /* Request a reset */
+ ssh_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &sshOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/* uncomment this if you have an ssh that can do what it claims
+#define SSH_COMMAND "ssh -q -x -o PasswordAuthentication=no StrictHostKeyChecking=no"
+*/
+/* use this if you have the (broken) OpenSSH 2.1.1 */
+/* sunjd@cn.ibm.com added the option -f to temporily work around the block issue
+ * in which the child process always stay in 'system' call. Please FIX this.
+ * Additonally, this issue seems related to both of 2.6 kernel and stonithd.
+ */
+#define SSH_COMMAND "ssh -q -x -n -l root"
+
+/* We need to do a real hard reboot without syncing anything to simulate a
+ * power cut.
+ * We have to do it in the background, otherwise this command will not
+ * return.
+ */
+#define REBOOT_COMMAND "nohup sh -c '(sleep 2; nohup " REBOOT " " REBOOT_OPTIONS ") </dev/null >/dev/null 2>&1' &"
+#undef REBOOT_COMMAND
+#define REBOOT_COMMAND "echo 'sleep 2; " REBOOT " " REBOOT_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1"
+#define POWEROFF_COMMAND "echo 'sleep 2; " POWEROFF_CMD " " POWEROFF_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1"
+
+#define MAX_PING_ATTEMPTS 15
+
+/*
+ * SSH STONITH device
+ *
+ * I used the null device as template, so I guess there is missing
+ * some functionality.
+ *
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist;
+ int hostcount;
+};
+
+static const char * pluginid = "SSHDevice-Stonith";
+static const char * NOTpluginid = "SSH device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *sshXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+static int
+ssh_status(StonithPlugin *s)
+{
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ return system(NULL) ? S_OK : S_OOPS;
+}
+
+
+/*
+ * Return the list of hosts configured for this SSH device
+ */
+
+static char **
+ssh_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* sd = (struct pluginDevice*)s;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ if (sd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in %s", __FUNCTION__);
+ return(NULL);
+ }
+
+ return OurImports->CopyHostList((const char * const *)sd->hostlist);
+}
+
+
+/*
+ * Reset the given host on this Stonith device.
+ */
+static int
+ssh_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ char cmd[4096];
+ int i, status = -1;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if (request == ST_POWERON) {
+ LOG(PIL_CRIT, "%s not capable of power-on operation", DEVICE);
+ return S_INVAL;
+ } else if (request != ST_POWEROFF && request != ST_GENERIC_RESET) {
+ return S_INVAL;
+ }
+
+ for (i = 0; i < sd->hostcount; i++) {
+ if (strcasecmp(host, sd->hostlist[i]) == 0) {
+ break;
+ }
+ }
+
+ if (i >= sd->hostcount) {
+ LOG(PIL_CRIT, "%s doesn't control host [%s]"
+ , sd->idinfo, host);
+ return(S_BADHOST);
+ }
+
+ LOG(PIL_INFO, "Initiating ssh-%s on host: %s"
+ , request == ST_POWEROFF ? "poweroff" : "reset", host);
+
+ snprintf(cmd, sizeof(cmd)-1, "%s \"%s\" \"%s\"", SSH_COMMAND
+ , host
+ , request == ST_POWEROFF ? POWEROFF_COMMAND : REBOOT_COMMAND);
+
+ status = system(cmd);
+ if (WIFEXITED(status) && 0 == WEXITSTATUS(status)) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "checking whether %s stonith'd", host);
+ }
+
+ snprintf(cmd, sizeof(cmd)-1
+ , "ping -w1 -c1 %s >/dev/null 2>&1", host);
+
+ for (i = 0; i < MAX_PING_ATTEMPTS; i++) {
+ status = system(cmd);
+ if (WIFEXITED(status) && 1 == WEXITSTATUS(status)) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "unable to ping %s"
+ " after %d tries, stonith did work"
+ , host, i);
+ }
+ return S_OK;
+ }
+ sleep(1);
+ }
+
+ LOG(PIL_CRIT, "still able to ping %s after %d tries, stonith"
+ " did not work", host, MAX_PING_ATTEMPTS);
+ return S_RESETFAIL;
+ }else{
+ LOG(PIL_CRIT, "command %s failed", cmd);
+ return S_RESETFAIL;
+ }
+}
+
+static const char * const *
+ssh_get_confignames(StonithPlugin* p)
+{
+ static const char * SshParams[] = {ST_HOSTLIST, NULL };
+ return SshParams;
+}
+
+/*
+ * Parse the config information in the given string, and stash it away...
+ */
+static int
+ssh_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ const char * hlist;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ if ((hlist = OurImports->GetValue(list, ST_HOSTLIST)) == NULL) {
+ return S_OOPS;
+ }
+ sd->hostlist = OurImports->StringToHostList(hlist);
+ if (sd->hostlist == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ sd->hostcount = 0;
+ }else{
+ for (sd->hostcount = 0; sd->hostlist[sd->hostcount]
+ ; sd->hostcount++) {
+ strdown(sd->hostlist[sd->hostcount]);
+ }
+ }
+
+ return sd->hostcount ? S_OK : S_OOPS;
+}
+
+
+static const char *
+ssh_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = sd->idinfo;
+ break;
+
+
+ case ST_DEVICENAME:
+ ret = "ssh STONITH device";
+ break;
+
+
+ case ST_DEVICEDESCR: /* Description of device type */
+ ret = "SSH-based host reset\n"
+ "Fine for testing, but not suitable for production!";
+ break;
+
+
+ case ST_DEVICEURL:
+ ret = "http://openssh.org";
+ break;
+
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = sshXML;
+ break;
+
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * SSH Stonith destructor...
+ */
+static void
+ssh_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd->pluginid = NOTpluginid;
+ if (sd->hostlist) {
+ stonith_free_hostlist(sd->hostlist);
+ sd->hostlist = NULL;
+ }
+ sd->hostcount = -1;
+ FREE(sd);
+}
+
+/* Create a new ssh Stonith device */
+static StonithPlugin*
+ssh_new(const char *subplugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ sd->hostlist = NULL;
+ sd->hostcount = -1;
+ sd->idinfo = DEVICE;
+ sd->sp.s_ops = &sshOps;
+ return &(sd->sp);
+}
diff --git a/lib/plugins/stonith/stonith_config_xml.h b/lib/plugins/stonith/stonith_config_xml.h
new file mode 100644
index 0000000..ff04ae9
--- /dev/null
+++ b/lib/plugins/stonith/stonith_config_xml.h
@@ -0,0 +1,157 @@
+/*
+ * stonith_config_xml.h: common macros easing the writing of config
+ * XML for STONITH plugins. Only a STONITH
+ * plugin should include this header!
+ *
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author: Dave Blaschke <debltc@us.ibm.com>
+ * Support: linux-ha@lists.linux-ha.org
+ *
+ * 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
+ *
+ */
+#ifndef _STONITH_CONFIG_XML_H
+#define _STONITH_CONFIG_XML_H
+
+/*
+ * The generic constants for XML
+ */
+
+/* <parameters>?</parameters> */
+#define XML_PARAMETERS_BEGIN "<parameters>"
+#define XML_PARAMETERS_END "</parameters>"
+
+/* <parameter name="ipaddr" unique="?">?<content type="string" /></parameter> */
+#define XML_PARAMETER_BEGIN(name,type,req,uniq) \
+ "<parameter name=\"" name "\" unique=\"" uniq "\" required=\"" req "\">" \
+ "<content type=\"" type "\" />\n"
+#define XML_PARAMETER_END "</parameter>\n"
+
+/* <shortdesc lang="en">?</shortdesc> */
+#define XML_PARM_SHORTDESC_BEGIN(lang) \
+ "<shortdesc lang=\"" lang "\">\n"
+#define XML_PARM_SHORTDESC_END "</shortdesc>\n"
+
+/* <longdesc lang="en">?</longdesc> */
+#define XML_PARM_LONGDESC_BEGIN(lang) \
+ "<longdesc lang=\"" lang "\">\n"
+#define XML_PARM_LONGDESC_END "</longdesc>\n"
+
+/*
+ * The short and long descriptions for the few standardized parameter names;
+ * these can be translated by appending different languages to these constants
+ * (must include XML_PARM_****DESC_BEGIN(), the translated description, and
+ * XML_PARM_****DESC_END for each language)
+ */
+#define XML_HOSTLIST_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "Hostlist" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_HOSTLIST_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The list of hosts that the STONITH device controls" \
+ XML_PARM_LONGDESC_END
+
+#define XML_IPADDR_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "IP Address" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_IPADDR_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The IP address of the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_LOGIN_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "Login" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_LOGIN_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The username used for logging in to the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PASSWD_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "Password" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PASSWD_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The password used for logging in to the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_COMMUNITY_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "SNMP Community" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_COMMUNITY_LONGDESC "" \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The SNMP community string associated with the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_TTYDEV_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "TTY Device" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_TTYDEV_LONGDESC "" \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The TTY device used for connecting to the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+/*
+ * Complete parameter descriptions for the few standardized parameter names
+ */
+#define XML_HOSTLIST_PARM \
+ XML_PARAMETER_BEGIN(ST_HOSTLIST, "string", "1", "0") \
+ XML_HOSTLIST_SHORTDESC \
+ XML_HOSTLIST_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_IPADDR_PARM \
+ XML_PARAMETER_BEGIN(ST_IPADDR, "string", "1", "0") \
+ XML_IPADDR_SHORTDESC \
+ XML_IPADDR_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_LOGIN_PARM \
+ XML_PARAMETER_BEGIN(ST_LOGIN, "string", "1", "0") \
+ XML_LOGIN_SHORTDESC \
+ XML_LOGIN_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_PASSWD_PARM \
+ XML_PARAMETER_BEGIN(ST_PASSWD, "string", "1", "0") \
+ XML_PASSWD_SHORTDESC \
+ XML_PASSWD_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_COMMUNITY_PARM \
+ XML_PARAMETER_BEGIN(ST_COMMUNITY, "string", "1", "0") \
+ XML_COMMUNITY_SHORTDESC \
+ XML_COMMUNITY_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_TTYDEV_PARM \
+ XML_PARAMETER_BEGIN(ST_TTYDEV, "string", "1", "0") \
+ XML_TTYDEV_SHORTDESC \
+ XML_TTYDEV_LONGDESC \
+ XML_PARAMETER_END
+
+#endif
diff --git a/lib/plugins/stonith/stonith_expect_helpers.h b/lib/plugins/stonith/stonith_expect_helpers.h
new file mode 100644
index 0000000..f9eaa19
--- /dev/null
+++ b/lib/plugins/stonith/stonith_expect_helpers.h
@@ -0,0 +1,120 @@
+/*
+ * stonith_expect_helpers.h: Some common expect defines.
+ *
+ * Copyright (C) 2004 Lars Marowsky-Bree <lmb@suse.de>
+ *
+ * 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
+ *
+ */
+
+/* This is still somewhat ugly. It needs to be included after the PILS
+ * definitions so that it can access them, but the code reduction seemed
+ * to justify this. Hopefully it can be made somewhat more elegant
+ * eventually. */
+
+/*
+ * Many expect/telnet plugins use these defines and functions.
+ */
+
+#define SEND(fd,s) { \
+ size_t slen = strlen(s); \
+ if (Debug) { \
+ LOG(PIL_DEBUG \
+ , "Sending [%s] (len %d)" \
+ , (s) \
+ , (int)slen); \
+ } \
+ if (write((fd), (s), slen) != slen) { \
+ LOG(PIL_CRIT \
+ , "%s: write failed" \
+ , __FUNCTION__); \
+ } \
+ }
+
+#define EXPECT(fd,p,t) { \
+ if (StonithLookFor(fd, p, t) < 0) \
+ return(errno == ETIMEDOUT \
+ ? S_TIMEOUT : S_OOPS); \
+ }
+
+#define NULLEXPECT(fd,p,t) { \
+ if (StonithLookFor(fd, p, t) < 0) \
+ return(NULL); \
+ }
+
+#define SNARF(fd,s, to) { \
+ if (StonithScanLine(fd,to,(s),sizeof(s))\
+ != S_OK){ \
+ return(S_OOPS); \
+ } \
+ }
+
+#define NULLSNARF(fd,s, to){ \
+ if (StonithScanLine(fd,to,(s),sizeof(s))\
+ != S_OK) { \
+ return(NULL); \
+ } \
+ }
+
+/* Look for any of the given patterns. We don't care which */
+static int
+StonithLookFor(int fd, struct Etoken * tlist, int timeout)
+{
+ int rc;
+ char savebuf[512];
+
+ if ((rc = EXPECT_TOK(fd, tlist, timeout, savebuf, sizeof(savebuf)
+ , Debug)) < 0) {
+ LOG(PIL_CRIT, "Did not find string %s from " DEVICE "."
+ , tlist[0].string);
+ LOG(PIL_CRIT, "Received [%s]", savebuf);
+ }
+ return(rc);
+}
+
+#ifndef DOESNT_USE_STONITHSCANLINE
+/* Accept either a CR/NL or an NL/CR */
+static struct Etoken CRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};
+
+static int
+StonithScanLine(int fd, int timeout, char * buf, int max)
+{
+ if (EXPECT_TOK(fd, CRNL, timeout, buf, max, Debug) < 0) {
+ LOG(PIL_CRIT, "Could not read line from" DEVICE ".");
+ return(S_OOPS);
+ }
+ return(S_OK);
+}
+#endif
+
+#ifndef DOESNT_USE_STONITHKILLCOMM
+static void
+Stonithkillcomm(int *rdfd, int *wrfd, int *pid)
+{
+ if ((rdfd != NULL) && (*rdfd >= 0)) {
+ close(*rdfd);
+ *rdfd = -1;
+ }
+ if ((wrfd != NULL) && (*wrfd >= 0)) {
+ close(*wrfd);
+ *wrfd = -1;
+ }
+ if ((pid != NULL) && (*pid > 0)) {
+ STONITH_KILL(*pid, SIGKILL);
+ (void)waitpid(*pid, NULL, 0);
+ *pid = -1;
+ }
+}
+#endif
diff --git a/lib/plugins/stonith/stonith_plugin_common.h b/lib/plugins/stonith/stonith_plugin_common.h
new file mode 100644
index 0000000..dcdd7c8
--- /dev/null
+++ b/lib/plugins/stonith/stonith_plugin_common.h
@@ -0,0 +1,127 @@
+/*
+ * stonith_plugin_common.h: common macros easing the writing of STONITH
+ * plugins. Only a STONITH plugin should
+ * include this header!
+ *
+ * Copyright (C) 2004 Lars Marowsky-Bree <lmb@suse.de>
+ *
+ * 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
+ *
+ */
+#ifndef _STONITH_PLUGIN_COMMON_H
+#define _STONITH_PLUGIN_COMMON_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <libintl.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <netdb.h>
+#ifdef HAVE_TERMIO_H
+# include <termio.h>
+#endif
+#ifdef HAVE_SYS_TERMIOS_H
+#include <sys/termios.h>
+#else
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#endif
+#include <glib.h>
+
+
+#include <stonith/stonith.h>
+#include <stonith/stonith_plugin.h>
+
+#define LOG(w...) PILCallLog(PluginImports->log, w)
+
+#define MALLOC PluginImports->alloc
+#define REALLOC PluginImports->mrealloc
+#define STRDUP PluginImports->mstrdup
+#define FREE PluginImports->mfree
+#define EXPECT_TOK OurImports->ExpectToken
+#define STARTPROC OurImports->StartProcess
+
+#ifdef MALLOCT
+# undef MALLOCT
+#endif
+#define ST_MALLOCT(t) ((t *)(MALLOC(sizeof(t))))
+
+#define N_(text) (text)
+#define _(text) dgettext(ST_TEXTDOMAIN, text)
+
+#define WHITESPACE " \t\n\r\f"
+
+#ifndef MIN
+/* some macros */
+# define MIN( i, j ) ( i > j ? j : i )
+#endif
+
+#define REPLSTR(s,v) { \
+ if ((s) != NULL) { \
+ FREE(s); \
+ (s)=NULL; \
+ } \
+ (s) = STRDUP(v); \
+ if ((s) == NULL) { \
+ PILCallLog(PluginImports->log, \
+ PIL_CRIT, "out of memory"); \
+ } \
+ }
+
+#ifndef DEVICE
+#define DEVICE "Dummy"
+#endif
+
+#define PIL_PLUGINTYPE STONITH_TYPE
+#define PIL_PLUGINTYPE_S STONITH_TYPE_S
+
+#define ISCORRECTDEV(i) ((i)!= NULL \
+ && ((struct pluginDevice *)(i))->pluginid == pluginid)
+
+#define ERRIFWRONGDEV(s, retval) if (!ISCORRECTDEV(s)) { \
+ LOG(PIL_CRIT, "%s: invalid argument", __FUNCTION__); \
+ return(retval); \
+ }
+
+#define VOIDERRIFWRONGDEV(s) if (!ISCORRECTDEV(s)) { \
+ LOG(PIL_CRIT, "%s: invalid argument", __FUNCTION__); \
+ return; \
+ }
+
+#define ISCONFIGED(i) (i->isconfigured)
+
+#define ERRIFNOTCONFIGED(s,retval) ERRIFWRONGDEV(s,retval); \
+ if (!ISCONFIGED(s)) { \
+ LOG(PIL_CRIT, "%s: not configured", __FUNCTION__); \
+ return(retval); \
+ }
+
+#define VOIDERRIFNOTCONFIGED(s) VOIDERRIFWRONGDEV(s); \
+ if (!ISCONFIGED(s)) { \
+ LOG(PIL_CRIT, "%s: not configured", __FUNCTION__); \
+ return; \
+ }
+
+#endif
+
diff --git a/lib/plugins/stonith/stonith_signal.h b/lib/plugins/stonith/stonith_signal.h
new file mode 100644
index 0000000..99513f5
--- /dev/null
+++ b/lib/plugins/stonith/stonith_signal.h
@@ -0,0 +1,68 @@
+/*
+ * stonith_signal.h: signal handling routines to be used by stonith
+ * plugin libraries
+ *
+ * Copyright (C) 2002 Horms <horms@verge.net.au>
+ *
+ * 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
+ *
+ */
+#ifndef _STONITH_SIGNAL_H
+#define _STONITH_SIGNAL_H
+
+#include <signal.h>
+#include <sys/signal.h>
+
+int
+stonith_signal_set_simple_handler(int sig, void (*handler)(int)
+, struct sigaction *oldact);
+
+int
+stonith_signal_set_simple_handler(int sig, void (*handler)(int)
+, struct sigaction *oldact)
+{
+ struct sigaction sa;
+ sigset_t mask;
+
+ (void)stonith_signal_set_simple_handler;
+ if(sigemptyset(&mask) < 0) {
+ return(-1);
+ }
+
+ sa.sa_handler = handler;
+ sa.sa_mask = mask;
+ sa.sa_flags = 0;
+
+ if(sigaction(sig, &sa, oldact) < 0) {
+ return(-1);
+ }
+
+ return(0);
+}
+
+#define STONITH_SIGNAL(_sig, _handler) \
+ stonith_signal_set_simple_handler((_sig), (_handler), NULL)
+#ifdef HAVE_SIGIGNORE
+#define STONITH_IGNORE_SIG(_sig) \
+ sigignore((_sig))
+#else
+#define STONITH_IGNORE_SIG(_sig) \
+ STONITH_SIGNAL((_sig), SIG_IGN)
+#endif
+#define STONITH_DEFAULT_SIG(_sig) STONITH_SIGNAL((_sig), SIG_DFL)
+
+#define STONITH_KILL(_pid, _sig) kill((_pid), (_sig))
+
+#endif /* _STONITH_SIGNAL_H */
diff --git a/lib/plugins/stonith/suicide.c b/lib/plugins/stonith/suicide.c
new file mode 100644
index 0000000..b9d1db4
--- /dev/null
+++ b/lib/plugins/stonith/suicide.c
@@ -0,0 +1,274 @@
+/* File: suicide.c
+ * Description: Stonith module for suicide
+ *
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * 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>
+#include <config.h>
+#include <sys/utsname.h>
+
+#define DEVICE "Suicide STONITH device"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN suicide
+#define PIL_PLUGIN_S "suicide"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * suicide_new(const char *);
+static void suicide_destroy(StonithPlugin *);
+static const char * const * suicide_get_confignames(StonithPlugin *);
+static int suicide_set_config(StonithPlugin *, StonithNVpair*);
+static const char * suicide_get_info(StonithPlugin * s, int InfoType);
+static int suicide_status(StonithPlugin * );
+static int suicide_reset_req(StonithPlugin * s, int request
+ , const char * host);
+static char ** suicide_hostlist(StonithPlugin *);
+
+static struct stonith_ops suicideOps ={
+ suicide_new, /* Create new STONITH object */
+ suicide_destroy, /* Destroy STONITH object */
+ suicide_get_info, /* Return STONITH info string */
+ suicide_get_confignames, /* Return configuration parameters */
+ suicide_set_config, /* Set configuration */
+ suicide_status, /* Return STONITH device status */
+ suicide_reset_req, /* Request a reset */
+ suicide_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &suicideOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+#define REBOOT_COMMAND "nohup sh -c 'sleep 2; " REBOOT " " REBOOT_OPTIONS " </dev/null >/dev/null 2>&1' &"
+#define POWEROFF_COMMAND "nohup sh -c 'sleep 2; " POWEROFF_CMD " " POWEROFF_OPTIONS " </dev/null >/dev/null 2>&1' &"
+/*
+#define REBOOT_COMMAND "echo 'sleep 2; " REBOOT " " REBOOT_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1"
+#define POWEROFF_COMMAND "echo 'sleep 2; " POWEROFF_CMD " " POWEROFF_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1"
+*/
+
+/*
+ * Suicide STONITH device
+ */
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+};
+
+static const char * pluginid = "SuicideDevice-Stonith";
+static const char * NOTpluginid = "Suicide device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *suicideXML =
+ XML_PARAMETERS_BEGIN
+ XML_PARAMETERS_END;
+
+static int
+suicide_status(StonithPlugin *s)
+{
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ return S_OK;
+}
+
+/*
+ * Return the list of hosts configured for this Suicide device
+ */
+static char **
+suicide_hostlist(StonithPlugin *s)
+{
+ char** ret = NULL;
+ struct utsname name;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ if (uname(&name) == -1) {
+ LOG(PIL_CRIT, "uname error %d", errno);
+ return ret;
+ }
+
+ ret = OurImports->StringToHostList(name.nodename);
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return ret;
+ }
+ strdown(ret[0]);
+
+ return ret;
+}
+
+/*
+ * Suicide - reset or poweroff itself.
+ */
+static int
+suicide_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = -1;
+ struct utsname name;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if (request == ST_POWERON) {
+ LOG(PIL_CRIT, "%s not capable of power-on operation", DEVICE);
+ return S_INVAL;
+ } else if (request != ST_POWEROFF && request != ST_GENERIC_RESET) {
+ LOG(PIL_CRIT, "As for suicide virtual stonith device, "
+ "reset request=%d is not supported", request);
+ return S_INVAL;
+ }
+
+ if (uname(&name) == -1) {
+ LOG(PIL_CRIT, "uname error %d", errno);
+ return S_RESETFAIL ;
+ }
+
+ if (strcmp(name.nodename, host)) {
+ LOG(PIL_CRIT, "%s doesn't control host [%s]"
+ , name.nodename, host);
+ return S_RESETFAIL ;
+ }
+
+ LOG(PIL_INFO, "Initiating suicide on host %s", host);
+
+ rc = system(
+ request == ST_GENERIC_RESET ? REBOOT_COMMAND : POWEROFF_COMMAND);
+
+ if (rc == 0) {
+ LOG(PIL_INFO, "Suicide stonith succeeded.");
+ return S_OK;
+ } else {
+ LOG(PIL_CRIT, "Suicide stonith failed.");
+ return S_RESETFAIL ;
+ }
+}
+
+static const char * const *
+suicide_get_confignames(StonithPlugin* p)
+{
+ /* Donnot need to initialize from external. */
+ static const char * SuicideParams[] = { NULL };
+ return SuicideParams;
+}
+
+/*
+ * Parse the config information in the given string, and stash it away...
+ */
+static int
+suicide_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ ERRIFWRONGDEV(s,S_OOPS);
+ return S_OK;
+}
+
+static const char *
+suicide_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+ sd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = sd->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = "suicide STONITH device";
+ break;
+
+ case ST_DEVICEDESCR: /* Description of device type */
+ ret = "Virtual device to reboot/powerdown itself.\n";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = suicideXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Suicide Stonith destructor...
+ */
+static void
+suicide_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd = (struct pluginDevice *)s;
+
+ sd->pluginid = NOTpluginid;
+ FREE(sd);
+}
+
+/* Create a new suicide Stonith device */
+static StonithPlugin*
+suicide_new(const char * subplugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ sd->idinfo = DEVICE;
+ sd->sp.s_ops = &suicideOps;
+ return &(sd->sp);
+}
diff --git a/lib/plugins/stonith/vacm.c b/lib/plugins/stonith/vacm.c
new file mode 100644
index 0000000..ce6d041
--- /dev/null
+++ b/lib/plugins/stonith/vacm.c
@@ -0,0 +1,485 @@
+
+/******************************************************************************
+*
+* Copyright 2000 Sistina Software, Inc.
+* Tiny bits Copyright 2000 Alan Robertson <alanr@unix.sh>
+* Tiny bits Copyright 2000 Zac Sprackett, VA Linux Systems
+* Tiny bits Copyright 2005 International Business Machines
+* Significantly Mangled by Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005
+*
+* This is free software released under the GNU General Public License.
+* There is no warranty for this software. See the file COPYING for
+* details.
+*
+* See the file CONTRIBUTORS for a list of contributors.
+*
+* This file is maintained by:
+* Michael C Tilstra <conrad@sistina.com>
+*
+* Becasue I have no device to test, now I just make it pass the compiling
+* with vacm-2.0.5a. Please review before using.
+* Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005
+*
+* This module provides a driver for the VA Linux Cluster Manager.
+* For more information on VACM, see http://vacm.sourceforge.net/
+*
+* This module is rather poorly commented. But if you've read the
+* VACM Manual, and looked at the code example they have, this
+* should make pretty clean sense. (You obiviously should have
+* looked at the other stonith source too)
+*
+*/
+
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define DEVICE "VA Linux Cluster Manager"
+
+#include "stonith_plugin_common.h"
+#include "vacmclient_api.h"
+
+#define PIL_PLUGIN vacm
+#define PIL_PLUGIN_S "vacm"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * vacm_new(const char *);
+static void vacm_destroy(StonithPlugin *);
+static const char * const * vacm_get_confignames(StonithPlugin *);
+static int vacm_set_config(StonithPlugin *, StonithNVpair *);
+static const char * vacm_getinfo(StonithPlugin * s, int InfoType);
+static int vacm_status(StonithPlugin * );
+static int vacm_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** vacm_hostlist(StonithPlugin *);
+
+static struct stonith_ops vacmOps ={
+ vacm_new, /* Create new STONITH object */
+ vacm_destroy, /* Destroy STONITH object */
+ vacm_getinfo, /* Return STONITH info string */
+ vacm_get_confignames, /* Return configuration parameters */
+ vacm_set_config, /* Set configuration */
+ vacm_status, /* Return STONITH device status */
+ vacm_reset_req, /* Request a reset */
+ vacm_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug);
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &vacmOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*structs*/
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ void *h; /* a handle to the nexxus. */
+ char * nexxus;
+ char * user;
+ char * passwd;
+};
+
+#define ST_NEXXUS "nexxus"
+
+static const char * pluginid = "VACMDevice-Stonith";
+static const char * NOTpluginid = "VACM device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_NEXXUS_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_NEXXUS \
+ XML_PARM_SHORTDESC_END
+
+#define XML_NEXXUS_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The Nexxus component of the VA Cluster Manager" \
+ XML_PARM_LONGDESC_END
+
+#define XML_NEXXUS_PARM \
+ XML_PARAMETER_BEGIN(ST_NEXXUS, "string", "1", "1") \
+ XML_NEXXUS_SHORTDESC \
+ XML_NEXXUS_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *vacmXML =
+ XML_PARAMETERS_BEGIN
+ XML_NEXXUS_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+/*funcs*/
+int
+vacm_status(StonithPlugin *s)
+{
+ struct pluginDevice *sd;
+ char snd[] = "NEXXUS:VERSION";
+ char *rcv, *tk;
+ int rcvlen;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ sd = (struct pluginDevice*)s;
+
+ /* If grabbing the nexxus version works, then the status must be ok.
+ * right?
+ */
+
+ api_nexxus_send_ipc(sd->h, snd, strlen(snd)+1);
+ while(1) {
+ if (api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) {
+ break;
+ }
+ if (!(tk = strtok(rcv,":"))) { /*NEXXUS*/
+ break;
+ }else if (!(tk=strtok(NULL,":"))) { /* Job ID */
+ break;
+ }else if (!(tk=strtok(NULL,":"))) { /* one of the below */
+ break;
+ } else if ( !strcmp(tk, "JOB_COMPLETED")) {
+ free(rcv);
+ return S_OK; /* YEAH!! */
+ }else if(!strcmp(tk, "JOB_STARTED")) {
+ free(rcv);
+ continue;
+ }else if(!strcmp(tk, "JOB_ERROR")) {
+ free(rcv);
+ break;
+ }else if(!strcmp(tk, "VERSION")) {
+ free(rcv);
+ continue;
+ } else {
+ LOG(PIL_CRIT, "Unexpected token \"%s\" in line \"%s\"\n"
+ , tk, rcv);
+ break;
+ }
+ }
+
+ return S_OOPS;
+}
+
+/* Better make sure the current group is correct.
+ * Can't think of a good way to do this.
+ */
+char **
+vacm_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice *sd;
+ char snd[] = "NEXXUS:NODE_LIST";
+ char *rcv,*tk;
+ int rcvlen;
+ char ** hlst=NULL;
+ int hacnt=0, hrcnt=0;
+#define MSTEP 20
+
+ ERRIFWRONGDEV(s, NULL);
+ sd = (struct pluginDevice*)s;
+
+ hlst = (char **)MALLOC(MSTEP * sizeof(char*));
+ if (hlst == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return NULL;
+ }
+ hacnt=MSTEP;
+
+ api_nexxus_send_ipc(sd->h, snd, strlen(snd)+1);
+ while(1) {
+ if(api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) {
+ goto HL_cleanup;
+ }
+ if(!(tk=strtok(rcv, ":"))) { /* NEXXUS */
+ goto HL_cleanup;
+ }else if(!(tk=strtok(NULL,":"))) { /* Job ID */
+ goto HL_cleanup;
+ }else if(!(tk=strtok(NULL,":"))) { /* JOB_* or NODELIST */
+ goto HL_cleanup;
+ }else if( !strcmp(tk, "JOB_STARTED")) {
+ free(rcv);
+ continue;
+ }else if( !strcmp(tk, "JOB_COMPLETED")) {
+ free(rcv);
+ return hlst;
+ }else if( !strcmp(tk, "JOB_ERROR")) {
+ free(rcv);
+ break;
+ }else if( !strcmp(tk, "NODELIST")) {
+ if(!(tk = strtok(NULL,":"))) { /* group */
+ goto HL_cleanup;
+ }else if((tk = strtok(NULL," \t\n\r"))) { /*Finally, a machine name.*/
+ if( hrcnt >= (hacnt-1)) { /* grow array. */
+ char **oldhlst = hlst;
+ hlst = (char **)REALLOC(hlst, (hacnt +MSTEP)*sizeof(char*));
+ if( !hlst ) {
+ stonith_free_hostlist(oldhlst);
+ return NULL;
+ }
+ hacnt += MSTEP;
+ }
+ hlst[hrcnt] = STRDUP(tk); /* stuff the name. */
+ hlst[hrcnt+1] = NULL; /* set next to NULL for looping */
+ if (hlst[hrcnt] == NULL) {
+ stonith_free_hostlist(hlst);
+ return NULL;
+ }
+ strdown(hlst[hrcnt]);
+ hrcnt++;
+ }
+ }else {
+ /* WTF?! */
+ LOG(PIL_CRIT, "Unexpected token \"%s\" in line \"%s\"\n",tk,rcv);
+ break;
+ }
+ }
+
+HL_cleanup:
+ stonith_free_hostlist(hlst); /* give the mem back */
+ return NULL;
+}
+
+#define SND_SIZE 256
+int
+vacm_reset_req(StonithPlugin *s, int request, const char *host)
+{
+ struct pluginDevice *sd;
+ char snd[SND_SIZE]; /* god forbid its bigger than this */
+ char *rcv, *tk;
+ int rcvlen;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ sd = (struct pluginDevice*)s;
+
+ switch(request) {
+#ifdef ST_POWERON
+ case ST_POWERON:
+ snprintf(snd, SND_SIZE, "EMP:POWER_ON:%s", host);
+ break;
+#endif /*ST_POWERON*/
+#ifdef ST_POWEROFF
+ case ST_POWEROFF:
+ snprintf(snd, SND_SIZE, "EMP:POWER_OFF:%s", host);
+ break;
+#endif /*ST_POWEROFF*/
+ case ST_GENERIC_RESET:
+ snprintf(snd, SND_SIZE, "EMP:POWER_CYCLE:%s", host);
+ break;
+ default:
+ return S_INVAL;
+ }
+
+ api_nexxus_send_ipc(sd->h, snd, strlen(snd)+1);
+ while(1) {
+ if (api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) {
+ return S_RESETFAIL;
+ }
+ if (!(tk = strtok(rcv,":"))) { /*EMP*/
+ break;
+ }else if (!(tk=strtok(NULL,":"))) { /* Job ID */
+ break;
+ }else if (!(tk=strtok(NULL,":"))) { /* one of teh below */
+ break;
+ } else if ( !strcmp(tk, "JOB_COMPLETED")) {
+ free(rcv);
+ return S_OK;
+ } else if(!strcmp(tk, "JOB_STARTED")) {
+ free(rcv);
+ continue;
+ } else if(!strcmp(tk, "JOB_ERROR")) {
+ free(rcv);
+ return S_RESETFAIL;
+ } else {
+ /* WTF?! */
+ LOG(PIL_CRIT, "Unexpected token \"%s\" in line \"%s\"\n"
+ , tk, rcv);
+ break;
+ }
+ }
+
+ return S_RESETFAIL;
+}
+
+/* list => "nexxus:username:password" */
+static const char * const *
+vacm_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_NEXXUS, ST_LOGIN, ST_PASSWD, NULL};
+ return ret;
+}
+
+static int
+vacm_set_config(StonithPlugin *s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_NEXXUS, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+ char *rcv;
+ int rcvlen;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->nexxus = namestocopy[0].s_value;
+ sd->user = namestocopy[1].s_value;
+ sd->passwd = namestocopy[2].s_value;
+ /* When to initialize the sd->h */
+
+ if (api_nexxus_connect(sd->nexxus, sd->user, sd->passwd, &sd->h)<0){
+ return S_OOPS;
+ }
+ if (api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) {
+ return S_OOPS;
+ }
+ if (strcmp(rcv, "NEXXUS_READY")) {
+ rc = S_BADCONFIG;
+ }else{
+ rc = S_OK;
+ }
+ free(rcv);
+
+ return(rc);
+}
+
+/*
+ * The "vacmconf:" is in the conffile so that one file could be used for
+ * multiple device configs. This module will only look at the first line
+ * that starts with this token. All other line are ignored. (and thus
+ * could contain configs for other modules.)
+ *
+ * I don't think any other stonith modules do this currently.
+ */
+const char *
+vacm_getinfo(StonithPlugin *s, int reqtype)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+ switch (reqtype) {
+
+ case ST_DEVICEID: /* What type of device? */
+ ret = sd->idinfo;
+ break;
+
+ case ST_DEVICENAME: /* Which particular device? */
+ ret = dgettext(ST_TEXTDOMAIN, "VACM");
+ break;
+
+ case ST_DEVICEDESCR: /* Description of dev type */
+ ret = "A driver for the VA Linux Cluster Manager.";
+ break;
+
+ case ST_DEVICEURL: /* VACM's web site */
+ ret = "http://vacm.sourceforge.net/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = vacmXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+
+ return ret;
+}
+
+void
+vacm_destroy(StonithPlugin *s)
+{
+ struct pluginDevice *sd;
+
+ VOIDERRIFWRONGDEV(s);
+ sd = (struct pluginDevice*)s;
+
+ if( sd->h ) {
+ api_nexxus_disconnect(sd->h);
+ }
+
+ sd->pluginid = NOTpluginid;
+ if (sd->nexxus != NULL) {
+ FREE(sd->nexxus);
+ sd->nexxus = NULL;
+ }
+ if (sd->user != NULL) {
+ FREE(sd->user);
+ sd->user = NULL;
+ }
+ if (sd->passwd != NULL) {
+ FREE(sd->passwd);
+ sd->passwd = NULL;
+ }
+
+ FREE(sd);
+}
+
+static StonithPlugin *
+vacm_new(const char *subplugin)
+{
+ struct pluginDevice *sd;
+
+ sd = MALLOC(sizeof(struct pluginDevice));
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->h = NULL;
+ sd->pluginid = pluginid;
+ sd->nexxus = NULL;
+ sd->user = NULL;
+ sd->passwd = NULL;
+ sd->idinfo = DEVICE;
+ sd->sp.s_ops = &vacmOps;
+ return &(sd->sp); /* same as "sd" */
+}
diff --git a/lib/plugins/stonith/wti_mpc.c b/lib/plugins/stonith/wti_mpc.c
new file mode 100644
index 0000000..548f91c
--- /dev/null
+++ b/lib/plugins/stonith/wti_mpc.c
@@ -0,0 +1,856 @@
+/*
+ * Stonith module for WTI MPC (SNMP)
+ * Copyright (c) 2001 Andreas Piesk <a.piesk@gmx.net>
+ * Mangled by Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005
+ *
+ * Modified for WTI MPC by Denis Chapligin <chollya@satgate.net>, SatGate, 2009
+ *
+ * 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>
+
+/* device ID */
+#define DEVICE "WTI MPC"
+
+#include "stonith_plugin_common.h"
+#undef FREE /* defined by snmp stuff */
+
+#ifdef PACKAGE_BUGREPORT
+#undef PACKAGE_BUGREPORT
+#endif
+#ifdef PACKAGE_NAME
+#undef PACKAGE_NAME
+#endif
+#ifdef PACKAGE_STRING
+#undef PACKAGE_STRING
+#endif
+#ifdef PACKAGE_TARNAME
+#undef PACKAGE_TARNAME
+#endif
+#ifdef PACKAGE_VERSION
+#undef PACKAGE_VERSION
+#endif
+
+#ifdef HAVE_NET_SNMP_NET_SNMP_CONFIG_H
+# include <net-snmp/net-snmp-config.h>
+# include <net-snmp/net-snmp-includes.h>
+# include <net-snmp/agent/net-snmp-agent-includes.h>
+# define INIT_AGENT() init_master_agent()
+#else
+# include <ucd-snmp/ucd-snmp-config.h>
+# include <ucd-snmp/ucd-snmp-includes.h>
+# include <ucd-snmp/ucd-snmp-agent-includes.h>
+# ifndef NETSNMP_DS_APPLICATION_ID
+# define NETSNMP_DS_APPLICATION_ID DS_APPLICATION_ID
+# endif
+# ifndef NETSNMP_DS_AGENT_ROLE
+# define NETSNMP_DS_AGENT_ROLE DS_AGENT_ROLE
+# endif
+# define netsnmp_ds_set_boolean ds_set_boolean
+# define INIT_AGENT() init_master_agent(161, NULL, NULL)
+#endif
+
+#define PIL_PLUGIN wti_mpc
+#define PIL_PLUGIN_S "wti_mpc"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#define DEBUGCALL \
+ if (Debug) { \
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); \
+ }
+
+static StonithPlugin * wti_mpc_new(const char *);
+static void wti_mpc_destroy(StonithPlugin *);
+static const char * const * wti_mpc_get_confignames(StonithPlugin *);
+static int wti_mpc_set_config(StonithPlugin *, StonithNVpair *);
+static const char * wti_mpc_getinfo(StonithPlugin * s, int InfoType);
+static int wti_mpc_status(StonithPlugin * );
+static int wti_mpc_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** wti_mpc_hostlist(StonithPlugin *);
+
+static struct stonith_ops wti_mpcOps ={
+ wti_mpc_new, /* Create new STONITH object */
+ wti_mpc_destroy, /* Destroy STONITH object */
+ wti_mpc_getinfo, /* Return STONITH info string */
+ wti_mpc_get_confignames, /* Get configuration parameters */
+ wti_mpc_set_config, /* Set configuration */
+ wti_mpc_status, /* Return STONITH device status */
+ wti_mpc_reset_req, /* Request a reset */
+ wti_mpc_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+ DEBUGCALL;
+
+ 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
+ , &wti_mpcOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * APCMaster tested with APC Masterswitch 9212
+ */
+
+/* outlet commands / status codes */
+#define OUTLET_ON 5
+#define OUTLET_OFF 6
+#define OUTLET_REBOOT 7
+
+/* oids */
+#define OID_IDENT ".1.3.6.1.2.1.1.5.0"
+
+#define OID_GROUP_NAMES_V1 ".1.3.6.1.4.1.2634.3.1.3.1.2.%u"
+#define OID_GROUP_STATE_V1 ".1.3.6.1.4.1.2634.3.1.3.1.3.%i"
+
+#define OID_GROUP_NAMES_V3 ".1.3.6.1.4.1.2634.3.100.300.1.2.%u"
+#define OID_GROUP_STATE_V3 ".1.3.6.1.4.1.2634.3.100.300.1.3.%i"
+
+#define MAX_OUTLETS 128
+
+/*
+ snmpset -c private -v1 172.16.0.32:161
+ ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.1" i 1
+ The last octet in the OID is the plug number. The value can
+ be 1 thru 8 because there are 8 power plugs on this device.
+ The integer that can be set is as follows: 1=on, 2=off, and
+ 3=reset
+*/
+
+/* own defines */
+#define MAX_STRING 128
+#define ST_PORT "port"
+#define ST_MIBVERSION "mib-version"
+
+/* structur of stonith object */
+struct pluginDevice {
+ StonithPlugin sp; /* StonithPlugin object */
+ const char* pluginid; /* id of object */
+ const char* idinfo; /* type of device */
+ struct snmp_session* sptr; /* != NULL->session created */
+ char * hostname; /* masterswitch's hostname */
+ /* or ip addr */
+ int port; /* snmp port */
+ int mib_version; /* mib version to use */
+ char * community; /* snmp community (r/w) */
+ int num_outlets; /* number of outlets */
+};
+
+/* constant strings */
+static const char *pluginid = "WTI-MPC-Stonith";
+static const char *NOTpluginID = "WTI MPC device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_PORT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_PORT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PORT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The port number on which the SNMP server is running on the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PORT_PARM \
+ XML_PARAMETER_BEGIN(ST_PORT, "string", "1", "0") \
+ XML_PORT_SHORTDESC \
+ XML_PORT_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_MIBVERSION_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_MIBVERSION \
+ XML_PARM_SHORTDESC_END
+
+#define XML_MIBVERSION_LONGDESC \
+ XML_MIBVERSION_LONGDESC_BEGIN("en") \
+ "Version number of MPC MIB that we should use. Valid values are 1 (for 1.44 firmware) and 3 (for 1.62 firmware and later)" \
+ XML_PARM_LONGDESC_END
+
+#define XML_MIBVERSION_PARM \
+ XML_PARAMETER_BEGIN(ST_MIBVERSION, "string", "1", "0") \
+ XML_PORT_SHORTDESC \
+ XML_PORT_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *apcmastersnmpXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_PORT_PARM
+ XML_COMMUNITY_PARM
+ XML_MIBVERSION_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * own prototypes
+ */
+
+static void MPC_error(struct snmp_session *sptr, const char *fn
+, const char *msg);
+static struct snmp_session *MPC_open(char *hostname, int port
+, char *community);
+static void *MPC_read(struct snmp_session *sptr, const char *objname
+, int type);
+static int MPC_write(struct snmp_session *sptr, const char *objname
+, char type, char *value);
+
+static void
+MPC_error(struct snmp_session *sptr, const char *fn, const char *msg)
+{
+ int snmperr = 0;
+ int cliberr = 0;
+ char *errstr;
+
+ snmp_error(sptr, &cliberr, &snmperr, &errstr);
+ LOG(PIL_CRIT
+ , "%s: %s (cliberr: %i / snmperr: %i / error: %s)."
+ , fn, msg, cliberr, snmperr, errstr);
+ free(errstr);
+}
+
+
+/*
+ * creates a snmp session
+ */
+static struct snmp_session *
+MPC_open(char *hostname, int port, char *community)
+{
+ static struct snmp_session session;
+ struct snmp_session *sptr;
+
+ DEBUGCALL;
+
+ /* create session */
+ snmp_sess_init(&session);
+
+ /* fill session */
+ session.peername = hostname;
+ session.version = SNMP_VERSION_1;
+ session.remote_port = port;
+ session.community = (u_char *)community;
+ session.community_len = strlen(community);
+ session.retries = 5;
+ session.timeout = 1000000;
+
+ /* open session */
+ sptr = snmp_open(&session);
+
+ if (sptr == NULL) {
+ MPC_error(&session, __FUNCTION__, "cannot open snmp session");
+ }
+
+ /* return pointer to opened session */
+ return (sptr);
+}
+
+/*
+ * parse config
+ */
+
+/*
+ * read value of given oid and return it as string
+ */
+static void *
+MPC_read(struct snmp_session *sptr, const char *objname, int type)
+{
+ oid name[MAX_OID_LEN];
+ size_t namelen = MAX_OID_LEN;
+ struct variable_list *vars;
+ struct snmp_pdu *pdu;
+ struct snmp_pdu *resp;
+ static char response_str[MAX_STRING];
+ static int response_int;
+
+ DEBUGCALL;
+
+ /* convert objname into oid; return NULL if invalid */
+ if (!read_objid(objname, name, &namelen)) {
+ LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname);
+ return (NULL);
+ }
+
+ /* create pdu */
+ if ((pdu = snmp_pdu_create(SNMP_MSG_GET)) != NULL) {
+
+ /* get-request have no values */
+ snmp_add_null_var(pdu, name, namelen);
+
+ /* send pdu and get response; return NULL if error */
+ if (snmp_synch_response(sptr, pdu, &resp) == SNMPERR_SUCCESS) {
+
+ /* request succeed, got valid response ? */
+ if (resp->errstat == SNMP_ERR_NOERROR) {
+
+ /* go through the returned vars */
+ for (vars = resp->variables; vars;
+ vars = vars->next_variable) {
+
+ /* return response as string */
+ if ((vars->type == type) && (type == ASN_OCTET_STR)) {
+ memset(response_str, 0, MAX_STRING);
+ strncpy(response_str, (char *)vars->val.string,
+ MIN(vars->val_len, MAX_STRING));
+ snmp_free_pdu(resp);
+ return ((void *) response_str);
+ }
+ /* return response as integer */
+ if ((vars->type == type) && (type == ASN_INTEGER)) {
+ response_int = *vars->val.integer;
+ snmp_free_pdu(resp);
+ return ((void *) &response_int);
+ }
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]."
+ , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat));
+ }
+ }else{
+ MPC_error(sptr, __FUNCTION__, "error sending/receiving pdu");
+ }
+ /* free repsonse pdu (necessary?) */
+ snmp_free_pdu(resp);
+ }else{
+ MPC_error(sptr, __FUNCTION__, "cannot create pdu");
+ }
+ /* error: return nothing */
+ return (NULL);
+}
+
+/*
+ * write value of given oid
+ */
+static int
+MPC_write(struct snmp_session *sptr, const char *objname, char type,
+ char *value)
+{
+ oid name[MAX_OID_LEN];
+ size_t namelen = MAX_OID_LEN;
+ struct snmp_pdu *pdu;
+ struct snmp_pdu *resp;
+
+ DEBUGCALL;
+
+ /* convert objname into oid; return FALSE if invalid */
+ if (!read_objid(objname, name, &namelen)) {
+ LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname);
+ return (FALSE);
+ }
+
+ /* create pdu */
+ if ((pdu = snmp_pdu_create(SNMP_MSG_SET)) != NULL) {
+
+ /* add to be written value to pdu */
+ snmp_add_var(pdu, name, namelen, type, value);
+
+ /* send pdu and get response; return NULL if error */
+ if (snmp_synch_response(sptr, pdu, &resp) == STAT_SUCCESS) {
+
+ /* go through the returned vars */
+ if (resp->errstat == SNMP_ERR_NOERROR) {
+
+ /* request successful done */
+ snmp_free_pdu(resp);
+ return (TRUE);
+
+ }else{
+ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]."
+ , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat));
+ }
+ }else{
+ MPC_error(sptr, __FUNCTION__, "error sending/receiving pdu");
+ }
+ /* free pdu (again: necessary?) */
+ snmp_free_pdu(resp);
+ }else{
+ MPC_error(sptr, __FUNCTION__, "cannot create pdu");
+ }
+ /* error */
+ return (FALSE);
+}
+
+/*
+ * return the status for this device
+ */
+
+static int
+wti_mpc_status(StonithPlugin * s)
+{
+ struct pluginDevice *ad;
+ char *ident;
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ ad = (struct pluginDevice *) s;
+
+ if ((ident = MPC_read(ad->sptr, OID_IDENT, ASN_OCTET_STR)) == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read ident.", __FUNCTION__);
+ return (S_ACCESS);
+ }
+
+ /* status ok */
+ return (S_OK);
+}
+
+/*
+ * return the list of hosts configured for this device
+ */
+
+static char **
+wti_mpc_hostlist(StonithPlugin * s)
+{
+ char **hl;
+ struct pluginDevice *ad;
+ int j, h, num_outlets;
+ char *outlet_name;
+ char objname[MAX_STRING];
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, NULL);
+
+ ad = (struct pluginDevice *) s;
+
+ /* allocate memory for array of up to NUM_OUTLETS strings */
+ if ((hl = (char **)MALLOC((ad->num_outlets+1) * sizeof(char *))) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+ /* clear hostlist array */
+ memset(hl, 0, (ad->num_outlets + 1) * sizeof(char *));
+ num_outlets = 0;
+
+ /* read NUM_OUTLETS values and put them into hostlist array */
+ for (j = 0; j < ad->num_outlets; ++j) {
+
+ /* prepare objname */
+ switch (ad->mib_version) {
+ case 3:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V3,j+1);
+ break;
+ case 1:
+ default:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V1,j+1);
+ break;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: using %s as group names oid", __FUNCTION__, objname);
+ }
+
+ /* read outlet name */
+ if ((outlet_name = MPC_read(ad->sptr, objname, ASN_OCTET_STR)) ==
+ NULL) {
+ LOG(PIL_CRIT, "%s: cannot read name for outlet %d."
+ , __FUNCTION__, j+1);
+ stonith_free_hostlist(hl);
+ hl = NULL;
+ return (hl);
+ }
+
+ /* Check whether the host is already listed */
+ for (h = 0; h < num_outlets; ++h) {
+ if (strcasecmp(hl[h],outlet_name) == 0)
+ break;
+ }
+
+ if (h >= num_outlets) {
+ /* put outletname in hostlist */
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: added %s to hostlist."
+ , __FUNCTION__, outlet_name);
+ }
+
+ if ((hl[num_outlets] = STRDUP(outlet_name)) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ stonith_free_hostlist(hl);
+ hl = NULL;
+ return (hl);
+ }
+ strdown(hl[num_outlets]);
+ num_outlets++;
+ }
+ }
+
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %d unique hosts connected to %d outlets."
+ , __FUNCTION__, num_outlets, j);
+ }
+ /* return list */
+ return (hl);
+}
+
+/*
+ * reset the host
+ */
+
+static int
+wti_mpc_reset_req(StonithPlugin * s, int request, const char *host)
+{
+ struct pluginDevice *ad;
+ char objname[MAX_STRING];
+ char value[MAX_STRING];
+ char *outlet_name;
+ int req_oid = OUTLET_REBOOT;
+ int outlet;
+ int found_outlet=-1;
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ ad = (struct pluginDevice *) s;
+
+ /* read max. as->num_outlets values */
+ for (outlet = 1; outlet <= ad->num_outlets; outlet++) {
+
+ /* prepare objname */
+ switch (ad->mib_version) {
+ case 3:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V3,outlet);
+ break;
+ case 1:
+ default:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V1,outlet);
+ break;
+ }
+
+ /* read outlet name */
+ if ((outlet_name = MPC_read(ad->sptr, objname, ASN_OCTET_STR))
+ == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read name for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: found outlet: %s.", __FUNCTION__, outlet_name);
+ }
+
+ /* found one */
+ if (strcasecmp(outlet_name, host) == 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: found %s at outlet %d."
+ , __FUNCTION__, host, outlet);
+ }
+
+ /* Ok, stop iterating over host list */
+ found_outlet=outlet;
+ break;
+ }
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: outlet: %i.", __FUNCTION__, outlet);
+ }
+
+ /* host not found in outlet names */
+ if (found_outlet == -1) {
+ LOG(PIL_CRIT, "%s: no active outlet for '%s'.", __FUNCTION__, host);
+ return (S_BADHOST);
+ }
+
+
+ /* choose the OID for the stonith request */
+ switch (request) {
+ case ST_POWERON:
+ req_oid = OUTLET_ON;
+ break;
+ case ST_POWEROFF:
+ req_oid = OUTLET_OFF;
+ break;
+ case ST_GENERIC_RESET:
+ req_oid = OUTLET_REBOOT;
+ break;
+ default: break;
+ }
+
+ /* Turn them all off */
+
+ /* prepare objnames */
+
+ switch (ad->mib_version) {
+ case 3:
+ snprintf(objname,MAX_STRING,OID_GROUP_STATE_V3,found_outlet);
+ break;
+ case 1:
+ default:
+ snprintf(objname,MAX_STRING,OID_GROUP_STATE_V1,found_outlet);
+ break;
+ }
+
+ snprintf(value, MAX_STRING, "%i", req_oid);
+
+ /* send reboot cmd */
+ if (!MPC_write(ad->sptr, objname, 'i', value)) {
+ LOG(PIL_CRIT
+ , "%s: cannot send reboot command for outlet %d."
+ , __FUNCTION__, found_outlet);
+ return (S_RESETFAIL);
+ }
+
+ return (S_OK);
+}
+
+/*
+ * Get the configuration parameter names.
+ */
+
+static const char * const *
+wti_mpc_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_PORT, ST_COMMUNITY, ST_MIBVERSION, NULL};
+ return ret;
+}
+
+/*
+ * Set the configuration parameters.
+ */
+
+static int
+wti_mpc_set_config(StonithPlugin * s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ char * i;
+ int mo;
+ char objname[MAX_STRING];
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_PORT, NULL}
+ , {ST_COMMUNITY, NULL}
+ , {ST_MIBVERSION, NULL}
+ , {NULL, NULL}
+ };
+
+ DEBUGCALL;
+ ERRIFWRONGDEV(s,S_INVAL);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->hostname = namestocopy[0].s_value;
+ sd->port = atoi(namestocopy[1].s_value);
+ PluginImports->mfree(namestocopy[1].s_value);
+ sd->community = namestocopy[2].s_value;
+ sd->mib_version = atoi(namestocopy[3].s_value);
+ PluginImports->mfree(namestocopy[3].s_value);
+
+ /* try to resolve the hostname/ip-address */
+ if (gethostbyname(sd->hostname) != NULL) {
+ /* init snmp library */
+ init_snmp("wti_mpc");
+
+ /* now try to get a snmp session */
+ if ((sd->sptr = MPC_open(sd->hostname, sd->port, sd->community)) != NULL) {
+
+ /* ok, get the number of groups from the mpc */
+ sd->num_outlets=0;
+ /* We scan goup names table starting from 1 to MAX_OUTLETS */
+ /* and increase num_outlet counter on every group entry with name */
+ /* first entry without name is the mark of the end of the group table */
+ for (mo=1;mo<MAX_OUTLETS;mo++) {
+ switch (sd->mib_version) {
+ case 3:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V3,mo);
+ break;
+ case 1:
+ default:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V1,mo);
+ break;
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: used for groupTable retrieval: %s."
+ , __FUNCTION__, objname);
+ }
+
+ if ((i = MPC_read(sd->sptr, objname, ASN_OCTET_STR)) == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read number of outlets."
+ , __FUNCTION__);
+ return (S_ACCESS);
+ }
+ if (strlen(i)) {
+ /* store the number of outlets */
+ sd->num_outlets++;
+ } else {
+ break;
+ }
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: number of outlets: %i."
+ , __FUNCTION__, sd->num_outlets );
+ }
+
+ /* Everything went well */
+ return (S_OK);
+ }else{
+ LOG(PIL_CRIT, "%s: cannot create snmp session."
+ , __FUNCTION__);
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: cannot resolve hostname '%s', h_errno %d."
+ , __FUNCTION__, sd->hostname, h_errno);
+ }
+
+ /* not a valid config */
+ return (S_BADCONFIG);
+}
+
+/*
+ * get info about the stonith device
+ */
+
+static const char *
+wti_mpc_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice *ad;
+ const char *ret = NULL;
+
+ DEBUGCALL;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ ad = (struct pluginDevice *) s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ad->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = ad->hostname;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "WTI MPC (via SNMP)\n"
+ "The WTI MPC can accept multiple simultaneous SNMP clients";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.wti.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = apcmastersnmpXML;
+ break;
+
+ }
+ return ret;
+}
+
+
+/*
+ * APC StonithPlugin destructor...
+ */
+
+static void
+wti_mpc_destroy(StonithPlugin * s)
+{
+ struct pluginDevice *ad;
+
+ DEBUGCALL;
+
+ VOIDERRIFWRONGDEV(s);
+
+ ad = (struct pluginDevice *) s;
+
+ ad->pluginid = NOTpluginID;
+
+ /* release snmp session */
+ if (ad->sptr != NULL) {
+ snmp_close(ad->sptr);
+ ad->sptr = NULL;
+ }
+
+ /* reset defaults */
+ if (ad->hostname != NULL) {
+ PluginImports->mfree(ad->hostname);
+ ad->hostname = NULL;
+ }
+ if (ad->community != NULL) {
+ PluginImports->mfree(ad->community);
+ ad->community = NULL;
+ }
+ ad->num_outlets = 0;
+
+ PluginImports->mfree(ad);
+}
+
+/*
+ * Create a new APC StonithPlugin device. Too bad this function can't be
+ * static
+ */
+
+static StonithPlugin *
+wti_mpc_new(const char *subplugin)
+{
+ struct pluginDevice *ad = ST_MALLOCT(struct pluginDevice);
+
+ DEBUGCALL;
+
+ /* no memory for stonith-object */
+ if (ad == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+
+ /* clear stonith-object */
+ memset(ad, 0, sizeof(*ad));
+
+ /* set defaults */
+ ad->pluginid = pluginid;
+ ad->sptr = NULL;
+ ad->hostname = NULL;
+ ad->community = NULL;
+ ad->mib_version=1;
+ ad->idinfo = DEVICE;
+ ad->sp.s_ops = &wti_mpcOps;
+
+ /* return the object */
+ return (&(ad->sp));
+}
diff --git a/lib/plugins/stonith/wti_nps.c b/lib/plugins/stonith/wti_nps.c
new file mode 100644
index 0000000..f0b81f7
--- /dev/null
+++ b/lib/plugins/stonith/wti_nps.c
@@ -0,0 +1,813 @@
+/*
+ *
+ * Copyright 2001 Mission Critical Linux, Inc.
+ *
+ * All Rights Reserved.
+ */
+/*
+ * Stonith module for WTI Network Power Switch Devices (NPS-xxx)
+ * Also supports the WTI Telnet Power Switch Devices (TPS-xxx)
+ *
+ * Copyright 2001 Mission Critical Linux, Inc.
+ * author: mike ledoux <mwl@mclinux.com>
+ * author: Todd Wheeling <wheeling@mclinux.com>
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ * Further hurt by Lon <lhh@redhat.com>, Red Hat, 2005
+ *
+ * Supported WTI devices:
+ * NPS-115
+ * NPS-230
+ * IPS-15
+ * IPS-800
+ * IPS-800-CE
+ * NBB-1600
+ * NBB-1600-CE
+ * TPS-2
+ *
+ * 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 WTI Network Power Switch, unlike the BayTech network power switch,
+ * accpets only one (telnet) connection/session at a time. When one
+ * session is active, any subsequent attempt to connect to the NPS will
+ * result in a connection refused/closed failure. In a cluster environment
+ * or other environment utilizing polling/monitoring of the NPS
+ * (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 NPS became unresponsive. In some
+ * configurations this necessitated placing the power switch onto a
+ * private subnet.
+ */
+
+#include <lha_internal.h>
+#define DEVICE "WTI Network Power Switch"
+
+#define DOESNT_USE_STONITHKILLCOMM 1
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN wti_nps
+#define PIL_PLUGIN_S "wti_nps"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#define MAX_WTIPLUGINID 256
+
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * wti_nps_new(const char *);
+static void wti_nps_destroy(StonithPlugin *);
+static const char * const * wti_nps_get_confignames(StonithPlugin *);
+static int wti_nps_set_config(StonithPlugin * , StonithNVpair * );
+static const char * wti_nps_get_info(StonithPlugin * s, int InfoType);
+static int wti_nps_status(StonithPlugin * );
+static int wti_nps_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** wti_nps_hostlist(StonithPlugin *);
+
+static struct stonith_ops wti_npsOps ={
+ wti_nps_new, /* Create new STONITH object */
+ wti_nps_destroy, /* Destroy STONITH object */
+ wti_nps_get_info, /* Return STONITH info string */
+ wti_nps_get_confignames,/* Return configration parameters */
+ wti_nps_set_config, /* set configration */
+ wti_nps_status, /* Return STONITH device status */
+ wti_nps_reset_req, /* Request a reset */
+ wti_nps_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
+ , &wti_npsOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * I have a NPS-110. This code has been tested with this switch.
+ * (Tested with NPS-230 and TPS-2 by lmb)
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ pid_t pid;
+ int rdfd;
+ int wrfd;
+ char * device;
+ char * passwd;
+};
+
+static const char * pluginid = "WTINPS-Stonith";
+static const char * NOTnpsid = "WTINPS device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *wti_npsXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+
+/*
+ * Different expect strings that we get from the WTI
+ * Network Power Switch
+ */
+
+#define WTINPSSTR " Power Switch"
+#define WTINBBSTR "Boot Bar"
+
+static struct Etoken password[] = { {"Password:", 0, 0}, {NULL,0,0}};
+static struct Etoken Prompt[] = { {"PS>", 0, 0}
+ , {"IPS>", 0, 0}
+ , {"BB>", 0, 0}
+ , {NULL,0,0}};
+static struct Etoken LoginOK[] = { {WTINPSSTR, 0, 0}
+ , {WTINBBSTR, 0, 0}
+ , {"Invalid password", 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[] = { {"rocessing - please wait", 0, 0}
+ , {"(Y/N):", 1, 0}
+ , {NULL,0,0}};
+
+static int NPS_connect_device(struct pluginDevice * nps);
+static int NPSLogin(struct pluginDevice * nps);
+static int NPSNametoOutlet(struct pluginDevice*, const char * name, char **outlets);
+static int NPSReset(struct pluginDevice*, char * outlets, const char * rebootid);
+static int NPSLogout(struct pluginDevice * nps);
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+static int NPS_onoff(struct pluginDevice*, const char * outlets, const char * unitid
+, int request);
+#endif
+
+/* Attempt to login up to 20 times... */
+static int
+NPSRobustLogin(struct pluginDevice * nps)
+{
+ int rc = S_OOPS;
+ int j = 0;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ for ( ; ; ) {
+ if (NPS_connect_device(nps) == S_OK) {
+ rc = NPSLogin(nps);
+ if (rc == S_OK) {
+ break;
+ }
+ }
+ if ((++j) == 20) {
+ break;
+ }
+ else {
+ sleep(1);
+ }
+ }
+
+ return rc;
+}
+
+/* Login to the WTI Network Power Switch (NPS) */
+static int
+NPSLogin(struct pluginDevice * nps)
+{
+ char IDinfo[128];
+ char * idptr = IDinfo;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* Look for the unit type info */
+ if (EXPECT_TOK(nps->rdfd, password, 2, IDinfo
+ , sizeof(IDinfo), Debug) < 0) {
+ LOG(PIL_CRIT, "No initial response from %s.", nps->idinfo);
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+ idptr += strspn(idptr, WHITESPACE);
+ /*
+ * We should be looking at something like this:
+ * Enter Password:
+ */
+
+ SEND(nps->wrfd, nps->passwd);
+ SEND(nps->wrfd, "\r");
+ /* Expect "Network Power Switch vX.YY" */
+
+ switch (StonithLookFor(nps->rdfd, LoginOK, 5)) {
+
+ case 0: /* Good! */
+ LOG(PIL_INFO, "Successful login to %s.", nps->idinfo);
+ break;
+
+ case 1: /* Uh-oh - bad password */
+ LOG(PIL_CRIT, "Invalid password for %s.", nps->idinfo);
+ return(S_ACCESS);
+
+ default:
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+ return(S_OK);
+}
+
+/* Log out of the WTI NPS */
+
+static int
+NPSLogout(struct pluginDevice* nps)
+{
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* Send "/h" help command and expect back prompt */
+ /*
+ SEND(nps->wrfd, "/h\r");
+ */
+ /* Expect "PS>" */
+ rc = StonithLookFor(nps->rdfd, Prompt, 5);
+
+ /* "/x" is Logout, "/x,y" auto-confirms */
+ SEND(nps->wrfd, "/x,y\r");
+
+ close(nps->wrfd);
+ close(nps->rdfd);
+ nps->wrfd = nps->rdfd = -1;
+
+ return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS));
+}
+
+/* Reset (power-cycle) the given outlets */
+static int
+NPSReset(struct pluginDevice* nps, char * outlets, const char * rebootid)
+{
+ char unum[32];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* Send "/h" help command and expect back prompt */
+ SEND(nps->wrfd, "/h\r");
+ /* Expect "PS>" */
+ EXPECT(nps->rdfd, Prompt, 5);
+
+ /* Send REBOOT command for given outlets */
+ snprintf(unum, sizeof(unum), "/BOOT %s,y\r", outlets);
+ SEND(nps->wrfd, unum);
+
+ /* Expect "Processing "... or "(Y/N)" (if confirmation turned on) */
+
+ retry:
+ switch (StonithLookFor(nps->rdfd, Processing, 5)) {
+ case 0: /* Got "Processing" Do nothing */
+ break;
+
+ case 1: /* Got that annoying command confirmation :-( */
+ SEND(nps->wrfd, "Y\r");
+ goto retry;
+
+ default:
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+ LOG(PIL_INFO, "Host is being rebooted: %s", rebootid);
+
+ /* Expect "PS>" */
+ if (StonithLookFor(nps->rdfd, Prompt, 60) < 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", rebootid);
+ SEND(nps->wrfd, "/h\r");
+ return(S_OK);
+}
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+static int
+NPS_onoff(struct pluginDevice* nps, const char * outlets, const char * unitid, int req)
+{
+ char unum[32];
+
+ const char * onoff = (req == ST_POWERON ? "/On" : "/Off");
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* Send "/h" help command and expect prompt back */
+ SEND(nps->wrfd, "/h\r");
+ /* Expect "PS>" */
+ EXPECT(nps->rdfd, Prompt, 5);
+
+ /* Send ON/OFF command for given outlet */
+ snprintf(unum, sizeof(unum), "%s %s,y\r", onoff, outlets);
+ SEND(nps->wrfd, unum);
+
+ /* Expect "Processing"... or "(Y/N)" (if confirmation turned on) */
+
+ if (StonithLookFor(nps->rdfd, Processing, 5) == 1) {
+ /* They've turned on that annoying command confirmation :-( */
+ SEND(nps->wrfd, "Y\r");
+ }
+ EXPECT(nps->rdfd, Prompt, 60);
+
+ /* All Right! Command done. Life is Good! */
+ LOG(PIL_INFO, "Power to NPS outlet(s) %s turned %s", outlets, onoff);
+
+ SEND(nps->wrfd, "/h\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
+NPSNametoOutlet(struct pluginDevice* nps, const char * name, char **outlets)
+{
+ char NameMapping[128];
+ int sockno;
+ char sockname[32];
+ char buf[32];
+ int left = 17;
+ int ret = -1;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if ((*outlets = (char *)MALLOC(left*sizeof(char))) == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(-1);
+ }
+
+ strncpy(*outlets, "", left);
+ left = left - 1; /* ensure terminating '\0' */
+ /* Expect "PS>" */
+ EXPECT(nps->rdfd, Prompt, 5);
+
+ /* The status command output contains mapping of hosts to outlets */
+ SEND(nps->wrfd, "/s\r");
+
+ /* Expect: "-----+" so we can skip over it... */
+ EXPECT(nps->rdfd, Separator, 5);
+
+ do {
+ NameMapping[0] = EOS;
+ SNARF(nps->rdfd, NameMapping, 5);
+
+ if (sscanf(NameMapping
+ , "%d | %16c",&sockno, sockname) == 2) {
+
+ char * last = sockname+16;
+ *last = EOS;
+ --last;
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (strncasecmp(name, sockname, 16) == 0) {
+ ret = sockno;
+ snprintf(buf, sizeof(buf), "%d ", sockno);
+ strncat(*outlets, buf, left);
+ left = left - strlen(buf);
+ }
+ }
+ } while (strlen(NameMapping) > 2 && left > 0);
+
+ return(ret);
+}
+
+static int
+wti_nps_status(StonithPlugin *s)
+{
+ struct pluginDevice* nps;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ nps = (struct pluginDevice*) s;
+
+ if ((rc = NPSRobustLogin(nps) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s.", nps->idinfo);
+ return(rc);
+ }
+
+ /* Send "/h" help command and expect back prompt */
+ SEND(nps->wrfd, "/h\r");
+ /* Expect "PS>" */
+ EXPECT(nps->rdfd, Prompt, 5);
+
+ return(NPSLogout(nps));
+}
+
+/*
+ * Return the list of hosts (outlet names) for the devices on this NPS unit
+ */
+
+static char **
+wti_nps_hostlist(StonithPlugin *s)
+{
+ char NameMapping[128];
+ char* NameList[64];
+ unsigned int numnames = 0;
+ char ** ret = NULL;
+ struct pluginDevice* nps;
+ unsigned int i;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ nps = (struct pluginDevice*) s;
+ if (NPSRobustLogin(nps) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s.", nps->idinfo);
+ return(NULL);
+ }
+
+ /* Expect "PS>" */
+ NULLEXPECT(nps->rdfd, Prompt, 5);
+
+ /* The status command output contains mapping of hosts to outlets */
+ SEND(nps->wrfd, "/s\r");
+
+ /* Expect: "-----" so we can skip over it... */
+ NULLEXPECT(nps->rdfd, Separator, 5);
+ NULLEXPECT(nps->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+
+ do {
+ int sockno;
+ char sockname[64];
+ NameMapping[0] = EOS;
+ NULLSNARF(nps->rdfd, NameMapping, 5);
+ if (sscanf(NameMapping
+ , "%d | %16c",&sockno, sockname) == 2) {
+
+ char * last = sockname+16;
+ 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 (!strcmp(sockname,"(undefined)") ||
+ !strcmp(sockname,"---")) {
+ /* lhh - skip undefined */
+ continue;
+ }
+ if ((nm = STRDUP(sockname)) == NULL) {
+ goto out_of_memory;
+ }
+ strdown(nm);
+ NameList[numnames] = nm;
+ ++numnames;
+ NameList[numnames] = NULL;
+ }
+ } while (strlen(NameMapping) > 2);
+
+ if (numnames >= 1) {
+ ret = (char **)MALLOC((numnames+1)*sizeof(char*));
+ if (ret == NULL) {
+ goto out_of_memory;
+ }else{
+ memset(ret, 0, (numnames+1)*sizeof(char*));
+ memcpy(ret, NameList, (numnames+1)*sizeof(char*));
+ }
+ }
+ (void)NPSLogout(nps);
+
+ 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 NPS device. We should add serial support here
+ * eventually...
+ */
+static int
+NPS_connect_device(struct pluginDevice * nps)
+{
+ int fd = OurImports->OpenStreamSocket(nps->device
+ , TELNET_PORT, TELNET_SERVICE);
+
+ if (fd < 0) {
+ return(S_OOPS);
+ }
+ nps->rdfd = nps->wrfd = fd;
+ return(S_OK);
+}
+
+/*
+ * Reset the given host on this Stonith device.
+ */
+static int
+wti_nps_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = 0;
+ int lorc = 0;
+ struct pluginDevice* nps;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ nps = (struct pluginDevice*) s;
+
+ if ((rc = NPSRobustLogin(nps)) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s.", nps->idinfo);
+ }else{
+ char *outlets;
+ int noutlet;
+
+ outlets = NULL;
+ noutlet = NPSNametoOutlet(nps, host, &outlets);
+
+ if (noutlet < 1) {
+ LOG(PIL_WARN, "%s doesn't control host [%s]"
+ , nps->device, host);
+ if (outlets != NULL) {
+ FREE(outlets);
+ outlets = NULL;
+ }
+ return(S_BADHOST);
+ }
+ switch(request) {
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+ case ST_POWERON:
+ case ST_POWEROFF:
+ rc = NPS_onoff(nps, outlets, host, request);
+ if (outlets != NULL) {
+ FREE(outlets);
+ outlets = NULL;
+ }
+ break;
+#endif
+ case ST_GENERIC_RESET:
+ rc = NPSReset(nps, outlets, host);
+ if (outlets != NULL) {
+ FREE(outlets);
+ outlets = NULL;
+ }
+ break;
+ default:
+ rc = S_INVAL;
+ if (outlets != NULL) {
+ FREE(outlets);
+ outlets = NULL;
+ }
+ break;
+ }
+ }
+
+ lorc = NPSLogout(nps);
+ return(rc != S_OK ? rc : lorc);
+}
+
+/*
+ * Parse the information in the given string,
+ * and stash it away...
+ */
+static int
+wti_nps_set_config(StonithPlugin * s, StonithNVpair *list)
+{
+ struct pluginDevice* nps;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.\n", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ nps = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ nps->device = namestocopy[0].s_value;
+ nps->passwd = namestocopy[1].s_value;
+ return S_OK;
+}
+
+
+/*
+ * Return the Stonith plugin configuration parameter
+ *
+ */
+static const char * const *
+wti_nps_get_confignames(StonithPlugin * p)
+{
+ static const char * names[] = { ST_IPADDR , ST_PASSWD , NULL};
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ return names;
+}
+
+/*
+ * Get info about the stonith device
+ *
+ */
+static const char *
+wti_nps_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* nps;
+ const char * ret;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ nps = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+
+ case ST_DEVICEID:
+ ret = nps->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = nps->device;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Western Telematic (WTI) Network Power Switch Devices (NPS-xxx)\n"
+ "Also supports the WTI Telnet Power Switch Devices (TPS-xxx)\n"
+ "NOTE: The WTI Network Power Switch, accepts only "
+ "one (telnet) connection/session at a time.";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.wti.com/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = wti_npsXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * WTI NPS Stonith destructor...
+ */
+static void
+wti_nps_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nps;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ nps = (struct pluginDevice *)s;
+
+ nps->pluginid = NOTnpsid;
+ if (nps->rdfd >= 0) {
+ close(nps->rdfd);
+ nps->rdfd = -1;
+ }
+ if (nps->wrfd >= 0) {
+ close(nps->wrfd);
+ nps->wrfd = -1;
+ }
+ if (nps->device != NULL) {
+ FREE(nps->device);
+ nps->device = NULL;
+ }
+ if (nps->passwd != NULL) {
+ FREE(nps->passwd);
+ nps->passwd = NULL;
+ }
+ FREE(nps);
+}
+
+/* Create a new BayTech Stonith device. */
+
+static StonithPlugin *
+wti_nps_new(const char *subplugin)
+{
+ struct pluginDevice* nps = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (nps == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(nps, 0, sizeof(*nps));
+ nps->pluginid = pluginid;
+ nps->pid = -1;
+ nps->rdfd = -1;
+ nps->wrfd = -1;
+ nps->device = NULL;
+ nps->passwd = NULL;
+ nps->idinfo = DEVICE;
+ nps->sp.s_ops = &wti_npsOps;
+
+ return &(nps->sp);
+}
+