summaryrefslogtreecommitdiffstats
path: root/lib/stonith
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/stonith/Makefile.am54
-rw-r--r--lib/stonith/README31
-rw-r--r--lib/stonith/expect.c539
-rwxr-xr-xlib/stonith/ha_log.sh114
-rw-r--r--lib/stonith/main.c727
-rw-r--r--lib/stonith/meatclient.c152
-rw-r--r--lib/stonith/st_ttylock.c225
-rw-r--r--lib/stonith/stonith.c636
8 files changed, 2478 insertions, 0 deletions
diff --git a/lib/stonith/Makefile.am b/lib/stonith/Makefile.am
new file mode 100644
index 0000000..429e1d3
--- /dev/null
+++ b/lib/stonith/Makefile.am
@@ -0,0 +1,54 @@
+#
+# Stonith: Shoot The Node In The Head
+#
+# Copyright (C) 2001 Alan Robertson
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+## include files
+
+## binaries
+sbin_PROGRAMS = stonith meatclient
+
+stonith_SOURCES = main.c
+
+stonith_LDADD = libstonith.la $(top_builddir)/lib/pils/libpils.la $(GLIBLIB) \
+ $(top_builddir)/lib/clplumbing/libplumb.la \
+ $(top_builddir)/lib/clplumbing/libplumbgpl.la
+stonith_LDFLAGS = @LIBADD_DL@ @LIBLTDL@ -export-dynamic @DLOPEN_FORCE_FLAGS@ @LIBADD_INTL@
+
+meatclient_SOURCES = meatclient.c
+meatclient_LDADD = $(GLIBLIB) libstonith.la
+
+## libraries
+
+lib_LTLIBRARIES = libstonith.la
+
+libstonith_la_SOURCES = expect.c stonith.c st_ttylock.c
+libstonith_la_LDFLAGS = -version-info 1:0:0
+libstonith_la_LIBADD = $(top_builddir)/lib/pils/libpils.la \
+ $(top_builddir)/replace/libreplace.la \
+ $(GLIBLIB)
+
+helperdir = $(datadir)/$(PACKAGE_NAME)
+helper_SCRIPTS = ha_log.sh
+
+EXTRA_DIST = $(helper_SCRIPTS)
diff --git a/lib/stonith/README b/lib/stonith/README
new file mode 100644
index 0000000..6b98ef9
--- /dev/null
+++ b/lib/stonith/README
@@ -0,0 +1,31 @@
+The STONITH module (a.k.a. STOMITH) provides an extensible interface
+for remotely powering down a node in the cluster. The idea is quite simple:
+When the software running on one machine wants to make sure another
+machine in the cluster is not using a resource, pull the plug on the other
+machine. It's simple and reliable, albiet admittedly brutal.
+
+Here's an example command line invocation used to power off a machine
+named 'nodeb'. The parameters are dependent on the type of device you
+are using for this capability.
+
+stonith -t rps10 -p "/dev/ttyS5 nodeb 0 " nodeb
+
+Currently supported devices:
+
+ apcsmart: APCSmart (tested with 2 old 900XLI)
+ baytech: Baytech RPC5
+ meatware: Alerts an operator to manually turn off a device.
+ nw_rpc100s: Micro Energetics Night/Ware RPC100S
+ rps10: Western Telematics RPS10
+ vacm_stonith: VA Linux Cluster Manager (see README.vacm)
+
+
+To see the parameter syntax for a module, run the 'stonith' command and omit the
+-p parameter. For example:
+
+$ /usr/sbin/stonith -t rps10 test
+
+stonith: Invalid config file for rps10 device.
+stonith: Config file syntax: <serial_device> <node> <outlet> [ <node> <outlet> [...] ]
+All tokens are white-space delimited.
+Blank lines and lines beginning with # are ignored
diff --git a/lib/stonith/expect.c b/lib/stonith/expect.c
new file mode 100644
index 0000000..bb1f818
--- /dev/null
+++ b/lib/stonith/expect.c
@@ -0,0 +1,539 @@
+/*
+ * Simple expect module for the STONITH library
+ *
+ * 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 <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stonith/st_ttylock.h>
+#include <clplumbing/longclock.h>
+#define ENABLE_PIL_DEFS_PRIVATE
+#include <pils/plugin.h>
+
+#ifdef _POSIX_PRIORITY_SCHEDULING
+# include <sched.h>
+#endif
+
+#include <stonith/stonith.h>
+#include <stonith/stonith_plugin.h>
+
+extern PILPluginUniv* StonithPIsys;
+
+#define LOG(args...) PILCallLog(StonithPIsys->imports->log, args)
+#define DEBUG(args...) LOG(PIL_DEBUG, args)
+#undef DEBUG
+#define DEBUG(args...) PILCallLog(StonithPIsys->imports->log, PIL_DEBUG, args)
+#define MALLOC StonithPIsys->imports->alloc
+#define REALLOC StonithPIsys->imports->mrealloc
+#define STRDUP StonithPIsys->imports->mstrdup
+#define FREE(p) {StonithPIsys->imports->mfree(p); (p) = NULL;}
+
+#ifdef TIMES_ALLOWS_NULL_PARAM
+# define TIMES_PARAM NULL
+#else
+ static struct tms dummy_longclock_tms_struct;
+# define TIMES_PARAM &dummy_longclock_tms_struct
+#endif
+
+static unsigned long
+our_times(void) /* Make times(2) behave rationally on Linux */
+{
+ clock_t ret;
+#ifndef DISABLE_TIMES_KLUDGE
+ int save_errno = errno;
+
+ /*
+ * This code copied from clplumbing/longclock.c to avoid
+ * making STONITH depend on clplumbing. See it for an explanation
+ */
+
+ errno = 0;
+#endif /* DISABLE_TIMES_KLUDGE */
+
+ ret = times(TIMES_PARAM);
+
+#ifndef DISABLE_TIMES_KLUDGE
+ if (errno != 0) {
+ ret = (clock_t) (-errno);
+ }
+ errno = save_errno;
+#endif /* DISABLE_TIMES_KLUDGE */
+ return (unsigned long)ret;
+}
+
+/*
+ * Look for ('expect') any of a series of tokens in the input
+ * Return the token type for the given token or -1 on error.
+ */
+
+static int
+ExpectToken(int fd, struct Etoken * toklist, int to_secs, char * savebuf
+, int maxline, int Debug)
+{
+ unsigned long starttime;
+ unsigned long endtime;
+ int wraparound=0;
+ unsigned Hertz = sysconf(_SC_CLK_TCK);
+ int tickstousec = (1000000/Hertz);
+ unsigned long now;
+ unsigned long ticks;
+ int nchars = 1; /* reserve space for an EOS */
+ struct timeval tv;
+ char * buf = savebuf;
+
+ struct Etoken * this;
+
+ /* Figure out when to give up. Handle lbolt wraparound */
+
+ starttime = our_times();
+ ticks = (to_secs*Hertz);
+ endtime = starttime + ticks;
+
+ if (endtime < starttime) {
+ wraparound = 1;
+ }
+
+ if (buf) {
+ *buf = EOS;
+ }
+
+ for (this=toklist; this->string; ++this) {
+ this->matchto = 0;
+ }
+
+
+ while (now = our_times(),
+ (wraparound && (now > starttime || now <= endtime))
+ || (!wraparound && now <= endtime)) {
+
+ fd_set infds;
+ char ch;
+ unsigned long timeleft;
+ int retval;
+
+ timeleft = endtime - now;
+
+ tv.tv_sec = timeleft / Hertz;
+ tv.tv_usec = (timeleft % Hertz) * tickstousec;
+
+ if (tv.tv_sec == 0 && tv.tv_usec < tickstousec) {
+ /* Give 'em a little chance */
+ tv.tv_usec = tickstousec;
+ }
+
+ /* Watch our FD to see when it has input. */
+ FD_ZERO(&infds);
+ FD_SET(fd, &infds);
+
+ retval = select(fd+1, &infds, NULL, NULL, &tv);
+ if (retval <= 0) {
+ errno = ETIMEDOUT;
+ return(-1);
+ }
+ /* Whew! All that work just to read one character! */
+
+ if (read(fd, &ch, sizeof(ch)) <= 0) {
+ return(-1);
+ }
+ /* Save the text, if we can */
+ if (buf && nchars < maxline-1) {
+ *buf = ch;
+ ++buf;
+ *buf = EOS;
+ ++nchars;
+ }
+ if (Debug > 1) {
+ DEBUG("Got '%c'", ch);
+ }
+
+ /* See how this character matches our expect strings */
+
+ for (this=toklist; this->string; ++this) {
+
+ if (ch == this->string[this->matchto]) {
+
+ /* It matches the current token */
+
+ ++this->matchto;
+ if (this->string[this->matchto] == EOS){
+ /* Hallelujah! We matched */
+ if (Debug) {
+ DEBUG("Matched [%s] [%d]"
+ , this->string
+ , this->toktype);
+ if (savebuf) {
+ DEBUG("Saved [%s]"
+ , savebuf);
+ }
+ }
+ return(this->toktype);
+ }
+ }else{
+
+ /* It doesn't appear to match this token */
+
+ int curlen;
+ int nomatch=1;
+ /*
+ * If we already had a match (matchto is
+ * greater than zero), we look for a match
+ * of the tail of the pattern matched so far
+ * (with the current character) against the
+ * head of the pattern.
+ */
+
+ /*
+ * This is to make the string "aab" match
+ * the pattern "ab" correctly
+ * Painful, but nice to do it right.
+ */
+
+ for (curlen = (this->matchto)
+ ; nomatch && curlen >= 0
+ ; --curlen) {
+ const char * tail;
+ tail=(this->string)
+ + this->matchto
+ - curlen;
+
+ if (strncmp(this->string, tail
+ , curlen) == 0
+ && this->string[curlen] == ch) {
+
+ if (this->string[curlen+1]==EOS){
+ /* We matched! */
+ /* (can't happen?) */
+ return(this->toktype);
+ }
+ this->matchto = curlen+1;
+ nomatch=0;
+ }
+ }
+ if (nomatch) {
+ this->matchto = 0;
+ }
+ }
+ }
+ }
+ errno = ETIMEDOUT;
+ return(-1);
+}
+
+/*
+ * Start a process with its stdin and stdout redirected to pipes
+ * so the parent process can talk to it.
+ */
+static int
+StartProcess(const char * cmd, int * readfd, int * writefd)
+{
+ pid_t pid;
+ int wrpipe[2]; /* The pipe the parent process writes to */
+ /* (which the child process reads from) */
+ int rdpipe[2]; /* The pipe the parent process reads from */
+ /* (which the child process writes to) */
+
+ if (pipe(wrpipe) < 0) {
+ perror("cannot create pipe\n");
+ return(-1);
+ }
+ if (pipe(rdpipe) < 0) {
+ perror("cannot create pipe\n");
+ close(wrpipe[0]);
+ close(wrpipe[1]);
+ return(-1);
+ }
+ switch(pid=fork()) {
+
+ case -1: perror("cannot StartProcess cmd");
+ close(rdpipe[0]);
+ close(wrpipe[1]);
+ close(wrpipe[0]);
+ close(rdpipe[1]);
+ return(-1);
+
+ case 0: /* We are the child */
+
+ /* Redirect stdin */
+ close(0);
+ dup2(wrpipe[0], 0);
+ close(wrpipe[0]);
+ close(wrpipe[1]);
+
+ /* Redirect stdout */
+ close(1);
+ dup2(rdpipe[1], 1);
+ close(rdpipe[0]);
+ close(rdpipe[1]);
+#if defined(SCHED_OTHER) && !defined(ON_DARWIN)
+ {
+ /*
+ * Try and (re)set our scheduling to "normal"
+ * Sometimes our callers run in soft
+ * real-time mode. The program we exec might
+ * not be very well behaved - this is bad for
+ * operation in high-priority (soft real-time)
+ * mode. In particular, telnet is prone to
+ * going into infinite loops when killed.
+ */
+ struct sched_param sp;
+ memset(&sp, 0, sizeof(sp));
+ sp.sched_priority = 0;
+ sched_setscheduler(0, SCHED_OTHER, &sp);
+ }
+#endif
+ execlp("/bin/sh", "sh", "-c", cmd, (const char *)NULL);
+ perror("cannot exec shell!");
+ exit(1);
+
+ default: /* We are the parent */
+ *readfd = rdpipe[0];
+ close(rdpipe[1]);
+
+ *writefd = wrpipe[1];
+ close(wrpipe[0]);
+ return(pid);
+ }
+ /*NOTREACHED*/
+ return(-1);
+}
+
+static char **
+stonith_copy_hostlist(const char * const * hostlist)
+{
+ int hlleng = 1;
+ const char * const * here = hostlist;
+ char ** hret;
+ char ** ret;
+
+ for (here = hostlist; *here; ++here) {
+ ++hlleng;
+ }
+ ret = (char**)MALLOC(hlleng * sizeof(char *));
+ if (ret == NULL) {
+ return ret;
+ }
+
+ hret = ret;
+ for (here = hostlist; *here; ++here,++hret) {
+ *hret = STRDUP(*here);
+ if (*hret == NULL) {
+ stonith_free_hostlist(ret);
+ return NULL;
+ }
+ }
+ *hret = NULL;
+ return ret;
+}
+
+static char **
+StringToHostList(const char * s)
+{
+ const char * here;
+ int hlleng = 0;
+ char ** ret;
+ char ** hret;
+ const char * delims = " \t\n\f\r,";
+
+ /* Count the number of strings (words) in the result */
+ here = s;
+ while (*here != EOS) {
+ /* skip delimiters */
+ here += strspn(here, delims);
+ if (*here == EOS) {
+ break;
+ }
+ /* skip over substring proper... */
+ here += strcspn(here, delims);
+ ++hlleng;
+ }
+
+
+ /* Malloc space for the result string pointers */
+ ret = (char**)MALLOC((hlleng+1) * sizeof(char *));
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ hret = ret;
+ here = s;
+
+ /* Copy each substring into a separate string */
+ while (*here != EOS) {
+ int slen; /* substring length */
+
+ /* skip delimiters */
+ here += strspn(here, delims);
+ if (*here == EOS) {
+ break;
+ }
+ /* Compute substring length */
+ slen = strcspn(here, delims);
+ *hret = MALLOC((slen+1) * sizeof(char));
+ if (*hret == NULL) {
+ stonith_free_hostlist(hret);
+ return NULL;
+ }
+ /* Copy string (w/o EOS) */
+ memcpy(*hret, here, slen);
+ /* Add EOS to result string */
+ (*hret)[slen] = EOS;
+ strdown(*hret);
+ here += slen;
+ ++hret;
+ }
+ *hret = NULL;
+ return ret;
+}
+
+
+static const char *
+GetValue(StonithNVpair* parameters, const char * name)
+{
+ while (parameters->s_name) {
+ if (strcmp(name, parameters->s_name) == 0) {
+ return parameters->s_value;
+ }
+ ++parameters;
+ }
+ return NULL;
+}
+
+static int
+CopyAllValues(StonithNamesToGet* output, StonithNVpair * input)
+{
+ int j;
+ int rc;
+
+ for (j=0; output[j].s_name; ++j) {
+ const char * value = GetValue(input, output[j].s_name);
+ if (value == NULL) {
+ rc = S_INVAL;
+ output[j].s_value = NULL;
+ goto fail;
+ }
+ if ((output[j].s_value = STRDUP(value)) == NULL) {
+ rc = S_OOPS;
+ goto fail;
+ }
+ }
+ return S_OK;
+
+fail:
+ for (j=0; output[j].s_value; ++j) {
+ FREE(output[j].s_value);
+ }
+ return rc;
+}
+
+
+static int
+OpenStreamSocket(const char * host, int port, const char * service)
+{
+ union s_un {
+ struct sockaddr_in si4;
+ struct sockaddr_in6 si6;
+ }sockun;
+ int sock;
+ int addrlen = -1;
+
+
+ memset(&sockun, 0, sizeof(sockun));
+
+ if (inet_pton(AF_INET, host, (void*)&sockun.si4.sin_addr) < 0) {
+ sockun.si4.sin_family = AF_INET;
+ }else if (inet_pton(AF_INET6, host, (void*)&sockun.si6.sin6_addr)<0){
+ sockun.si6.sin6_family = AF_INET6;
+ }else{
+ struct hostent* hostp = gethostbyname(host);
+ if (hostp == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ sockun.si4.sin_family = hostp->h_addrtype;
+ memcpy(&sockun.si4.sin_addr, hostp->h_addr, hostp->h_length);
+ }
+ if ((sock = socket(sockun.si4.sin_family, SOCK_STREAM, 0)) < 0) {
+ return -1;
+ }
+ if (service != NULL) {
+ struct servent* se = getservbyname(service, "tcp");
+ if (se != NULL) {
+ /* We convert it back later... */
+ port = ntohs(se->s_port);
+ }
+ }
+ if (port <= 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ port = htons(port);
+ if (sockun.si6.sin6_family == AF_INET6) {
+ sockun.si6.sin6_port = port;
+ addrlen = sizeof(sockun.si6);
+ }else if (sockun.si4.sin_family == AF_INET) {
+ sockun.si4.sin_port = port;
+ addrlen = sizeof(sockun.si4);
+ }else{
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (connect(sock, (struct sockaddr*)(&sockun), addrlen)< 0){
+ int save = errno;
+ perror("connect() failed");
+ close(sock);
+ errno = save;
+ return -1;
+ }
+ return sock;
+}
+
+StonithImports stonithimports = {
+ ExpectToken,
+ StartProcess,
+ OpenStreamSocket,
+ GetValue,
+ CopyAllValues,
+ StringToHostList,
+ stonith_copy_hostlist,
+ stonith_free_hostlist,
+ st_ttylock,
+ st_ttyunlock
+};
diff --git a/lib/stonith/ha_log.sh b/lib/stonith/ha_log.sh
new file mode 100755
index 0000000..73093f0
--- /dev/null
+++ b/lib/stonith/ha_log.sh
@@ -0,0 +1,114 @@
+#!/bin/sh
+#
+#
+# ha_log.sh for stonith external plugins
+# (equivalent to ocf_log in ocf-shellfuncs in resource-agents)
+#
+# Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Brée
+# All Rights Reserved.
+#
+#
+# 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
+#
+
+# Build version: @GLUE_BUILD_VERSION@
+
+PROG=`basename $0`
+
+: ${HA_DATEFMT=+"%b %d %T"}
+: ${HA_LOGD=yes}
+: ${HA_LOGTAG=""}
+: ${HA_LOGFACILITY=daemon}
+: ${HA_LOGFILE=""}
+: ${HA_DEBUGLOG=""}
+: ${HA_debug="0"}
+
+hadate() {
+ date "+$HA_DATEFMT"
+}
+
+level_pres() {
+ case "$1" in
+ crit) echo "CRIT";;
+ err|error) echo "ERROR";;
+ warn|warning) echo "WARN";;
+ notice) echo "notice";;
+ info) echo "info";;
+ debug) echo "debug";;
+ *)
+ ha_log err "$PROG: unrecognized loglevel: $1"
+ exit 1
+ ;;
+ esac
+}
+
+set_logtag() {
+ # add parent pid to the logtag
+ if [ "$HA_LOGTAG" ]; then
+ if [ -n "$CRM_meta_st_device_id" ]; then
+ HA_LOGTAG="$HA_LOGTAG($CRM_meta_st_device_id)[$PPID]"
+ else
+ HA_LOGTAG="$HA_LOGTAG[$PPID]"
+ fi
+ fi
+}
+
+ha_log() {
+ loglevel=$1
+ shift
+ prn_level=`level_pres $loglevel`
+ msg="$prn_level: $@"
+
+ if [ "x$HA_debug" = "x0" -a "x$loglevel" = xdebug ] ; then
+ return 0
+ fi
+
+ set_logtag
+
+ # if we're connected to a tty, then output to stderr
+ if tty >/dev/null; then
+ if [ "$HA_LOGTAG" ]; then
+ echo "$HA_LOGTAG: $msg"
+ else
+ echo "$msg"
+ fi >&2
+ return 0
+ fi
+
+ [ "x$HA_LOGD" = "xyes" ] &&
+ cat<<EOF | ha_logger -t "$HA_LOGTAG" && return 0
+$msg
+EOF
+
+ if [ -n "$HA_LOGFACILITY" -a "$HA_LOGFACILITY" != none ]; then
+ logger -t "$HA_LOGTAG" -p $HA_LOGFACILITY.$loglevel "$msg"
+ fi
+ dest=${HA_LOGFILE:-$HA_DEBUGLOG}
+ if [ -n "$dest" ]; then
+ msg="$prn_level: `hadate` $@"
+ echo "$HA_LOGTAG: $msg" >> $dest
+ fi
+}
+
+if [ $# -lt 2 ]; then
+ ha_log err "$PROG: not enough arguments [$#]"
+ exit 1
+fi
+
+loglevel="$1"
+shift 1
+msg="$*"
+
+ha_log "$loglevel" "$msg"
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);
+}
diff --git a/lib/stonith/meatclient.c b/lib/stonith/meatclient.c
new file mode 100644
index 0000000..e95dc0e
--- /dev/null
+++ b/lib/stonith/meatclient.c
@@ -0,0 +1,152 @@
+/*
+ * Stonith client for Human Operator Stonith device
+ *
+ * Copyright (c) 2001 Gregor Binder <gbinder@sysfive.com>
+ *
+ * This program is a rewrite of the "do_meatware" program by
+ * David C. Teigland <teigland@sistina.com> originally appeared
+ * in the GFS stomith meatware agent.
+ *
+ * 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 <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stonith/stonith.h>
+#include <glib.h>
+
+#define OPTIONS "c:w"
+
+void usage(const char * cmd);
+
+void
+usage(const char * cmd)
+{
+ fprintf(stderr, "usage: %s -c node [-w]\n", cmd);
+ exit(S_INVAL);
+}
+
+extern char * optarg;
+extern int optind, opterr, optopt;
+int
+main(int argc, char** argv)
+{
+ char * cmdname;
+ const char * meatpipe_pr = HA_VARRUNDIR "/meatware"; /* if you intend to
+ change this, modify
+ meatware.c as well */
+ char * opthost = NULL;
+ int clearhost = 0;
+
+ int c, argcount, waitmode = 0;
+ int errors = 0;
+
+ if ((cmdname = strrchr(argv[0], '/')) == NULL) {
+ cmdname = argv[0];
+ }else{
+ ++cmdname;
+ }
+
+ while ((c = getopt(argc, argv, OPTIONS)) != -1) {
+ switch(c) {
+ case 'c': opthost = optarg;
+ ++clearhost;
+ break;
+ case 'w': ++waitmode;
+ break;
+ default: ++errors;
+ break;
+ }
+ }
+ argcount = argc - optind;
+ if (!(argcount == 0) || !opthost) {
+ errors++;
+ }
+
+ if (errors) {
+ usage(cmdname);
+ }
+
+ strdown(opthost);
+
+ if (clearhost) {
+
+ int rc, fd;
+ char resp[3];
+
+ char line[256];
+ char meatpipe[256];
+
+ gboolean waited=FALSE;
+
+ snprintf(meatpipe, 256, "%s.%s", meatpipe_pr, opthost);
+
+ while(1) {
+ fd = open(meatpipe, O_WRONLY | O_NONBLOCK);
+ if (fd >= 0)
+ break;
+ if (!waitmode || (errno != ENOENT && errno != ENXIO)) {
+ if (waited) printf("\n");
+ snprintf(line, sizeof(line)
+ , "Meatware_IPC failed: %s", meatpipe);
+ perror(line);
+ exit(S_BADHOST);
+ }
+ printf("."); fflush(stdout); waited=TRUE;
+ sleep(1);
+ }
+ if (waited) printf("\n");
+
+ printf("\nWARNING!\n\n"
+ "If node \"%s\" has not been manually power-cycled or "
+ "disconnected from all shared resources and networks, "
+ "data on shared disks may become corrupted and "
+ "migrated services might not work as expected.\n"
+ "Please verify that the name or address above "
+ "corresponds to the node you just rebooted.\n\n"
+ "PROCEED? [yN] ", opthost);
+
+ rc = scanf("%s", resp);
+
+ if (rc == 0 || rc == EOF || tolower(resp[0] != 'y')) {
+ printf("Meatware_client: operation canceled.\n");
+ exit(S_INVAL);
+ }
+
+ sprintf(line, "meatware reply %s", opthost);
+
+ rc = write(fd, line, 256);
+
+ if (rc < 0) {
+ sprintf(line, "Meatware_IPC failed: %s", meatpipe);
+ perror(line);
+ exit(S_OOPS);
+ }
+
+ printf("Meatware_client: reset confirmed.\n");
+ }
+
+ exit(S_OK);
+}
diff --git a/lib/stonith/st_ttylock.c b/lib/stonith/st_ttylock.c
new file mode 100644
index 0000000..adc918d
--- /dev/null
+++ b/lib/stonith/st_ttylock.c
@@ -0,0 +1,225 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <clplumbing/cl_signal.h>
+#include <stonith/st_ttylock.h>
+
+/*
+ * The following information is from the Filesystem Hierarchy Standard
+ * version 2.1 dated 12 April, 2000.
+ *
+ * 5.6 /var/lock : Lock files
+ * Lock files should be stored within the /var/lock directory structure.
+ * Device lock files, such as the serial device lock files that were originally
+ * found in either /usr/spool/locks or /usr/spool/uucp, must now be stored in
+ * /var/lock. The naming convention which must be used is LCK.. followed by
+ * the base name of the device file. For example, to lock /dev/cua0 the file
+ * LCK..cua0 would be created.
+ *
+ * The format used for device lock files must be the HDB UUCP lock file format.
+ * The HDB format is to store the process identifier (PID) as a ten byte
+ * ASCII decimal number, with a trailing newline. For example, if process 1230
+ * holds a lock file, it would contain the eleven characters: space, space,
+ * space, space, space, space, one, two, three, zero, and newline.
+ * Then, anything wishing to use /dev/cua0 can read the lock file and act
+ * accordingly (all locks in /var/lock should be world-readable).
+ *
+ *
+ * PERMISSIONS NOTE:
+ * Different linux distributions set the mode of the lock directory differently
+ * Any process which wants to create lock files must have write permissions
+ * on HA_VARLOCKDIR (probably /var/lock). For things like the heartbeat API
+ * code, this may mean allowing the uid of the processes that use this API
+ * to join group uucp, or making the binaries setgid to uucp.
+ */
+
+#define DEVDIR "/dev/"
+#define DEVLEN (sizeof(DEVDIR)-1)
+
+static void raw_device (const char *dev, char *dest_name, size_t size);
+static int DoLock(const char * prefix, const char *lockname);
+static int DoUnlock(const char * prefix, const char *lockname);
+
+/* The code in this file originally written by Guenther Thomsen */
+/* Somewhat mangled by Alan Robertson */
+
+/*
+ * Lock a tty (using lock files, see linux `man 2 open` close to O_EXCL)
+ * serial_device has to be _the complete path_, i.e. including '/dev/' to the
+ * special file, which denotes the tty to lock -tho
+ * return 0 on success,
+ * -1 if device is locked (lockfile exists and isn't stale),
+ * -2 for temporarily failure, try again,
+ * other negative value, if something unexpected happend (failure anyway)
+ */
+
+static void
+raw_device (const char *serial_device, char *dest_name, size_t size)
+{
+ char* dp = dest_name;
+ const char* sp = serial_device+DEVLEN;
+ const char* dpend = dp + size - 1;
+
+ while (*sp != '\0' && dp < dpend) {
+ if (isalnum((unsigned int)*sp))
+ *dp++ = *sp;
+ sp++;
+ }
+ *dp = EOS;
+}
+
+int
+st_ttylock(const char *serial_device)
+{
+ char rawname[64];
+
+ if (serial_device == NULL) {
+ errno = EFAULT;
+ return -3;
+ }
+ raw_device (serial_device, rawname, sizeof(rawname));
+ return(DoLock("LCK..", rawname));
+}
+
+/*
+ * Unlock a tty (remove its lockfile)
+ * do we need to check, if its (still) ours? No, IMHO, if someone else
+ * locked our line, it's his fault -tho
+ * returns 0 on success
+ * <0 if some failure occured
+ */
+
+int
+st_ttyunlock(const char *serial_device)
+{
+ char rawname[64];
+
+ if (serial_device == NULL) {
+ errno = EFAULT;
+ return -3;
+ }
+
+ raw_device (serial_device, rawname, sizeof(rawname));
+ return(DoUnlock("LCK..", rawname));
+}
+
+/* This is what the FHS standard specifies for the size of our lock file */
+#define LOCKSTRLEN 11
+
+static int
+DoLock(const char * prefix, const char *lockname)
+{
+ char lf_name[256], tf_name[256], buf[LOCKSTRLEN+1];
+ int fd;
+ long pid, mypid;
+ int rc;
+ struct stat sbuf;
+
+ mypid = (unsigned long) getpid();
+
+ snprintf(lf_name, sizeof(lf_name), "%s/%s%s"
+ , HA_VARLOCKDIR, prefix, lockname);
+
+ snprintf(tf_name, sizeof(tf_name), "%s/tmp%lu-%s"
+ , HA_VARLOCKDIR, mypid, lockname);
+
+ if ((fd = open(lf_name, O_RDONLY)) >= 0) {
+ if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) {
+ sleep(1); /* if someone was about to create one,
+ * give'm a sec to do so
+ * Though if they follow our protocol,
+ * this won't happen. They should really
+ * put the pid in, then link, not the
+ * other way around.
+ */
+ }
+ if (read(fd, buf, sizeof(buf)) < 1) {
+ /* lockfile empty -> rm it and go on */;
+ } else {
+ if (sscanf(buf, "%lu", &pid) < 1) {
+ /* lockfile screwed up -> rm it and go on */
+ } else {
+ if (pid > 1 && ((long)getpid() != pid)
+ && ((CL_KILL((pid_t)pid, 0) >= 0)
+ || errno != ESRCH)) {
+ /* tty is locked by existing (not
+ * necessarily running) process
+ * -> give up */
+ close(fd);
+ return -1;
+ } else {
+ /* stale lockfile -> rm it and go on */
+ }
+ }
+ }
+ unlink(lf_name);
+ }
+ if ((fd = open(tf_name, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) {
+ /* Hmmh, why did we fail? Anyway, nothing we can do about it */
+ return -3;
+ }
+
+ /* Slight overkill with the %*d format ;-) */
+ snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN-1, mypid);
+
+ if (write(fd, buf, LOCKSTRLEN) != LOCKSTRLEN) {
+ /* Again, nothing we can do about this */
+ return -3;
+ }
+ close(fd);
+
+ switch (link(tf_name, lf_name)) {
+ case 0:
+ if (stat(tf_name, &sbuf) < 0) {
+ /* something weird happened */
+ rc = -3;
+ break;
+ }
+ if (sbuf.st_nlink < 2) {
+ /* somehow, it didn't get through - NFS trouble? */
+ rc = -2;
+ break;
+ }
+ rc = 0;
+ break;
+ case EEXIST:
+ rc = -1;
+ break;
+ default:
+ rc = -3;
+ }
+ unlink(tf_name);
+ return rc;
+}
+
+static int
+DoUnlock(const char * prefix, const char *lockname)
+{
+ char lf_name[256];
+
+ snprintf(lf_name, sizeof(lf_name), "%s/%s%s", HA_VARLOCKDIR
+ , prefix, lockname);
+ return unlink(lf_name);
+}
diff --git a/lib/stonith/stonith.c b/lib/stonith/stonith.c
new file mode 100644
index 0000000..4ced8c7
--- /dev/null
+++ b/lib/stonith/stonith.c
@@ -0,0 +1,636 @@
+/*
+ * Stonith API infrastructure.
+ *
+ * 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 <sys/wait.h>
+#include <sys/param.h>
+#include <dlfcn.h>
+#include <dirent.h>
+#include <glib.h>
+#define ENABLE_PIL_DEFS_PRIVATE
+#include <pils/plugin.h>
+#include <pils/generic.h>
+#include <stonith/stonith.h>
+#include <stonith/stonith_plugin.h>
+
+
+#define MALLOC StonithPIsys->imports->alloc
+#ifdef MALLOCT
+# undef MALLOCT
+#endif
+#define MALLOCT(t) (t*)(MALLOC(sizeof(t)))
+#define REALLOC StonithPIsys->imports->mrealloc
+#define STRDUP StonithPIsys->imports->mstrdup
+#define FREE(p) {StonithPIsys->imports->mfree(p); (p) = NULL;}
+
+#define LOG(args...) PILCallLog(StonithPIsys->imports->log, args)
+
+#define EXTPINAME_S "external"
+#define RHCSPINAME_S "rhcs"
+
+PILPluginUniv* StonithPIsys = NULL;
+static GHashTable* Splugins = NULL;
+static int init_pluginsys(void);
+extern StonithImports stonithimports;
+
+static PILGenericIfMgmtRqst Reqs[] =
+{
+ {STONITH_TYPE_S, &Splugins, &stonithimports, NULL, NULL},
+ {NULL, NULL, NULL, NULL, NULL}
+};
+
+void PILpisysSetDebugLevel(int);
+/* Initialize the plugin system... */
+static int
+init_pluginsys(void) {
+
+ if (StonithPIsys) {
+ return TRUE;
+ }
+
+
+ /* PILpisysSetDebugLevel(10); */
+ StonithPIsys = NewPILPluginUniv(STONITH_MODULES);
+
+ if (StonithPIsys) {
+ int rc = PILLoadPlugin(StonithPIsys, PI_IFMANAGER, "generic", Reqs);
+ if (rc != PIL_OK) {
+ fprintf(stderr, "generic plugin load failed: %d\n", rc);
+ DelPILPluginUniv(StonithPIsys);
+ StonithPIsys = NULL;
+ }
+ /*PILSetDebugLevel(StonithPIsys, PI_IFMANAGER, "generic", 10);*/
+ }else{
+ fprintf(stderr, "pi univ creation failed\n");
+ }
+ return StonithPIsys != NULL;
+}
+
+/*
+ * Create a new Stonith object of the requested type.
+ */
+
+Stonith *
+stonith_new(const char * type)
+{
+ StonithPlugin * sp = NULL;
+ struct stonith_ops* ops = NULL;
+ char * key;
+ char * subplugin;
+ char * typecopy;
+
+
+ if (!init_pluginsys()) {
+ return NULL;
+ }
+
+ if ((typecopy = STRDUP(type)) == NULL) {
+ return NULL;
+ }
+
+ if (((subplugin = strchr(typecopy, '/')) != NULL) &&
+ (strncmp(EXTPINAME_S, typecopy, strlen(EXTPINAME_S)) == 0 ||
+ strncmp(RHCSPINAME_S, typecopy, strlen(RHCSPINAME_S)) == 0)) {
+ *subplugin++ = 0; /* make two strings */
+ }
+
+ /* Look and see if it's already loaded... */
+
+ if (g_hash_table_lookup_extended(Splugins, typecopy
+ , (gpointer)&key, (gpointer)&ops)) {
+ /* Yes! Increment reference count */
+ PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S, typecopy, 1);
+
+ }else{ /* No. Try and load it... */
+ if (PILLoadPlugin(StonithPIsys, STONITH_TYPE_S, typecopy, NULL)
+ != PIL_OK) {
+ FREE(typecopy);
+ return NULL;
+ }
+
+ /* Look up the plugin in the Splugins table */
+ if (!g_hash_table_lookup_extended(Splugins, typecopy
+ , (void*)&key, (void*)&ops)) {
+ /* OOPS! didn't find it(!?!)... */
+ PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S
+ , typecopy, -1);
+ FREE(typecopy);
+ return NULL;
+ }
+ }
+
+ if (ops != NULL) {
+ sp = ops->new((const char *)(subplugin));
+ if (sp != NULL) {
+ sp->s.stype = STRDUP(typecopy);
+ }
+ }
+
+ FREE(typecopy);
+ return sp ? (&sp->s) : NULL;
+}
+
+static int
+qsort_string_cmp(const void *a, const void *b)
+{
+ return(strcmp(*(const char * const *)a, *(const char * const *)b));
+}
+
+/*
+ * Return list of STONITH types valid in stonith_new()
+ */
+
+static char **
+get_plugin_list(const char *pltype)
+{
+ char ** typelist = NULL;
+ const char * const *extPI;
+ const char * const *p;
+ int numextPI, i;
+ Stonith * ext;
+
+ /* let the external plugin return a list */
+ if ((ext = stonith_new(pltype)) == NULL) {
+ LOG(PIL_CRIT, "Cannot create new external "
+ "plugin object");
+ return NULL;
+ }
+ if ((extPI = stonith_get_confignames(ext)) == NULL) {
+ /* don't complain if rhcs plugins are not installed */
+ if (strcmp(pltype, "rhcs"))
+ LOG(PIL_INFO, "Cannot get %s plugin subplugins", pltype);
+ stonith_delete(ext);
+ return NULL;
+ }
+
+ /* count the external plugins */
+ for (numextPI = 0, p = extPI; *p; p++, numextPI++);
+
+ typelist = (char **)
+ MALLOC((numextPI+1)*sizeof(char *));
+ if (typelist == NULL) {
+ LOG(PIL_CRIT, "Out of memory");
+ stonith_delete(ext);
+ return NULL;
+ }
+
+ memset(typelist, 0, (numextPI + 1)*sizeof(char *));
+
+ /* copy external plugins */
+ for (i = 0; i < numextPI; i++) {
+ int len = strlen(pltype) +
+ strlen(extPI[i]) + 2;
+ typelist[i] = MALLOC(len);
+ if (typelist[i] == NULL) {
+ LOG(PIL_CRIT, "Out of memory");
+ stonith_delete(ext);
+ goto err;
+ }
+ snprintf(typelist[i], len, "%s/%s"
+ , pltype, extPI[i]);
+ }
+
+ stonith_delete(ext);
+
+ /* sort the list of plugin names */
+ qsort(typelist, numextPI, sizeof(char *), qsort_string_cmp);
+
+ return typelist;
+err:
+ stonith_free_hostlist(typelist);
+ return NULL;
+}
+
+char **
+stonith_types(void)
+{
+ int i, j, cur=0, rl_size, sub_pl = 0;
+ static char ** rl = NULL;
+ char ** new_list, **sub_list = NULL;
+
+ if (!init_pluginsys()) {
+ return NULL;
+ }
+
+ new_list = PILListPlugins(StonithPIsys, STONITH_TYPE_S, NULL);
+ if (new_list == NULL) {
+ return NULL;
+ }
+ for (i=0; new_list[i]; ++i)
+ ; /* count */
+ rl_size = i+1;
+
+ rl = (char**)MALLOC(rl_size * sizeof(char *));
+ if (rl == NULL) {
+ LOG(PIL_CRIT, "Out of memory");
+ goto types_exit;
+ }
+
+ for (i=0; new_list[i]; ++i) {
+ /* look for 'external' and 'rhcs' plugins */
+ if (strcmp(new_list[i], EXTPINAME_S) == 0) {
+ sub_list = get_plugin_list(EXTPINAME_S);
+ sub_pl = 1;
+ } else if (strcmp(new_list[i], RHCSPINAME_S) == 0) {
+ sub_list = get_plugin_list(RHCSPINAME_S);
+ sub_pl = 1;
+ }
+ if (sub_pl) {
+ if (sub_list) {
+ for (j=0; sub_list[j]; ++j)
+ ; /* count */
+ rl_size += j;
+ rl = (char**)REALLOC(rl, rl_size*sizeof(char *));
+ for (j=0; sub_list[j]; ++j) {
+ rl[cur++] = sub_list[j];
+ }
+ FREE(sub_list);
+ sub_list = NULL;
+ }
+ sub_pl = 0;
+ } else {
+ rl[cur] = STRDUP(new_list[i]);
+ if (rl[cur] == NULL) {
+ LOG(PIL_CRIT, "Out of memory");
+ goto types_exit_mem;
+ }
+ cur++;
+ }
+ }
+
+ rl[cur] = NULL;
+ goto types_exit;
+
+types_exit_mem:
+ stonith_free_hostlist(rl);
+ rl = NULL;
+types_exit:
+ PILFreePluginList(new_list);
+ return rl;
+}
+
+/* Destroy the STONITH object... */
+
+void
+stonith_delete(Stonith *s)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+
+ if (sp && sp->s_ops) {
+ char * st = sp->s.stype;
+ sp->s_ops->destroy(sp);
+ PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S, st, -1);
+ /* destroy should not free it */
+ FREE(st);
+ }
+}
+
+const char * const *
+stonith_get_confignames(Stonith* s)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+
+ if (sp && sp->s_ops) {
+ return sp->s_ops->get_confignames(sp);
+ }
+ return NULL;
+}
+
+const char*
+stonith_get_info(Stonith* s, int infotype)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+
+ if (sp && sp->s_ops) {
+ return sp->s_ops->get_info(sp, infotype);
+ }
+ return NULL;
+
+}
+
+void
+stonith_set_debug (Stonith* s, int debuglevel)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+ if (StonithPIsys == NULL) {
+ return;
+ }
+ PILSetDebugLevel(StonithPIsys, STONITH_TYPE_S, sp->s.stype, debuglevel);
+}
+
+void
+stonith_set_log(Stonith* s, PILLogFun logfun)
+{
+ if (StonithPIsys == NULL) {
+ return;
+ }
+ PilPluginUnivSetLog(StonithPIsys, logfun);
+}
+
+int
+stonith_set_config(Stonith* s, StonithNVpair* list)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+
+ if (sp && sp->s_ops) {
+ int rc = sp->s_ops->set_config(sp, list);
+ if (rc == S_OK) {
+ sp->isconfigured = TRUE;
+ }
+ return rc;
+ }
+ return S_INVAL;
+}
+
+/*
+ * FIXME: We really ought to support files with name=value type syntax
+ * on each line...
+ *
+ */
+int
+stonith_set_config_file(Stonith* s, const char * configname)
+{
+ FILE * cfgfile;
+
+ char line[1024];
+
+ if ((cfgfile = fopen(configname, "r")) == NULL) {
+ LOG(PIL_CRIT, "Cannot open %s", configname);
+ return(S_BADCONFIG);
+ }
+ while (fgets(line, sizeof(line), cfgfile) != NULL){
+ int len;
+
+ if (*line == '#' || *line == '\n' || *line == EOS) {
+ continue;
+ }
+
+ /*remove the new line in the end*/
+ len = strnlen(line, sizeof(line)-1);
+ if (line[len-1] == '\n'){
+ line[len-1] = '\0';
+ }else{
+ line[len] = '\0';
+ }
+
+ fclose(cfgfile);
+ return stonith_set_config_info(s, line);
+ }
+ fclose(cfgfile);
+ return S_BADCONFIG;
+}
+
+int
+stonith_set_config_info(Stonith* s, const char * info)
+{
+ StonithNVpair* cinfo;
+ int rc;
+ cinfo = stonith1_compat_string_to_NVpair(s, info);
+ if (cinfo == NULL) {
+ return S_BADCONFIG;
+ }
+ rc = stonith_set_config(s, cinfo);
+ free_NVpair(cinfo); cinfo = NULL;
+ return rc;
+}
+
+char**
+stonith_get_hostlist(Stonith* s)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+ if (sp && sp->s_ops && sp->isconfigured) {
+ return sp->s_ops->get_hostlist(sp);
+ }
+ return NULL;
+}
+
+void
+stonith_free_hostlist(char** hostlist)
+{
+ char ** here;
+
+ for (here=hostlist; *here; ++here) {
+ FREE(*here);
+ }
+ FREE(hostlist);
+}
+
+int
+stonith_get_status(Stonith* s)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+ if (sp && sp->s_ops && sp->isconfigured) {
+ return sp->s_ops->get_status(sp);
+ }
+ return S_INVAL;
+}
+
+void
+strdown(char *str)
+{
+ while( *str ) {
+ if( isupper(*str) )
+ *str = tolower(*str);
+ str++;
+ }
+}
+
+int
+stonith_req_reset(Stonith* s, int operation, const char* node)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+ if (sp && sp->s_ops && sp->isconfigured) {
+ char* nodecopy = STRDUP(node);
+ int rc;
+ if (nodecopy == NULL) {
+ return S_OOPS;
+ }
+ strdown(nodecopy);
+
+ rc = sp->s_ops->req_reset(sp, operation, nodecopy);
+ FREE(nodecopy);
+ return rc;
+ }
+ return S_INVAL;
+}
+/* Stonith 1 compatibility: Convert a string to an NVpair set */
+StonithNVpair*
+stonith1_compat_string_to_NVpair(Stonith* s, const char * str)
+{
+ /* We make some assumptions that the order of parameters in the
+ * result from stonith_get_confignames() matches that which
+ * was required from a Stonith1 module.
+ * Everything after the last delimiter is passed along as part of
+ * the final argument - white space and all...
+ */
+ const char * const * config_names;
+ int n_names;
+ int j;
+ const char * delims = " \t\n\r\f";
+ StonithNVpair* ret;
+
+ if ((config_names = stonith_get_confignames(s)) == NULL) {
+ return NULL;
+ }
+ for (n_names=0; config_names[n_names] != NULL; ++n_names) {
+ /* Just count */;
+ }
+ ret = (StonithNVpair*) (MALLOC((n_names+1)*sizeof(StonithNVpair)));
+ if (ret == NULL) {
+ return NULL;
+ }
+ memset(ret, 0, (n_names+1)*sizeof(StonithNVpair));
+ for (j=0; j < n_names; ++j) {
+ size_t len;
+ if ((ret[j].s_name = STRDUP(config_names[j])) == NULL) {
+ goto freeandexit;
+ }
+ ret[j].s_value = NULL;
+ str += strspn(str, delims);
+ if (*str == EOS) {
+ goto freeandexit;
+ }
+ if (j == (n_names -1)) {
+ len = strlen(str);
+ }else{
+ len = strcspn(str, delims);
+ }
+ if ((ret[j].s_value = MALLOC((len+1)*sizeof(char))) == NULL) {
+ goto freeandexit;
+ }
+ memcpy(ret[j].s_value, str, len);
+ ret[j].s_value[len] = EOS;
+ str += len;
+ }
+ ret[j].s_name = NULL;
+ return ret;
+freeandexit:
+ free_NVpair(ret); ret = NULL;
+ return NULL;
+}
+
+StonithNVpair*
+stonith_env_to_NVpair(Stonith* s)
+{
+ /* Read the config names values from the environment */
+ const char * const * config_names;
+ int n_names;
+ int j;
+ StonithNVpair* ret;
+
+ if ((config_names = stonith_get_confignames(s)) == NULL) {
+ return NULL;
+ }
+ for (n_names=0; config_names[n_names] != NULL; ++n_names) {
+ /* Just count */;
+ }
+ ret = (StonithNVpair*) (MALLOC((n_names+1)*sizeof(StonithNVpair)));
+ if (ret == NULL) {
+ return NULL;
+ }
+ memset(ret, 0, (n_names+1)*sizeof(StonithNVpair));
+ for (j=0; j < n_names; ++j) {
+ char *env_value;
+ if ((ret[j].s_name = STRDUP(config_names[j])) == NULL) {
+ goto freeandexit;
+ }
+ env_value = getenv(config_names[j]);
+ if (env_value) {
+ if ((ret[j].s_value = STRDUP(env_value)) == NULL) {
+ goto freeandexit;
+ }
+ } else {
+ ret[j].s_value = NULL;
+ }
+ }
+ ret[j].s_name = NULL;
+ return ret;
+freeandexit:
+ free_NVpair(ret); ret = NULL;
+ return NULL;
+}
+
+static int NVcur = -1;
+static int NVmax = -1;
+static gboolean NVerr = FALSE;
+
+static void
+stonith_walk_ghash(gpointer key, gpointer value, gpointer user_data)
+{
+ StonithNVpair* u = user_data;
+
+ if (NVcur <= NVmax && !NVerr) {
+ u[NVcur].s_name = STRDUP(key);
+ u[NVcur].s_value = STRDUP(value);
+ if (u[NVcur].s_name == NULL || u[NVcur].s_value == NULL) {
+ /* Memory allocation error */
+ NVerr = TRUE;
+ return;
+ }
+ ++NVcur;
+ }else{
+ NVerr = TRUE;
+ }
+}
+
+
+StonithNVpair*
+stonith_ghash_to_NVpair(GHashTable* stringtable)
+{
+ int hsize = g_hash_table_size(stringtable);
+ StonithNVpair* ret;
+
+ if ((ret = (StonithNVpair*)MALLOC(sizeof(StonithNVpair)*(hsize+1))) == NULL) {
+ return NULL;
+ }
+ NVmax = hsize;
+ NVcur = 0;
+ ret[hsize].s_name = NULL;
+ ret[hsize].s_value = NULL;
+ g_hash_table_foreach(stringtable, stonith_walk_ghash, ret);
+ NVmax = NVcur = -1;
+ if (NVerr) {
+ free_NVpair(ret);
+ ret = NULL;
+ }
+ return ret;
+}
+
+void
+free_NVpair(StonithNVpair* nv)
+{
+ StonithNVpair* this;
+
+ if (nv == NULL) {
+ return;
+ }
+ for (this=nv; this->s_name; ++this) {
+ FREE(this->s_name);
+ if (this->s_value) {
+ FREE(this->s_value);
+ }
+ }
+ FREE(nv);
+}