📄 external.c
字号:
/* * 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 * * 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 "stonith_plugin_common.h"#define PIL_PLUGIN external#define PIL_PLUGIN_S "external"#define PIL_PLUGINLICENSE LICENSE_LGPL#define PIL_PLUGINLICENSEURL URL_LGPL#define ST_COMMAND "command options"#define MAX_EXTERNALLINE 256#include <pils/plugin.h>static StonithPlugin * external_new(void);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_rcPIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);PIL_rcPIL_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; int config; char * command; GHashTable * cmd_opts;};static const char * pluginid = "EXTERNALDevice-Stonith";static const char * NOTpluginID = "EXTERNAL device has been destroyed";/* Prototypes *//* Run the command with op as a single command line argument 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 intexternal_status(StonithPlugin *s){ int rc = 0; struct pluginDevice *sd = NULL; ERRIFWRONGDEV(s,S_OOPS); sd = (struct pluginDevice*) s; rc = external_run_cmd(sd, "status", NULL); if (rc == 0) { LOG(PIL_DEBUG, "%s: running %s status returned %d", __FUNCTION__, sd->command, rc); } else { LOG(PIL_INFO, "%s: running %s status returned %d", __FUNCTION__, sd->command, rc); } return rc;}static char **external_hostlist(StonithPlugin *s){ char ** ret = NULL; char * output; char * tmp; struct pluginDevice* sd; int rc, i; ERRIFNOTCONFIGED(s,NULL); sd = (struct pluginDevice*) s; rc = external_run_cmd(sd, "hostlist", &output); if (rc == 0) { LOG(PIL_DEBUG, "%s: '%s hostlist' succeeded", __FUNCTION__, sd->command); return NULL; } else { LOG(PIL_CRIT, "%s: '%s hostlist' failed with rc %d", __FUNCTION__, sd->command, rc); if (output) { FREE(output); } return NULL; } if (!output) { LOG(PIL_CRIT, "%s: '%s hostlist' returned an empty hostlist", __FUNCTION__, sd->command); return NULL; } ret = MALLOC(sizeof(char *) * 64); if (!ret) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); return NULL; } /* White-space split the output here */ i = 0; while ((tmp = strtok(output, WHITESPACE))) { ret[i] = STRDUP(tmp); if (!ret[i]) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); stonith_free_hostlist(ret); return NULL; } /* External scripts should be treated with care... * Arbitary limits are bad, but who knows? * XXX: Up this if it ever becomes a problem. * Though power switches driving >=32 nodes really * should be implemented as proper STONITH plug-ins. */ i++; if (i > 32) { LOG(PIL_CRIT, "%s: run away hostlist? >= 32 nodes", __FUNCTION__); stonith_free_hostlist(ret); return NULL; } } if (output) { FREE(output); } if (i == 0) { LOG(PIL_CRIT, "%s: '%s hostlist' returned an empty hostlist", __FUNCTION__, sd->command); stonith_free_hostlist(ret); ret = NULL; } return(ret);}static intexternal_reset_req(StonithPlugin * s, int request, const char * host){ struct pluginDevice *sd = NULL; const char *op; int rc; ERRIFNOTCONFIGED(s,S_OOPS); LOG(PIL_INFO, "%s %s", _("Host external-reset initiating on "), host); sd = (struct pluginDevice*) s; switch (request) { case ST_GENERIC_RESET: op = "reset"; break; case ST_POWEROFF: op = "poweroff"; break; case ST_POWERON: op = "poweron"; break; default: LOG(PIL_CRIT, "%s: Unknown stonith request %d", __FUNCTION__, request); return S_OOPS; break; } g_hash_table_insert(sd->cmd_opts, g_strdup("ST_HOST"), g_strdup(host)); rc = external_run_cmd(sd, op, NULL); g_hash_table_remove(sd->cmd_opts, "ST_HOST"); if (rc == 0) { LOG(PIL_INFO, "%s: '%s %s' for host %s succeeded", __FUNCTION__, sd->command, op, host); return S_OK; } else { LOG(PIL_CRIT, "%s: '%s %s' for host %s failed with rc %d", __FUNCTION__, sd->command, op, host, rc); return(S_RESETFAIL); } /* notreached */ return S_OOPS;}static intexternal_parse_config_info(struct pluginDevice* sd, const char * info){ int i; int j; char *command = NULL; char *tmp, *key, *value, *tmp_val; struct stat buf; /* make sure that command has not already been set */ if (sd->config) { return(S_OOPS); } tmp = STRDUP(info); if (!tmp) { goto err_mem; } command = strtok(tmp, WHITESPACE); if (!command) { LOG(PIL_CRIT, "%s: cannot find command to call.", __FUNCTION__); goto err; } if (command[0] != '/') { /* Not an absolute pathname. */ j = strlen(command) + strlen(STONITH_EXT_PLUGINDIR) + 2; sd->command = MALLOC(j); if (!sd->command) { goto err_mem; } if (snprintf(sd->command, j, "%s/%s", sd->command, STONITH_EXT_PLUGINDIR) == j) { goto err_mem; } } else { sd->command = STRDUP(command); } if (!sd->command) { goto err_mem; } if (stat(sd->command, &buf) != 0) { LOG(PIL_CRIT, "%s: stating %s failed.", __FUNCTION__, sd->command); goto err; } if (S_ISREG(buf.st_mode) && (buf.st_mode & (S_IXUSR|S_IXOTH|S_IXGRP))) { LOG(PIL_INFO, "%s: %s found to be executable.", __FUNCTION__, sd->command); } else { LOG(PIL_CRIT, "%s: %s found NOT to be executable.", __FUNCTION__, sd->command); goto err; } sd->cmd_opts = g_hash_table_new(g_str_hash, g_str_equal); /* white-space split the option string and put it into the * hashtable. TODO: Maybe treat "" as delimeters too so * whitespace can be passed to the plugins... */ i = 0; while ((tmp_val = strtok(tmp, WHITESPACE))) { key = MALLOC(10); if (!key) { goto err_mem; } if (snprintf(key, 10, "ST_OPT_%d", i) == 10) { FREE(key); goto err_mem; } value = STRDUP(tmp_val); if (!value) { FREE(key); goto err_mem; } g_hash_table_insert(sd->cmd_opts, key, value); i++; } FREE(tmp); sd->config = 1; return(S_OK);err_mem: LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__);err: if (tmp) { FREE(tmp); } external_unconfig(sd); return(S_OOPS);}static gbooleanlet_remove_eachitem(gpointer key, gpointer value, gpointer user_data){ if (key) { FREE(key); } if (value) { FREE(value); } return TRUE;}static voidexternal_unconfig(struct pluginDevice *sd) { sd->config = 0; 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->command) { FREE(sd->command); sd->command = NULL; }}/* * Parse the information in the given string * and stash it away... */static intexternal_set_config(StonithPlugin* s, StonithNVpair *list){ char externalLine[MAX_EXTERNALLINE]; struct pluginDevice* sd; StonithNamesToGet namestoget [] = { {ST_COMMAND, NULL} , {NULL, NULL} }; int rc; if (Debug) { LOG(PIL_DEBUG, "%s:called.", __FUNCTION__); } ERRIFWRONGDEV(s,S_OOPS); sd = (struct pluginDevice*) s; if ((rc = OurImports->GetAllValues(namestoget, list)) != S_OK){ LOG(PIL_INFO, "Can not get parameter from StonithNVpair"); return rc; } if ((snprintf(externalLine, MAX_EXTERNALLINE,"%s", namestoget[0].s_value)) <= 0) { LOG(PIL_CRIT,"Can not copy parameter to externalLine"); } return (external_parse_config_info(sd,externalLine));}/* * Return STONITH config vars */static const char**external_get_confignames(StonithPlugin* p){ static const char * ExternalParams[] = {ST_COMMAND, NULL }; return ExternalParams;}/* * Return STONITH info string */static const char *external_getinfo(StonithPlugin * s, int reqtype){ struct pluginDevice* sd; char * ret; ERRIFWRONGDEV(s,NULL); /* TODO: Retrieve from plugin...? */ /* * We look in the ST_TEXTDOMAIN catalog for our messages */ sd = (struct pluginDevice *)s; switch (reqtype) { case ST_DEVICEID: ret = _("External STONITH plugin"); break; case ST_DEVICEDESCR: /* Description of device type */ ret = _("EXTERNAL-program based STONITH plugin\n"); break; default: ret = NULL; break; } return ret;}/* * EXTERNAL Stonith destructor... */static voidexternal_destroy(StonithPlugin *s){ struct pluginDevice* sd; VOIDERRIFWRONGDEV(s); sd = (struct pluginDevice *)s; sd->pluginid = NOTpluginID; external_unconfig(sd); FREE(sd);}/* Create a new external Stonith device */static StonithPlugin *external_new(void){ struct pluginDevice* sd = MALLOCT(struct pluginDevice); if (sd == NULL) { LOG(PIL_CRIT, "out of memory"); return(NULL); } memset(sd, 0, sizeof(*sd)); sd->pluginid = pluginid; sd->sp.s_ops = &externalOps; external_unconfig(sd); return &(sd->sp);}static voidext_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 voidext_del_from_env(gpointer key, gpointer value, gpointer user_data){ unsetenv((char *)key);}/* Run the command with op as a single command line argument 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[BUFF_LEN]; GString* g_str_tmp = NULL; if (snprintf(cmd, BUFF_LEN, "%s %s", sd->command, op) == BUFF_LEN) { LOG(PIL_CRIT, "%s: out of memory or command too long", __FUNCTION__); goto err; } /* We only have a global environment to use here. So we add our * options to it, and then later remove them again. */ g_hash_table_foreach(sd->cmd_opts, ext_add_to_env, NULL); file = popen(cmd, "r"); if (NULL==file) { LOG(PIL_CRIT, "%s: Calling '%s' failed", __FUNCTION__, cmd); goto err; } 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) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); goto err; } 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); rc = pclose(file); if (output) { *output = data; } else { FREE(data); } g_hash_table_foreach(sd->cmd_opts, ext_del_from_env, NULL); return(rc);err: g_hash_table_foreach(sd->cmd_opts, ext_del_from_env, NULL); if (data) { FREE(data); } if (output) { *output = NULL; } return(-1);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -