2
* Stonith module for RCD_SERIAL Stonith device
4
* Original code from null.c by
5
* Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
7
* Copious borrowings from nw_rpc100s.c by
8
* Copyright (c) 2000 Computer Generation Incorporated
9
* Eric Z. Ayers <eric.ayers@compgen.com>
11
* and from apcsmart.c by
12
* Copyright (c) 2000 Andreas Piesk <a.piesk@gmx.net>
14
* Modifications for RC Delayed Serial Ciruit by
15
* Copyright (c) 2002 John Sutton <john@scl.co.uk>
17
* Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
19
* This library is free software; you can redistribute it and/or
20
* modify it under the terms of the GNU Lesser General Public
21
* License as published by the Free Software Foundation; either
22
* version 2.1 of the License, or (at your option) any later version.
24
* This library is distributed in the hope that it will be useful,
25
* but WITHOUT ANY WARRANTY; without even the implied warranty of
26
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27
* Lesser General Public License for more details.
29
* You should have received a copy of the GNU Lesser General Public
30
* License along with this library; if not, write to the Free Software
31
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35
#include <lha_internal.h>
37
#define DEVICE "RC Delayed Serial"
38
#include "stonith_plugin_common.h"
39
#include "stonith_signal.h"
41
#define PIL_PLUGIN rcd_serial
42
#define PIL_PLUGIN_S "rcd_serial"
43
#define PIL_PLUGINLICENSE LICENSE_LGPL
44
#define PIL_PLUGINLICENSEURL URL_LGPL
46
#define ST_DTRRTS "dtr|rts"
47
#define ST_MSDURATION "msduration"
48
#define MAX_RCD_SERIALLINE 512
50
#include <pils/plugin.h>
51
#include <sys/ioctl.h>
54
static StonithPlugin* rcd_serial_new(const char *);
55
static void rcd_serial_destroy(StonithPlugin *);
56
static int rcd_serial_set_config(StonithPlugin *, StonithNVpair *);
57
static const char ** rcd_serial_get_confignames(StonithPlugin *);
58
static const char * rcd_serial_getinfo(StonithPlugin * s, int InfoType);
59
static int rcd_serial_status(StonithPlugin * );
60
static int rcd_serial_reset_req(StonithPlugin * s, int request, const char * host);
61
static char ** rcd_serial_hostlist(StonithPlugin *);
63
static struct stonith_ops rcd_serialOps ={
64
rcd_serial_new, /* Create new STONITH object */
65
rcd_serial_destroy, /* Destroy STONITH object */
66
rcd_serial_getinfo, /* Return STONITH info string */
67
rcd_serial_get_confignames,/* Return STONITH info string */
68
rcd_serial_set_config, /* Get configuration from NVpairs */
69
rcd_serial_status, /* Return STONITH device status */
70
rcd_serial_reset_req, /* Request a reset */
71
rcd_serial_hostlist, /* Return list of supported hosts */
74
PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
75
static const PILPluginImports* PluginImports;
76
static PILPlugin* OurPlugin;
77
static PILInterface* OurInterface;
78
static StonithImports* OurImports;
79
static void* interfprivate;
82
PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
85
PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
87
/* Force the compiler to do a little type checking */
88
(void)(PILPluginInitFun)PIL_PLUGIN_INIT;
90
PluginImports = imports;
93
/* Register ourself as a plugin */
94
imports->register_plugin(us, &OurPIExports);
96
/* Register our interface implementation */
97
return imports->register_interface(us, PIL_PLUGINTYPE_S
106
/* ------------------- RCD specific stuff -------------- */
109
A diagram of a circuit suitable for use with this plugin is in
110
README.rcd_serial which should be somewhere in the distribution (if Alan
111
includes it ;-) and/or at http://www.scl.co.uk/rcd_serial/ (if I remember
114
Once you've got this built, you can test things using the stonith command
118
will show a list of plugin types, including rcd_serial
120
stonith -t rcd_serial testhost
121
will show required parameters
123
In these 3 you can either pass the params after the -p option or you can
124
put them in a config file and use -F configname instead of -p "param ...".
126
stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" -S
127
will show the status of the device
129
stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" -l
130
will list the single host testhost
132
stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" testhost
133
will reset testhost (provided testhost has its reset pins
134
suitably wired to the RTS signal coming out of port /dev/ttyS0
135
and that 1.5s is enough time to cause a reset ;-)
139
Define RCD_NOPAUSE if you are using the serial port for some purpose
140
_in_addition_ to using it as a stonith device. For example, I use one
141
of the input pins on the same serial port for monitoring the state of a
142
power supply. Periodically, a cron job has to open the port to read the
143
state of this input and thus has to clear down the output pins DTR and RTS
144
in order to avoid causing a spurious stonith reset. Now, if it should
145
happen that just at the same time as we are _really_ trying to do a stonith
146
reset, this cron job starts up, then the stonith reset won't occur ;-(.
147
To avoid this (albeit unlikely) outcome, you should #define RCD_NOPAUSE.
148
The effect of this is that instead of setting the line high just once and
149
then falling into a pause until an alarm goes off, rather, the program falls
150
into a loop which is continuously setting the line high. That costs us a bit
151
of CPU as compared with sitting in a pause, but hey, how often is this code
152
going to get exercised! Never, we hope...
157
static int RCD_alarmcaught;
164
static void RCD_alarm_handler(int sig);
165
static int RCD_open_serial_port(char *device);
166
static int RCD_close_serial_port(char *device, int fd);
169
RCD_alarm_handler(int sig) {
170
#if !defined(HAVE_POSIX_SIGNALS)
172
signal(sig, SIG_DFL);
174
signal(sig, RCD_alarm_handler);
180
/* Maybe a bit naughty but it works and it saves duplicating all */
181
/* this setup code - if handler called with 0 for sig, we install */
182
/* ourself as handler. */
184
sa.sa_handler = (void (*)(int))SIG_DFL;
186
sa.sa_handler = RCD_alarm_handler;
189
sigemptyset(&sigmask);
190
sa.sa_mask = sigmask;
192
sigaction(SIGALRM, &sa, NULL);
202
RCD_open_serial_port(char *device) {
207
if (OurImports->TtyLock(device) < 0) {
209
LOG(PIL_DEBUG, "%s: ttylock failed.", __FUNCTION__);
214
bothbits = TIOCM_RTS | TIOCM_DTR;
216
if ((fd = open(device, O_RDONLY | O_NDELAY)) != -1) {
218
Opening the device always sets DTR & CTS high.
219
Clear them down immediately.
221
status = ioctl(fd, TIOCMBIC, &bothbits);
222
/* If there was an error clearing bits, set the fd to -1
223
* ( indicates error ) */
233
RCD_close_serial_port(char *device, int fd) {
235
if (device != NULL) {
236
OurImports->TtyUnlock(device);
242
* RCD_Serial STONITH device.
244
struct pluginDevice {
246
const char * pluginid;
248
char ** hostlist; /* name of single host we can reset */
249
int hostcount; /* i.e. 1 after initialisation */
250
char * device; /* serial device name */
251
char * signal; /* either rts or dtr */
252
long msduration; /* how long (ms) to assert the signal */
255
static const char * pluginid = "RCD_SerialDevice-Stonith";
256
static const char * NOTrcd_serialID = "RCD_Serial device has been destroyed";
258
#include "stonith_config_xml.h"
260
#define XML_DTRRTS_SHORTDESC \
261
XML_PARM_SHORTDESC_BEGIN("en") \
263
XML_PARM_SHORTDESC_END
265
#define XML_DTRRTS_LONGDESC \
266
XML_PARM_LONGDESC_BEGIN("en") \
267
"The hardware handshaking technique to use with " ST_TTYDEV "(\"dtr\" or \"rts\")" \
268
XML_PARM_LONGDESC_END
270
#define XML_DTRRTS_PARM \
271
XML_PARAMETER_BEGIN(ST_DTRRTS, "string", "1") \
272
XML_DTRRTS_SHORTDESC \
273
XML_DTRRTS_LONGDESC \
276
#define XML_MSDURATION_SHORTDESC \
277
XML_PARM_SHORTDESC_BEGIN("en") \
279
XML_PARM_SHORTDESC_END
281
#define XML_MSDURATION_LONGDESC \
282
XML_PARM_LONGDESC_BEGIN("en") \
283
"The delay duration (in milliseconds) between the assertion of the control signal on " ST_TTYDEV " and the closing of the reset switch" \
284
XML_PARM_LONGDESC_END
286
#define XML_MSDURATION_PARM \
287
XML_PARAMETER_BEGIN(ST_MSDURATION, "string", "1") \
288
XML_MSDURATION_SHORTDESC \
289
XML_MSDURATION_LONGDESC \
292
static const char *rcd_serialXML =
301
rcd_serial_status(StonithPlugin *s)
303
struct pluginDevice* rcd;
307
ERRIFWRONGDEV(s,S_OOPS);
309
rcd = (struct pluginDevice*) s;
312
All we can do is make sure the serial device exists and
313
can be opened and closed without error.
316
if ((fd = RCD_open_serial_port(rcd->device)) == -1) {
317
err = strerror(errno);
318
LOG(PIL_CRIT, "%s: open of %s failed - %s",
319
__FUNCTION__, rcd->device, err);
323
if (RCD_close_serial_port(rcd->device, fd) != 0) {
324
err = strerror(errno);
325
LOG(PIL_CRIT, "%s: close of %s failed - %s",
326
__FUNCTION__, rcd->device, err);
335
* Return the list of hosts configured for this RCD_SERIAL device
338
rcd_serial_hostlist(StonithPlugin *s)
340
struct pluginDevice* rcd;
342
ERRIFWRONGDEV(s,NULL);
343
rcd = (struct pluginDevice*) s;
344
if (rcd->hostcount < 0) {
346
, "unconfigured stonith object in RCD_SERIAL_list_hosts");
350
return OurImports->CopyHostList((const char **)rcd->hostlist);
354
* At last, we really do it! I don't know what the request argument
355
* is so am just ignoring it...
358
rcd_serial_reset_req(StonithPlugin * s, int request, const char * host)
360
struct pluginDevice* rcd;
363
struct itimerval timer;
366
ERRIFWRONGDEV(s,S_OOPS);
368
rcd = (struct pluginDevice *) s;
370
/* check that host matches */
371
if (strcasecmp(host, rcd->hostlist[0])) {
372
LOG(PIL_CRIT, "%s: host '%s' not in hostlist.",
377
/* Set the appropriate bit for the signal */
378
sigbit = *(rcd->signal)=='r' ? TIOCM_RTS : TIOCM_DTR;
380
/* Set up the timer */
381
timer.it_interval.tv_sec = 0;
382
timer.it_interval.tv_usec = 0;
383
timer.it_value.tv_sec = rcd->msduration / 1000;
384
timer.it_value.tv_usec = (rcd->msduration % 1000) * 1000;
386
/* Open the device */
387
if ((fd = RCD_open_serial_port(rcd->device)) == -1) {
389
err = strerror(errno);
391
err = sys_errlist[errno];
393
LOG(PIL_CRIT, "%s: open of %s failed - %s",
394
__FUNCTION__, rcd->device, err);
398
/* Start the timer */
399
RCD_alarm_handler(0);
403
setitimer(ITIMER_REAL, &timer, 0);
405
/* Set the line high */
406
ioctl(fd, TIOCMBIS, &sigbit);
408
/* Wait for the alarm signal */
410
while(!RCD_alarmcaught) ioctl(fd, TIOCMBIS, &sigbit);
415
/* Clear the line low */
416
ioctl(fd, TIOCMBIC, &sigbit);
419
if (RCD_close_serial_port(rcd->device, fd) != 0) {
420
err = strerror(errno);
421
LOG(PIL_CRIT, "%s: close of %s failed - %s",
422
__FUNCTION__, rcd->device, err);
426
LOG(PIL_INFO,"Host rcd_serial-reset: %s", host);
431
* Parse the information in the given string
432
* and stash it away...
435
rcd_serial_set_config(StonithPlugin* s, StonithNVpair *list)
437
struct pluginDevice* rcd;
438
StonithNamesToGet namestocopy [] =
439
{ {ST_HOSTLIST, NULL}
442
, {ST_MSDURATION, NULL}
448
LOG(PIL_DEBUG, "%s:called", __FUNCTION__);
450
ERRIFWRONGDEV(s,S_OOPS);
451
if (s->isconfigured) {
455
rcd = (struct pluginDevice*) s;
457
if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
461
if ((rcd->hostlist = (char **)MALLOC(2*sizeof(char*))) == NULL) {
462
LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__);
463
FREE(namestocopy[0].s_value);
464
FREE(namestocopy[1].s_value);
465
FREE(namestocopy[2].s_value);
466
FREE(namestocopy[3].s_value);
469
rcd->hostlist[0] = namestocopy[0].s_value;
470
g_strdown(rcd->hostlist[0]);
471
rcd->hostlist[1] = NULL;
473
rcd->device = namestocopy[1].s_value;
474
rcd->signal = namestocopy[2].s_value;
475
if (strcmp(rcd->signal, "rts") && strcmp(rcd->signal, "dtr")) {
476
LOG(PIL_CRIT, "%s: Invalid signal name '%s'",
477
pluginid, rcd->signal);
478
FREE(namestocopy[3].s_value);
483
rcd->msduration = strtol(namestocopy[3].s_value, &endptr, 0);
484
if (((errno == ERANGE)
485
&& (rcd->msduration == LONG_MIN || rcd->msduration == LONG_MAX))
486
|| *endptr != 0 || rcd->msduration < 1) {
487
LOG(PIL_CRIT, "%s: Invalid msduration '%s'",
488
pluginid, namestocopy[3].s_value);
489
FREE(namestocopy[3].s_value);
492
FREE(namestocopy[3].s_value);
498
* Return STONITH config vars
501
rcd_serial_get_confignames(StonithPlugin* p)
503
static const char * RcdParams[] = {ST_HOSTLIST, ST_TTYDEV
504
, ST_DTRRTS, ST_MSDURATION, NULL };
509
* Return STONITH info string
512
rcd_serial_getinfo(StonithPlugin * s, int reqtype)
514
struct pluginDevice* rcd;
517
ERRIFWRONGDEV(s,NULL);
519
* We look in the ST_TEXTDOMAIN catalog for our messages
521
rcd = (struct pluginDevice *)s;
531
ret = "RC Delayed Serial STONITH Device\n"
532
"This device can be constructed cheaply from"
533
" readily available components,\n"
534
"with sufficient expertise and testing.\n"
535
"See README.rcd_serial for circuit diagram.\n";
538
ret = "http://www.scl.co.uk/rcd_serial/";
540
case ST_CONF_XML: /* XML metadata */
551
* RCD_SERIAL Stonith destructor...
554
rcd_serial_destroy(StonithPlugin *s)
556
struct pluginDevice* rcd;
558
VOIDERRIFWRONGDEV(s);
560
rcd = (struct pluginDevice *)s;
562
rcd->pluginid = NOTrcd_serialID;
564
stonith_free_hostlist(rcd->hostlist);
565
rcd->hostlist = NULL;
578
* Create a new RCD_Serial Stonith device.
579
* Too bad this function can't be static. (Hmm, weird, it _is_ static?)
581
static StonithPlugin *
582
rcd_serial_new(const char *subplugin)
584
struct pluginDevice* rcd = ST_MALLOCT(struct pluginDevice);
587
LOG(PIL_CRIT, "out of memory");
590
memset(rcd, 0, sizeof(*rcd));
592
rcd->pluginid = pluginid;
593
rcd->hostlist = NULL;
598
rcd->idinfo = DEVICE;
599
rcd->sp.s_ops = &rcd_serialOps;