mirror of https://github.com/sipwise/heartbeat.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
794 lines
17 KiB
794 lines
17 KiB
/*
|
|
* 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** 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 (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 (Debug) {
|
|
LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
|
|
__FUNCTION__, sd->subplugin, op, rc);
|
|
}
|
|
if (rc != 0) {
|
|
LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
|
|
__FUNCTION__, sd->subplugin, op, rc);
|
|
if (output) {
|
|
FREE(output);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
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);
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
|
|
__FUNCTION__, sd->subplugin, op, rc);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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++) {
|
|
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_INFO, "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**
|
|
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 (Debug) {
|
|
LOG(PIL_DEBUG, "%s: '%s %s' returned %d",
|
|
__FUNCTION__, sd->subplugin, op, rc);
|
|
}
|
|
if (rc != 0) {
|
|
LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
|
|
__FUNCTION__, sd->subplugin, op, rc);
|
|
if (output) { FREE(output); }
|
|
return NULL;
|
|
}
|
|
|
|
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 **)sd->confignames;
|
|
}
|
|
|
|
/*
|
|
* Return STONITH info string
|
|
*/
|
|
static const char *
|
|
external_getinfo(StonithPlugin * s, int reqtype)
|
|
{
|
|
struct pluginDevice* sd;
|
|
char * ret = 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, &ret);
|
|
if (Debug) {
|
|
LOG(PIL_DEBUG, "%s: '%s %s' returned %d",
|
|
__FUNCTION__, sd->subplugin, op, rc);
|
|
}
|
|
|
|
if (rc == 0) {
|
|
if (sd->outputbuf == NULL) {
|
|
FREE(sd->outputbuf);
|
|
}
|
|
sd->outputbuf = ret;
|
|
return(ret);
|
|
}
|
|
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);
|
|
}
|
|
|
|
/* 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 rc;
|
|
char * data = NULL;
|
|
FILE * file;
|
|
char cmd[FILENAME_MAX+64];
|
|
struct stat buf;
|
|
int slen;
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
goto err;
|
|
}
|
|
|
|
data = NULL;
|
|
slen=0;
|
|
data = MALLOC(1);
|
|
while(data && !feof(file)) {
|
|
data[slen]=EOS;
|
|
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;
|
|
}else{
|
|
sleep(1);
|
|
}
|
|
}
|
|
if (!data) {
|
|
LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
|
|
goto err;
|
|
}
|
|
|
|
|
|
rc = pclose(file);
|
|
if (rc != 0) {
|
|
LOG(PIL_INFO, "%s: Calling '%s' returned %d", __FUNCTION__, cmd, rc);
|
|
}
|
|
if (Debug && data) {
|
|
LOG(PIL_INFO, "%s: '%s' output: %s", __FUNCTION__, cmd, data);
|
|
}
|
|
|
|
if (output) {
|
|
*output = data;
|
|
} else {
|
|
FREE(data);
|
|
}
|
|
|
|
if (sd->cmd_opts) {
|
|
g_hash_table_foreach(sd->cmd_opts, ext_del_from_env, NULL);
|
|
}
|
|
|
|
return(rc);
|
|
|
|
err:
|
|
if (sd->cmd_opts) {
|
|
g_hash_table_foreach(sd->cmd_opts, ext_del_from_env, NULL);
|
|
}
|
|
if (data) {
|
|
FREE(data);
|
|
}
|
|
if (output) {
|
|
*output = NULL;
|
|
}
|
|
|
|
return(-1);
|
|
|
|
}
|