diff options
Diffstat (limited to '')
-rw-r--r-- | lib/plugins/stonith/rhcs.c | 1035 |
1 files changed, 1035 insertions, 0 deletions
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); + +} |