summaryrefslogtreecommitdiffstats
path: root/lib/plugins/stonith/ibmhmc.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:40:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:40:13 +0000
commite9be59e1502a41bab9891d96d753102a7dafef0b (patch)
treec3b2da87c414881f4b53d0964f407c83492d813e /lib/plugins/stonith/ibmhmc.c
parentInitial commit. (diff)
downloadcluster-glue-upstream.tar.xz
cluster-glue-upstream.zip
Adding upstream version 1.0.12.upstream/1.0.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/plugins/stonith/ibmhmc.c')
-rw-r--r--lib/plugins/stonith/ibmhmc.c1261
1 files changed, 1261 insertions, 0 deletions
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;
+}
+*/