diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 06:40:13 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 06:40:13 +0000 |
commit | e9be59e1502a41bab9891d96d753102a7dafef0b (patch) | |
tree | c3b2da87c414881f4b53d0964f407c83492d813e /lib/stonith | |
parent | Initial commit. (diff) | |
download | cluster-glue-e9be59e1502a41bab9891d96d753102a7dafef0b.tar.xz cluster-glue-e9be59e1502a41bab9891d96d753102a7dafef0b.zip |
Adding upstream version 1.0.12.upstream/1.0.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | lib/stonith/Makefile.am | 54 | ||||
-rw-r--r-- | lib/stonith/README | 31 | ||||
-rw-r--r-- | lib/stonith/expect.c | 539 | ||||
-rwxr-xr-x | lib/stonith/ha_log.sh | 114 | ||||
-rw-r--r-- | lib/stonith/main.c | 727 | ||||
-rw-r--r-- | lib/stonith/meatclient.c | 152 | ||||
-rw-r--r-- | lib/stonith/st_ttylock.c | 225 | ||||
-rw-r--r-- | lib/stonith/stonith.c | 636 |
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); +} |