diff options
Diffstat (limited to 'lib/stonith/main.c')
-rw-r--r-- | lib/stonith/main.c | 727 |
1 files changed, 727 insertions, 0 deletions
diff --git a/lib/stonith/main.c b/lib/stonith/main.c new file mode 100644 index 0000000..44e099f --- /dev/null +++ b/lib/stonith/main.c @@ -0,0 +1,727 @@ +/* + * Stonith: simple test program for exercising the Stonith API code + * + * 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> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <syslog.h> +#include <stonith/stonith.h> +#include <pils/plugin.h> +#include <clplumbing/cl_log.h> +#include <glib.h> +#include <libxml/entities.h> + +#define OPTIONS "c:F:p:t:T:EsnSlLmvhVd" +#define EQUAL '=' + +extern char * optarg; +extern int optind, opterr, optopt; + +static int debug = 0; + +#define LOG_TERMINAL 0 +#define LOG_CLLOG 1 +static int log_destination = LOG_TERMINAL; + +static const char META_TEMPLATE[] = +"<?xml version=\"1.0\"?>\n" +"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n" +"<resource-agent name=\"%s\">\n" +"<version>1.0</version>\n" +"<longdesc lang=\"en\">\n" +"%s\n" +"</longdesc>\n" +"<shortdesc lang=\"en\">%s</shortdesc>\n" +"%s\n" +"<actions>\n" +"<action name=\"start\" timeout=\"20\" />\n" +"<action name=\"stop\" timeout=\"15\" />\n" +"<action name=\"status\" timeout=\"20\" />\n" +"<action name=\"monitor\" timeout=\"20\" interval=\"3600\" />\n" +"<action name=\"meta-data\" timeout=\"15\" />\n" +"</actions>\n" +"<special tag=\"heartbeat\">\n" +"<version>2.0</version>\n" +"</special>\n" +"</resource-agent>\n"; + +void version(void); +void usage(const char * cmd, int exit_status, const char * devtype); +void confhelp(const char * cmd, FILE* stream, const char * devtype); +void print_stonith_meta(Stonith * stonith_obj, const char *rsc_type); +void print_types(void); +void print_confignames(Stonith *s); + +void log_buf(int severity, char *buf); +void log_msg(int severity, const char * fmt, ...)G_GNUC_PRINTF(2,3); +void trans_log(int priority, const char * fmt, ...)G_GNUC_PRINTF(2,3); + +static int pil_loglevel_to_syslog_severity[] = { + /* Indices: <none>=0, PIL_FATAL=1, PIL_CRIT=2, PIL_WARN=3, + PIL_INFO=4, PIL_DEBUG=5 + */ + LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_WARNING, LOG_INFO, LOG_DEBUG + }; + +/* + * Note that we don't use the cl_log logging code because the STONITH + * command is intended to be shipped without the clplumbing libraries. + * + * :-( + * + * The stonith command has so far always been shipped along with + * the clplumbing library, so we'll use cl_log + * If that ever changes, we'll use something else + */ + +void +version() +{ + printf("stonith: %s (%s)\n", GLUE_VERSION, GLUE_BUILD_VERSION); + exit(0); +} + +void +usage(const char * cmd, int exit_status, const char * devtype) +{ + FILE *stream; + + stream = exit_status ? stderr : stdout; + + /* non-NULL devtype indicates help for specific device, so no usage */ + if (devtype == NULL) { + fprintf(stream, "usage:\n"); + fprintf(stream, "\t %s [-svh] " + "-L\n" + , cmd); + + fprintf(stream, "\t %s [-svh] " + "-t stonith-device-type " + "-n\n" + , cmd); + + fprintf(stream, "\t %s [-svh] " + "-t stonith-device-type " + "-m\n" + , cmd); + + fprintf(stream, "\t %s [-svh] " + "-t stonith-device-type " + "{-p stonith-device-parameters | " + "-F stonith-device-parameters-file | " + "-E | " + "name=value...} " + "[-c count] " + "-lS\n" + , cmd); + + fprintf(stream, "\t %s [-svh] " + "-t stonith-device-type " + "{-p stonith-device-parameters | " + "-F stonith-device-parameters-file | " + "-E | " + "name=value...} " + "[-c count] " + "-T {reset|on|off} nodename\n" + , cmd); + + fprintf(stream, "\nwhere:\n"); + fprintf(stream, "\t-L\tlist supported stonith device types\n"); + fprintf(stream, "\t-l\tlist hosts controlled by this stonith device\n"); + fprintf(stream, "\t-S\treport stonith device status\n"); + fprintf(stream, "\t-s\tsilent\n"); + fprintf(stream, "\t-v\tverbose\n"); + fprintf(stream, "\t-n\toutput the config names of stonith-device-parameters\n"); + fprintf(stream, "\t-m\tdisplay meta-data of the stonith device type\n"); + fprintf(stream, "\t-h\tdisplay detailed help message with stonith device description(s)\n"); + } + + if (exit_status == 0) { + confhelp(cmd, stream, devtype); + } + + exit(exit_status); +} + +/* Thanks to Lorn Kay <lorn_kay@hotmail.com> for the confhelp code */ +void +confhelp(const char * cmd, FILE* stream, const char * devtype) +{ + char ** typelist; + char ** this; + Stonith * s; + int devfound = 0; + + + /* non-NULL devtype indicates help for specific device, so no header */ + if (devtype == NULL) { + fprintf(stream + , "\nSTONITH -t device types and" + " associated configuration details:\n"); + } + + typelist = stonith_types(); + + if (typelist == NULL) { + fprintf(stderr, + "Failed to retrieve list of STONITH modules!\n"); + return; + } + for(this=typelist; *this && !devfound; ++this) { + const char * SwitchType = *this; + const char * cres; + const char * const * pnames; + + + if ((s = stonith_new(SwitchType)) == NULL) { + fprintf(stderr, "Invalid STONITH type %s(!)\n" + , SwitchType); + continue; + } + + if (devtype) { + if (strcmp(devtype, SwitchType)) { + continue; + } else { + devfound = 1; + } + } + + fprintf(stream, "\n\nSTONITH Device: %s - ", SwitchType); + + if ((cres = stonith_get_info(s, ST_DEVICEDESCR)) != NULL){ + fprintf(stream, "%s\n" + , cres); + } + + if ((cres = stonith_get_info(s, ST_DEVICEURL)) != NULL){ + fprintf(stream + , "For more information see %s\n" + , cres); + } + if (NULL == (pnames = stonith_get_confignames(s))) { + continue; + } + fprintf(stream + , "List of valid parameter names for %s STONITH device:\n" + , SwitchType); + for (;*pnames; ++pnames) { + fprintf(stream + , "\t%s\n", *pnames); + } + +#ifdef ST_CONFI_INFO_SYNTAX + fprintf(stream, "\nConfig info [-p] syntax for %s:\n\t%s\n" + , SwitchType, stonith_get_info(s, ST_CONF_INFO_SYNTAX)); +#else + fprintf(stream, "For Config info [-p] syntax" + ", give each of the above parameters in order as" + "\nthe -p value.\n" + "Arguments are separated by white space."); +#endif +#ifdef ST_CONFI_FILE_SYNTAX + fprintf(stream, "\nConfig file [-F] syntax for %s:\n\t%s\n" + , SwitchType, stonith->get_info(s, ST_CONF_FILE_SYNTAX)); +#else + fprintf(stream + , "\nConfig file [-F] syntax is the same as -p" + ", except # at the start of a line" + "\ndenotes a comment\n"); +#endif + + stonith_delete(s); s = NULL; + } + /* Note that the type list can't/shouldn't be freed */ + if (devtype && !devfound) { + fprintf(stderr, "Invalid device type: '%s'\n", devtype); + } + +} + +void +print_stonith_meta(Stonith * stonith_obj, const char *rsc_type) +{ + const char * meta_param = NULL; + const char * meta_longdesc = NULL; + const char * meta_shortdesc = NULL; + char *xml_meta_longdesc = NULL; + char *xml_meta_shortdesc = NULL; + static const char * no_parameter_info = "<!-- no value -->"; + + meta_longdesc = stonith_get_info(stonith_obj, ST_DEVICEDESCR); + if (meta_longdesc == NULL) { + fprintf(stderr, "stonithRA plugin: no long description"); + meta_longdesc = no_parameter_info; + } + xml_meta_longdesc = (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_longdesc); + + meta_shortdesc = stonith_get_info(stonith_obj, ST_DEVICEID); + if (meta_shortdesc == NULL) { + fprintf(stderr, "stonithRA plugin: no short description"); + meta_shortdesc = no_parameter_info; + } + xml_meta_shortdesc = (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_shortdesc); + + meta_param = stonith_get_info(stonith_obj, ST_CONF_XML); + if (meta_param == NULL) { + fprintf(stderr, "stonithRA plugin: no list of parameters"); + meta_param = no_parameter_info; + } + + printf(META_TEMPLATE, + rsc_type, xml_meta_longdesc, xml_meta_shortdesc, meta_param); + + xmlFree(xml_meta_longdesc); + xmlFree(xml_meta_shortdesc); +} + +#define MAXNVARG 50 + +void +print_types() +{ + char ** typelist; + + typelist = stonith_types(); + if (typelist == NULL) { + log_msg(LOG_ERR, "Could not list Stonith types."); + }else{ + char ** this; + + for(this=typelist; *this; ++this) { + printf("%s\n", *this); + } + } +} + +void +print_confignames(Stonith *s) +{ + const char * const * names; + int i; + + names = stonith_get_confignames(s); + + if (names != NULL) { + for (i=0; names[i]; ++i) { + printf("%s ", names[i]); + } + } + printf("\n"); +} + +void +log_buf(int severity, char *buf) +{ + if (severity == LOG_DEBUG && !debug) + return; + if (log_destination == LOG_TERMINAL) { + fprintf(stderr, "%s: %s\n", prio2str(severity),buf); + } else { + cl_log(severity, "%s", buf); + } +} + +void +log_msg(int severity, const char * fmt, ...) +{ + va_list ap; + char buf[MAXLINE]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf)-1, fmt, ap); + va_end(ap); + log_buf(severity, buf); +} + +void +trans_log(int priority, const char * fmt, ...) +{ + int severity; + va_list ap; + char buf[MAXLINE]; + + severity = pil_loglevel_to_syslog_severity[ priority % sizeof + (pil_loglevel_to_syslog_severity) ]; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf)-1, fmt, ap); + va_end(ap); + log_buf(severity, buf); +} + +int +main(int argc, char** argv) +{ + char * cmdname; + int rc; + Stonith * s; + const char * SwitchType = NULL; + const char * optfile = NULL; + const char * parameters = NULL; + int reset_type = ST_GENERIC_RESET; + int verbose = 0; + int status = 0; + int silent = 0; + int listhosts = 0; + int listtypes = 0; + int listparanames = 0; + int params_from_env = 0; + + int c; + int errors = 0; + int argcount; + StonithNVpair nvargs[MAXNVARG]; + int nvcount=0; + int j; + int count = 1; + int help = 0; + int metadata = 0; + + /* The bladehpi stonith plugin makes use of openhpi which is + * threaded. The mix of memory allocation without thread + * initialization followed by g_thread_init followed by + * deallocating that memory results in segfault. Hence the + * following G_SLICE setting; see + * http://library.gnome.org/devel/glib/stable/glib-Memory-Slices.html#g-slice-alloc + */ + + setenv("G_SLICE", "always-malloc", 1); + + if ((cmdname = strrchr(argv[0], '/')) == NULL) { + cmdname = argv[0]; + }else{ + ++cmdname; + } + + + while ((c = getopt(argc, argv, OPTIONS)) != -1) { + switch(c) { + + case 'c': count = atoi(optarg); + if (count < 1) { + fprintf(stderr + , "bad count [%s]\n" + , optarg); + usage(cmdname, 1, NULL); + } + break; + + case 'd': debug++; + break; + + case 'F': optfile = optarg; + break; + + case 'E': params_from_env = 1; + break; + + case 'h': help++; + break; + + case 'm': metadata++; + break; + + case 'l': ++listhosts; + break; + + case 'L': ++listtypes; + break; + + case 'p': parameters = optarg; + break; + + case 's': ++silent; + break; + + case 'S': ++status; + break; + + case 't': SwitchType = optarg; + break; + + case 'T': if (strcmp(optarg, "on")== 0) { + reset_type = ST_POWERON; + }else if (strcmp(optarg, "off")== 0) { + reset_type = ST_POWEROFF; + }else if (strcmp(optarg, "reset")== 0) { + reset_type = ST_GENERIC_RESET; + }else{ + fprintf(stderr + , "bad reset type [%s]\n" + , optarg); + usage(cmdname, 1, NULL); + } + break; + + case 'n': ++listparanames; + break; + + case 'v': ++verbose; + break; + + case 'V': version(); + break; + + default: ++errors; + break; + } + } + + /* if we're invoked by stonithd, log through cl_log */ + if (!isatty(fileno(stdin))) { + log_destination = LOG_CLLOG; + cl_log_set_entity("stonith"); + cl_log_enable_stderr(debug?TRUE:FALSE); + cl_log_set_facility(HA_LOG_FACILITY); + + /* Use logd if it's enabled by heartbeat */ + cl_inherit_logging_environment(0); + } + + if (help && !errors) { + usage(cmdname, 0, SwitchType); + } + if (debug) { + PILpisysSetDebugLevel(debug); + setenv("HA_debug","2",0); + } + if ((optfile && parameters) || (optfile && params_from_env) + || (params_from_env && parameters)) { + fprintf(stderr + , "Please use just one of -F, -p, and -E options\n"); + usage(cmdname, 1, NULL); + } + + /* + * Process name=value arguments on command line... + */ + for (;optind < argc; ++optind) { + char * eqpos; + if ((eqpos=strchr(argv[optind], EQUAL)) == NULL) { + break; + } + if (parameters || optfile || params_from_env) { + fprintf(stderr + , "Cannot mix name=value and -p, -F, or -E " + "style arguments\n"); + usage(cmdname, 1, NULL); + } + if (nvcount >= MAXNVARG) { + fprintf(stderr + , "Too many name=value style arguments\n"); + exit(1); + } + nvargs[nvcount].s_name = argv[optind]; + *eqpos = EOS; + nvargs[nvcount].s_value = eqpos+1; + nvcount++; + } + nvargs[nvcount].s_name = NULL; + nvargs[nvcount].s_value = NULL; + + argcount = argc - optind; + + if (!(argcount == 1 || (argcount < 1 + && (status||listhosts||listtypes||listparanames||metadata)))) { + ++errors; + } + + if (errors) { + usage(cmdname, 1, NULL); + } + + if (listtypes) { + print_types(); + exit(0); + } + + if (SwitchType == NULL) { + log_msg(LOG_ERR,"Must specify device type (-t option)"); + usage(cmdname, 1, NULL); + } + s = stonith_new(SwitchType); + if (s == NULL) { + log_msg(LOG_ERR,"Invalid device type: '%s'", SwitchType); + exit(S_OOPS); + } + if (debug) { + stonith_set_debug(s, debug); + } + stonith_set_log(s, (PILLogFun)trans_log); + + if (!listparanames && !metadata && optfile == NULL && + parameters == NULL && !params_from_env && nvcount == 0) { + const char * const * names; + int needs_parms = 1; + + if (s != NULL && (names = stonith_get_confignames(s)) != NULL && names[0] == NULL) { + needs_parms = 0; + } + + if (needs_parms) { + fprintf(stderr + , "Must specify either -p option, -F option, -E option, or " + "name=value style arguments\n"); + if (s != NULL) { + stonith_delete(s); + } + usage(cmdname, 1, NULL); + } + } + + if (listparanames) { + print_confignames(s); + stonith_delete(s); + s=NULL; + exit(0); + } + + if (metadata) { + print_stonith_meta(s,SwitchType); + stonith_delete(s); + s=NULL; + exit(0); + } + + /* Old STONITH version 1 stuff... */ + if (optfile) { + /* Configure the Stonith object from a file */ + if ((rc=stonith_set_config_file(s, optfile)) != S_OK) { + log_msg(LOG_ERR + , "Invalid config file for %s device." + , SwitchType); +#if 0 + log_msg(LOG_INFO, "Config file syntax: %s" + , s->s_ops->getinfo(s, ST_CONF_FILE_SYNTAX)); +#endif + stonith_delete(s); s=NULL; + exit(S_BADCONFIG); + } + }else if (params_from_env) { + /* Configure Stonith object from the environment */ + StonithNVpair * pairs; + if ((pairs = stonith_env_to_NVpair(s)) == NULL) { + fprintf(stderr + , "Invalid config info for %s device.\n" + , SwitchType); + stonith_delete(s); s=NULL; + exit(1); + } + if ((rc = stonith_set_config(s, pairs)) != S_OK) { + fprintf(stderr + , "Invalid config info for %s device\n" + , SwitchType); + } + }else if (parameters) { + /* Configure Stonith object from the -p argument */ + StonithNVpair * pairs; + if ((pairs = stonith1_compat_string_to_NVpair + ( s, parameters)) == NULL) { + fprintf(stderr + , "Invalid STONITH -p parameter [%s]\n" + , parameters); + stonith_delete(s); s=NULL; + exit(1); + } + if ((rc = stonith_set_config(s, pairs)) != S_OK) { + fprintf(stderr + , "Invalid config info for %s device\n" + , SwitchType); + } + }else{ + /* + * Configure STONITH device using cmdline arguments... + */ + if ((rc = stonith_set_config(s, nvargs)) != S_OK) { + const char * const * names; + int j; + fprintf(stderr + , "Invalid config info for %s device\n" + , SwitchType); + + names = stonith_get_confignames(s); + + if (names != NULL) { + fprintf(stderr + , "Valid config names are:\n"); + + for (j=0; names[j]; ++j) { + fprintf(stderr + , "\t%s\n", names[j]); + } + } + stonith_delete(s); s=NULL; + exit(rc); + } + } + + + for (j=0; j < count; ++j) { + rc = S_OK; + + if (status) { + rc = stonith_get_status(s); + + if (!silent) { + if (rc == S_OK) { + log_msg((log_destination == LOG_TERMINAL) ? + LOG_INFO : LOG_DEBUG, + "%s device OK.", SwitchType); + }else{ + /* Uh-Oh */ + log_msg(LOG_ERR, "%s device not accessible." + , SwitchType); + } + } + } + + if (listhosts) { + char ** hostlist; + + hostlist = stonith_get_hostlist(s); + if (hostlist == NULL) { + log_msg(LOG_ERR, "Could not list hosts for %s." + , SwitchType); + rc = -1; + }else{ + char ** this; + + for(this=hostlist; *this; ++this) { + printf("%s\n", *this); + } + stonith_free_hostlist(hostlist); + } + } + + if (optind < argc) { + char *nodename; + nodename = g_strdup(argv[optind]); + strdown(nodename); + rc = stonith_req_reset(s, reset_type, nodename); + g_free(nodename); + } + } + stonith_delete(s); s = NULL; + return(rc); +} |