diff options
Diffstat (limited to '')
-rw-r--r-- | lib/plugins/stonith/rcd_serial.c | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/lib/plugins/stonith/rcd_serial.c b/lib/plugins/stonith/rcd_serial.c new file mode 100644 index 0000000..f1396a7 --- /dev/null +++ b/lib/plugins/stonith/rcd_serial.c @@ -0,0 +1,602 @@ +/* + * Stonith module for RCD_SERIAL Stonith device + * + * Original code from null.c by + * Copyright (c) 2000 Alan Robertson <alanr@unix.sh> + * + * Copious borrowings from nw_rpc100s.c by + * Copyright (c) 2000 Computer Generation Incorporated + * Eric Z. Ayers <eric.ayers@compgen.com> + * + * and from apcsmart.c by + * Copyright (c) 2000 Andreas Piesk <a.piesk@gmx.net> + * + * Modifications for RC Delayed Serial Ciruit by + * Copyright (c) 2002 John Sutton <john@scl.co.uk> + * + * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <lha_internal.h> + +#define DEVICE "RC Delayed Serial" +#include "stonith_plugin_common.h" +#include "stonith_signal.h" + +#define PIL_PLUGIN rcd_serial +#define PIL_PLUGIN_S "rcd_serial" +#define PIL_PLUGINLICENSE LICENSE_LGPL +#define PIL_PLUGINLICENSEURL URL_LGPL + +#define ST_DTRRTS "dtr_rts" +#define ST_MSDURATION "msduration" +#define MAX_RCD_SERIALLINE 512 + +#include <pils/plugin.h> +#include <sys/ioctl.h> +#include <sys/time.h> + +static StonithPlugin* rcd_serial_new(const char *); +static void rcd_serial_destroy(StonithPlugin *); +static int rcd_serial_set_config(StonithPlugin *, StonithNVpair *); +static const char * const * rcd_serial_get_confignames(StonithPlugin *); +static const char * rcd_serial_getinfo(StonithPlugin * s, int InfoType); +static int rcd_serial_status(StonithPlugin * ); +static int rcd_serial_reset_req(StonithPlugin * s, int request, const char * host); +static char ** rcd_serial_hostlist(StonithPlugin *); + +static struct stonith_ops rcd_serialOps ={ + rcd_serial_new, /* Create new STONITH object */ + rcd_serial_destroy, /* Destroy STONITH object */ + rcd_serial_getinfo, /* Return STONITH info string */ + rcd_serial_get_confignames,/* Return STONITH info string */ + rcd_serial_set_config, /* Get configuration from NVpairs */ + rcd_serial_status, /* Return STONITH device status */ + rcd_serial_reset_req, /* Request a reset */ + rcd_serial_hostlist, /* Return list of supported hosts */ +}; + +PIL_PLUGIN_BOILERPLATE2("1.0", Debug) +static const PILPluginImports* PluginImports; +static PILPlugin* OurPlugin; +static PILInterface* OurInterface; +static StonithImports* OurImports; +static void* interfprivate; + +PIL_rc +PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); + +PIL_rc +PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports) +{ + /* Force the compiler to do a little type checking */ + (void)(PILPluginInitFun)PIL_PLUGIN_INIT; + + PluginImports = imports; + OurPlugin = us; + + /* Register ourself as a plugin */ + imports->register_plugin(us, &OurPIExports); + + /* Register our interface implementation */ + return imports->register_interface(us, PIL_PLUGINTYPE_S + , PIL_PLUGIN_S + , &rcd_serialOps + , NULL /*close */ + , &OurInterface + , (void*)&OurImports + , &interfprivate); +} + +/* ------------------- RCD specific stuff -------------- */ + +/* + A diagram of a circuit suitable for use with this plugin is in + README.rcd_serial which should be somewhere in the distribution (if Alan + includes it ;-) and/or at http://www.scl.co.uk/rcd_serial/ (if I remember + to put it there ;-). + + Once you've got this built, you can test things using the stonith command + as follows: + + stonith -L + will show a list of plugin types, including rcd_serial + + stonith -t rcd_serial testhost + will show required parameters + + In these 3 you can either pass the params after the -p option or you can + put them in a config file and use -F configname instead of -p "param ...". + + stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" -S + will show the status of the device + + stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" -l + will list the single host testhost + + stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" testhost + will reset testhost (provided testhost has its reset pins + suitably wired to the RTS signal coming out of port /dev/ttyS0 + and that 1.5s is enough time to cause a reset ;-) +*/ + +/* + Define RCD_NOPAUSE if you are using the serial port for some purpose + _in_addition_ to using it as a stonith device. For example, I use one + of the input pins on the same serial port for monitoring the state of a + power supply. Periodically, a cron job has to open the port to read the + state of this input and thus has to clear down the output pins DTR and RTS + in order to avoid causing a spurious stonith reset. Now, if it should + happen that just at the same time as we are _really_ trying to do a stonith + reset, this cron job starts up, then the stonith reset won't occur ;-(. + To avoid this (albeit unlikely) outcome, you should #define RCD_NOPAUSE. + The effect of this is that instead of setting the line high just once and + then falling into a pause until an alarm goes off, rather, the program falls + into a loop which is continuously setting the line high. That costs us a bit + of CPU as compared with sitting in a pause, but hey, how often is this code + going to get exercised! Never, we hope... +*/ +#undef RCD_NOPAUSE + +#ifdef RCD_NOPAUSE +static int RCD_alarmcaught; +#endif + +/* + * own prototypes + */ + +static void RCD_alarm_handler(int sig); +static int RCD_open_serial_port(char *device); +static int RCD_close_serial_port(char *device, int fd); + +static void +RCD_alarm_handler(int sig) { +#if !defined(HAVE_POSIX_SIGNALS) + if (sig) { + signal(sig, SIG_DFL); + }else{ + signal(sig, RCD_alarm_handler); + } +#else + struct sigaction sa; + sigset_t sigmask; + + /* Maybe a bit naughty but it works and it saves duplicating all */ + /* this setup code - if handler called with 0 for sig, we install */ + /* ourself as handler. */ + if (sig) { + sa.sa_handler = (void (*)(int))SIG_DFL; + }else{ + sa.sa_handler = RCD_alarm_handler; + } + + sigemptyset(&sigmask); + sa.sa_mask = sigmask; + sa.sa_flags = 0; + sigaction(SIGALRM, &sa, NULL); +#endif + +#ifdef RCD_NOPAUSE + RCD_alarmcaught = 1; +#endif + return; +} + +static int +RCD_open_serial_port(char *device) { + int fd; + int status; + int bothbits; + + if (OurImports->TtyLock(device) < 0) { + if (Debug) { + LOG(PIL_DEBUG, "%s: ttylock failed.", __FUNCTION__); + } + return -1; + } + + bothbits = TIOCM_RTS | TIOCM_DTR; + + if ((fd = open(device, O_RDONLY | O_NDELAY)) != -1) { + /* + Opening the device always sets DTR & CTS high. + Clear them down immediately. + */ + status = ioctl(fd, TIOCMBIC, &bothbits); + /* If there was an error clearing bits, set the fd to -1 + * ( indicates error ) */ + if (status != 0 ) { + fd = -1; + } + } + + return fd; +} + +static int +RCD_close_serial_port(char *device, int fd) { + int rc = close(fd); + if (device != NULL) { + OurImports->TtyUnlock(device); + } + return rc; +} + +/* + * RCD_Serial STONITH device. + */ +struct pluginDevice { + StonithPlugin sp; + const char * pluginid; + const char * idinfo; + char ** hostlist; /* name of single host we can reset */ + int hostcount; /* i.e. 1 after initialisation */ + char * device; /* serial device name */ + char * signal; /* either rts or dtr */ + long msduration; /* how long (ms) to assert the signal */ +}; + +static const char * pluginid = "RCD_SerialDevice-Stonith"; +static const char * NOTrcd_serialID = "RCD_Serial device has been destroyed"; + +#include "stonith_config_xml.h" + +#define XML_DTRRTS_SHORTDESC \ + XML_PARM_SHORTDESC_BEGIN("en") \ + ST_DTRRTS \ + XML_PARM_SHORTDESC_END + +#define XML_DTRRTS_LONGDESC \ + XML_PARM_LONGDESC_BEGIN("en") \ + "The hardware handshaking technique to use with " ST_TTYDEV "(\"dtr\" or \"rts\")" \ + XML_PARM_LONGDESC_END + +#define XML_DTRRTS_PARM \ + XML_PARAMETER_BEGIN(ST_DTRRTS, "string", "1", "0") \ + XML_DTRRTS_SHORTDESC \ + XML_DTRRTS_LONGDESC \ + XML_PARAMETER_END + +#define XML_MSDURATION_SHORTDESC \ + XML_PARM_SHORTDESC_BEGIN("en") \ + ST_MSDURATION \ + XML_PARM_SHORTDESC_END + +#define XML_MSDURATION_LONGDESC \ + XML_PARM_LONGDESC_BEGIN("en") \ + "The delay duration (in milliseconds) between the assertion of the control signal on " ST_TTYDEV " and the closing of the reset switch" \ + XML_PARM_LONGDESC_END + +#define XML_MSDURATION_PARM \ + XML_PARAMETER_BEGIN(ST_MSDURATION, "string", "1", "0") \ + XML_MSDURATION_SHORTDESC \ + XML_MSDURATION_LONGDESC \ + XML_PARAMETER_END + +static const char *rcd_serialXML = + XML_PARAMETERS_BEGIN + XML_HOSTLIST_PARM + XML_TTYDEV_PARM + XML_DTRRTS_PARM + XML_MSDURATION_PARM + XML_PARAMETERS_END; + +static int +rcd_serial_status(StonithPlugin *s) +{ + struct pluginDevice* rcd; + int fd; + const char * err; + + ERRIFWRONGDEV(s,S_OOPS); + + rcd = (struct pluginDevice*) s; + + /* + All we can do is make sure the serial device exists and + can be opened and closed without error. + */ + + if ((fd = RCD_open_serial_port(rcd->device)) == -1) { + err = strerror(errno); + LOG(PIL_CRIT, "%s: open of %s failed - %s", + __FUNCTION__, rcd->device, err); + return(S_OOPS); + } + + if (RCD_close_serial_port(rcd->device, fd) != 0) { + err = strerror(errno); + LOG(PIL_CRIT, "%s: close of %s failed - %s", + __FUNCTION__, rcd->device, err); + return(S_OOPS); + } + + return S_OK; +} + + +/* + * Return the list of hosts configured for this RCD_SERIAL device + */ +static char ** +rcd_serial_hostlist(StonithPlugin *s) +{ + struct pluginDevice* rcd; + + ERRIFWRONGDEV(s,NULL); + rcd = (struct pluginDevice*) s; + if (rcd->hostcount < 0) { + LOG(PIL_CRIT + , "unconfigured stonith object in RCD_SERIAL_list_hosts"); + return(NULL); + } + + return OurImports->CopyHostList((const char * const *)rcd->hostlist); +} + +/* + * At last, we really do it! I don't know what the request argument + * is so am just ignoring it... + */ +static int +rcd_serial_reset_req(StonithPlugin * s, int request, const char * host) +{ + struct pluginDevice* rcd; + int fd; + int sigbit; + struct itimerval timer; + const char * err; + + ERRIFWRONGDEV(s,S_OOPS); + + rcd = (struct pluginDevice *) s; + + /* check that host matches */ + if (strcasecmp(host, rcd->hostlist[0])) { + LOG(PIL_CRIT, "%s: host '%s' not in hostlist.", + __FUNCTION__, host); + return(S_BADHOST); + } + + /* Set the appropriate bit for the signal */ + sigbit = *(rcd->signal)=='r' ? TIOCM_RTS : TIOCM_DTR; + + /* Set up the timer */ + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = 0; + timer.it_value.tv_sec = rcd->msduration / 1000; + timer.it_value.tv_usec = (rcd->msduration % 1000) * 1000; + + /* Open the device */ + if ((fd = RCD_open_serial_port(rcd->device)) == -1) { +#ifdef HAVE_STRERROR + err = strerror(errno); +#else + err = sys_errlist[errno]; +#endif + LOG(PIL_CRIT, "%s: open of %s failed - %s", + __FUNCTION__, rcd->device, err); + return(S_OOPS); + } + + /* Start the timer */ + RCD_alarm_handler(0); +#ifdef RCD_NOPAUSE + RCD_alarmcaught = 0; +#endif + setitimer(ITIMER_REAL, &timer, 0); + + /* Set the line high */ + ioctl(fd, TIOCMBIS, &sigbit); + + /* Wait for the alarm signal */ +#ifdef RCD_NOPAUSE + while(!RCD_alarmcaught) ioctl(fd, TIOCMBIS, &sigbit); +#else + pause(); +#endif + + /* Clear the line low */ + ioctl(fd, TIOCMBIC, &sigbit); + + /* Close the port */ + if (RCD_close_serial_port(rcd->device, fd) != 0) { + err = strerror(errno); + LOG(PIL_CRIT, "%s: close of %s failed - %s", + __FUNCTION__, rcd->device, err); + return(S_OOPS); + } + + LOG(PIL_INFO,"Host rcd_serial-reset: %s", host); + return S_OK; +} + +/* + * Parse the information in the given string + * and stash it away... + */ +static int +rcd_serial_set_config(StonithPlugin* s, StonithNVpair *list) +{ + struct pluginDevice* rcd; + StonithNamesToGet namestocopy [] = + { {ST_HOSTLIST, NULL} + , {ST_TTYDEV, NULL} + , {ST_DTRRTS, NULL} + , {ST_MSDURATION, NULL} + , {NULL, NULL} + }; + char *endptr; + int rc = 0; + + LOG(PIL_DEBUG, "%s:called", __FUNCTION__); + + ERRIFWRONGDEV(s,S_OOPS); + if (s->isconfigured) { + return S_OOPS; + } + + rcd = (struct pluginDevice*) s; + + if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) { + return rc; + } + + if ((rcd->hostlist = (char **)MALLOC(2*sizeof(char*))) == NULL) { + LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__); + FREE(namestocopy[0].s_value); + FREE(namestocopy[1].s_value); + FREE(namestocopy[2].s_value); + FREE(namestocopy[3].s_value); + return S_OOPS; + } + rcd->hostlist[0] = namestocopy[0].s_value; + strdown(rcd->hostlist[0]); + rcd->hostlist[1] = NULL; + rcd->hostcount = 1; + rcd->device = namestocopy[1].s_value; + rcd->signal = namestocopy[2].s_value; + if (strcmp(rcd->signal, "rts") && strcmp(rcd->signal, "dtr")) { + LOG(PIL_CRIT, "%s: Invalid signal name '%s'", + pluginid, rcd->signal); + FREE(namestocopy[3].s_value); + return S_BADCONFIG; + } + + errno = 0; + rcd->msduration = strtol(namestocopy[3].s_value, &endptr, 0); + if (((errno == ERANGE) + && (rcd->msduration == LONG_MIN || rcd->msduration == LONG_MAX)) + || *endptr != 0 || rcd->msduration < 1) { + LOG(PIL_CRIT, "%s: Invalid msduration '%s'", + pluginid, namestocopy[3].s_value); + FREE(namestocopy[3].s_value); + return S_BADCONFIG; + } + FREE(namestocopy[3].s_value); + + return S_OK; +} + +/* + * Return STONITH config vars + */ +static const char * const * +rcd_serial_get_confignames(StonithPlugin* p) +{ + static const char * RcdParams[] = {ST_HOSTLIST, ST_TTYDEV + , ST_DTRRTS, ST_MSDURATION, NULL }; + return RcdParams; +} + +/* + * Return STONITH info string + */ +static const char * +rcd_serial_getinfo(StonithPlugin * s, int reqtype) +{ + struct pluginDevice* rcd; + const char * ret; + + ERRIFWRONGDEV(s,NULL); + /* + * We look in the ST_TEXTDOMAIN catalog for our messages + */ + rcd = (struct pluginDevice *)s; + + switch (reqtype) { + case ST_DEVICEID: + ret = rcd->idinfo; + break; + case ST_DEVICENAME: + ret = rcd->device; + break; + case ST_DEVICEDESCR: + ret = "RC Delayed Serial STONITH Device\n" + "This device can be constructed cheaply from" + " readily available components,\n" + "with sufficient expertise and testing.\n" + "See README.rcd_serial for circuit diagram.\n"; + break; + case ST_DEVICEURL: + ret = "http://www.scl.co.uk/rcd_serial/"; + break; + case ST_CONF_XML: /* XML metadata */ + ret = rcd_serialXML; + break; + default: + ret = NULL; + break; + } + return ret; +} + +/* + * RCD_SERIAL Stonith destructor... + */ +static void +rcd_serial_destroy(StonithPlugin *s) +{ + struct pluginDevice* rcd; + + VOIDERRIFWRONGDEV(s); + + rcd = (struct pluginDevice *)s; + + rcd->pluginid = NOTrcd_serialID; + if (rcd->hostlist) { + stonith_free_hostlist(rcd->hostlist); + rcd->hostlist = NULL; + } + rcd->hostcount = -1; + if (rcd->device) { + FREE(rcd->device); + } + if (rcd->signal) { + FREE(rcd->signal); + } + FREE(rcd); +} + +/* + * Create a new RCD_Serial Stonith device. + * Too bad this function can't be static. (Hmm, weird, it _is_ static?) + */ +static StonithPlugin * +rcd_serial_new(const char *subplugin) +{ + struct pluginDevice* rcd = ST_MALLOCT(struct pluginDevice); + + if (rcd == NULL) { + LOG(PIL_CRIT, "out of memory"); + return(NULL); + } + memset(rcd, 0, sizeof(*rcd)); + + rcd->pluginid = pluginid; + rcd->hostlist = NULL; + rcd->hostcount = -1; + rcd->device = NULL; + rcd->signal = NULL; + rcd->msduration = 0; + rcd->idinfo = DEVICE; + rcd->sp.s_ops = &rcd_serialOps; + + return &(rcd->sp); +} |