2
* Stonith module for Night/Ware RPC100S
4
* Original code from baytech.c by
5
* Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
7
* Modifications for NW RPC100S
8
* Copyright (c) 2000 Computer Generation Incorporated
9
* Eric Z. Ayers <eric.ayers@compgen.com>
11
* Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
13
* This library is free software; you can redistribute it and/or
14
* modify it under the terms of the GNU Lesser General Public
15
* License as published by the Free Software Foundation; either
16
* version 2.1 of the License, or (at your option) any later version.
18
* This library is distributed in the hope that it will be useful,
19
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
* Lesser General Public License for more details.
23
* You should have received a copy of the GNU Lesser General Public
24
* License along with this library; if not, write to the Free Software
25
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29
#include <lha_internal.h>
30
#define DEVICE "NW RPC100S Power Switch"
31
#include "stonith_plugin_common.h"
33
#define PIL_PLUGIN nw_rpc100s
34
#define PIL_PLUGIN_S "nw_rpc100s"
35
#define PIL_PLUGINLICENSE LICENSE_LGPL
36
#define PIL_PLUGINLICENSEURL URL_LGPL
37
#define MAX_CFGLINE 256
38
#include <pils/plugin.h>
40
static StonithPlugin * nw_rpc100s_new(const char *);
41
static void nw_rpc100s_destroy(StonithPlugin *);
42
static int nw_rpc100s_set_config(StonithPlugin *, StonithNVpair *);
43
static const char** nw_rpc100s_get_confignames(StonithPlugin *);
44
static const char * nw_rpc100s_getinfo(StonithPlugin * s, int InfoType);
45
static int nw_rpc100s_status(StonithPlugin * );
46
static int nw_rpc100s_reset_req(StonithPlugin * s, int request, const char * host);
47
static char ** nw_rpc100s_hostlist(StonithPlugin *);
49
static struct stonith_ops nw_rpc100sOps ={
50
nw_rpc100s_new, /* Create new STONITH object */
51
nw_rpc100s_destroy, /* Destroy STONITH object */
52
nw_rpc100s_getinfo, /* Return STONITH info string */
53
nw_rpc100s_get_confignames,/* Return STONITH info string */
54
nw_rpc100s_set_config, /* Get configuration from NVpairs */
55
nw_rpc100s_status, /* Return STONITH device status */
56
nw_rpc100s_reset_req, /* Request a reset */
57
nw_rpc100s_hostlist, /* Return list of supported hosts */
60
PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
61
static const PILPluginImports* PluginImports;
62
static PILPlugin* OurPlugin;
63
static PILInterface* OurInterface;
64
static StonithImports* OurImports;
65
static void* interfprivate;
67
#include "stonith_signal.h"
69
#define DOESNT_USE_STONITHKILLCOMM
70
#define DOESNT_USE_STONITHSCANLINE
71
#include "stonith_expect_helpers.h"
74
PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
77
PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
79
/* Force the compiler to do a little type checking */
80
(void)(PILPluginInitFun)PIL_PLUGIN_INIT;
82
PluginImports = imports;
85
/* Register ourself as a plugin */
86
imports->register_plugin(us, &OurPIExports);
88
/* Register our interface implementation */
89
return imports->register_interface(us, PIL_PLUGINTYPE_S
99
The Nightware RPS-100S is manufactured by:
101
Micro Energetics Corp
103
http://www.nightware.com/
105
Thank you to David Hicks of Micro Energetics Corp. for providing
106
a demo unit to write this software.
108
This switch has a very simple protocol,
109
You issue a command and it gives a response.
110
Sample commands are conveniently documented on a sticker on the
111
bottom of the device.
113
The switch accepts a single command of the form
115
//0,yyy,zzz[/m][/h]<CR>
117
Where yyy is the wait time before activiting the relay.
118
zzz is the relay time.
120
The default is that the relay is in a default state of ON, which
121
means that usually yyy is the number of seconds to wait
122
before shutting off the power and zzz is the number of seconds the
123
power remains off. There is a dip switch to change the default
124
state to 'OFF'. Don't set this switch. It will screw up this code.
126
An asterisk can be used for zzz to specify an infinite switch time.
127
The /m /and /h command options will convert the specified wait and
128
switch times to either minutewes or hours.
133
<cr><lf>Invalid Entry<cr><lf>
136
As far as THIS software is concerned, we have to implement 4 commands:
138
status --> //0,0,BOGUS; # Not a real command, this is just a
139
# probe to see if switch is alive
140
open(on) --> //0,0,0; # turn power to default state (on)
141
close(off) --> //0,0,*; # leave power off indefinitely
142
reboot --> //0,0,10; # immediately turn power off for 10 seconds.
144
and expect the response 'OK' to confirm that the unit is operational.
149
struct pluginDevice {
151
const char * pluginid;
154
int fd; /* FD open to the serial port */
156
char * device; /* Serial device name to use to communicate
160
char * node; /* Name of the node that this is controlling */
164
/* This string is used to identify this type of object in the config file */
165
static const char * pluginid = "NW_RPC100S";
166
static const char * NOTrpcid = "NW RPC100S device has been destroyed";
168
#include "stonith_config_xml.h"
170
static const char *nw_rpc100sXML =
177
* Different expect strings that we get from the NW_RPC100S
178
* Remote Power Controllers...
181
static struct Etoken NWtokOK[] = { {"OK", 0, 0}, {NULL,0,0}};
182
static struct Etoken NWtokInvalidEntry[] = { {"Invalid Entry", 0, 0}, {NULL,0,0}};
183
/* Accept either a CR/NL or an NL/CR */
184
static struct Etoken NWtokCRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};
186
static int RPCConnect(struct pluginDevice * ctx);
187
static int RPCDisconnect(struct pluginDevice * ctx);
189
static int RPCReset(struct pluginDevice*, int unitnum, const char * rebootid);
190
#if defined(ST_POWERON)
191
static int RPCOn(struct pluginDevice*, int unitnum, const char * rebootid);
193
#if defined(ST_POWEROFF)
194
static int RPCOff(struct pluginDevice*, int unitnum, const char * rebootid);
196
static int RPCNametoOutlet ( struct pluginDevice * ctx, const char * host );
198
/*static int RPC_parse_config_info(struct pluginDevice* ctx, const char * info);*/
201
#define SENDCMD(cmd, timeout) { \
202
int return_val = RPCSendCommand(ctx, cmd, timeout); \
203
if (return_val != S_OK) { \
209
* RPCSendCommand - send a command to the specified outlet
212
RPCSendCommand (struct pluginDevice *ctx, const char *command, int timeout)
214
char writebuf[64]; /* All commands are short.
215
They should be WAY LESS
218
int return_val; /* system call result */
219
fd_set rfds, wfds, xfds;
220
/* list of FDs for select() */
221
struct timeval tv; /* */
227
snprintf (writebuf, sizeof(writebuf), "%s\r", command);
230
LOG(PIL_DEBUG, "Sending %s", writebuf);
233
/* Make sure the serial port won't block on us. use select() */
234
FD_SET(ctx->fd, &wfds);
235
FD_SET(ctx->fd, &xfds);
240
return_val = select(ctx->fd+1, NULL, &wfds,&xfds, &tv);
241
if (return_val == 0) {
242
/* timeout waiting on serial port */
243
LOG(PIL_CRIT, "%s: Timeout writing to %s"
244
, pluginid, ctx->device);
246
} else if ((return_val == -1) || FD_ISSET(ctx->fd, &xfds)) {
247
/* an error occured */
248
LOG(PIL_CRIT, "%s: Error before writing to %s: %s"
249
, pluginid, ctx->device, strerror(errno));
253
/* send the command */
254
if (write(ctx->fd, writebuf, strlen(writebuf)) !=
255
(int)strlen(writebuf)) {
256
LOG(PIL_CRIT, "%s: Error writing to %s : %s"
257
, pluginid, ctx->device, strerror(errno));
264
} /* end RPCSendCommand() */
267
* RPCReset - Reset (power-cycle) the given outlet number
269
* This device can only control one power outlet - unitnum is ignored.
273
RPCReset(struct pluginDevice* ctx, int unitnum, const char * rebootid)
277
LOG(PIL_DEBUG, "Calling RPCReset (%s)", pluginid);
281
LOG(PIL_CRIT, "%s: device %s is not open!", pluginid
286
/* send the "toggle power" command */
287
SENDCMD("//0,0,10;\r\n", 12);
290
EXPECT(ctx->fd, NWtokOK, 5);
292
LOG(PIL_DEBUG, "Got OK");
294
EXPECT(ctx->fd, NWtokCRNL, 2);
296
LOG(PIL_DEBUG, "Got NL");
301
} /* end RPCReset() */
304
#if defined(ST_POWERON)
306
* RPCOn - Turn OFF the given outlet number
309
RPCOn(struct pluginDevice* ctx, int unitnum, const char * host)
313
LOG(PIL_CRIT, "%s: device %s is not open!", pluginid
318
/* send the "On" command */
319
SENDCMD("//0,0,0;\r\n", 10);
322
EXPECT(ctx->fd, NWtokOK, 5);
323
EXPECT(ctx->fd, NWtokCRNL, 2);
331
#if defined(ST_POWEROFF)
333
* RPCOff - Turn Off the given outlet number
336
RPCOff(struct pluginDevice* ctx, int unitnum, const char * host)
340
LOG(PIL_CRIT, "%s: device %s is not open!", pluginid
345
/* send the "Off" command */
346
SENDCMD("//0,0,*;\r\n", 10);
349
EXPECT(ctx->fd, NWtokOK, 5);
350
EXPECT(ctx->fd, NWtokCRNL, 2);
359
* nw_rpc100s_status - API entry point to probe the status of the stonith device
360
* (basically just "is it reachable and functional?", not the
361
* status of the individual outlets)
364
* S_OOPS - some error occured
365
* S_OK - if the stonith device is reachable and online.
368
nw_rpc100s_status(StonithPlugin *s)
370
struct pluginDevice* ctx;
373
LOG(PIL_DEBUG, "Calling nw_rpc100s_status (%s)", pluginid);
376
ERRIFNOTCONFIGED(s,S_OOPS);
378
ctx = (struct pluginDevice*) s;
379
if (RPCConnect(ctx) != S_OK) {
383
/* The "connect" really does enough work to see if the
384
controller is alive... It verifies that it is returning
388
return(RPCDisconnect(ctx));
392
* nw_rpc100s_hostlist - API entry point to return the list of hosts
393
* for the devices on this NW_RPC100S unit
395
* This type of device is configured from the config file,
396
* so we don't actually have to connect to figure this
397
* out, just peruse the 'ctx' structure.
400
* a malloced array, terminated with a NULL,
401
* of null-terminated malloc'ed strings.
404
nw_rpc100s_hostlist(StonithPlugin *s)
406
char ** ret = NULL; /* list to return */
407
struct pluginDevice* ctx;
410
LOG(PIL_DEBUG, "Calling nw_rpc100s_hostlist (%s)", pluginid);
413
ERRIFNOTCONFIGED(s,NULL);
415
ctx = (struct pluginDevice*) s;
417
ret = OurImports->StringToHostList(ctx->node);
419
LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
425
} /* end si_hostlist() */
428
* Parse the given configuration information, and stash it away...
430
* <info> contains the parameters specific to this type of object
432
* The format of <parameters> for this module is:
433
* <serial device> <remotenode> <outlet> [<remotenode> <outlet>] ...
435
* e.g. A machine named 'nodea' can kill a machine named 'nodeb' through
436
* a device attached to serial port /dev/ttyS0.
437
* A machine named 'nodeb' can kill machines 'nodea' and 'nodec'
438
* through a device attached to serial port /dev/ttyS1 (outlets 0
439
* and 1 respectively)
441
* stonith nodea NW_RPC100S /dev/ttyS0 nodeb 0
442
* stonith nodeb NW_RPC100S /dev/ttyS0 nodea 0 nodec 1
444
* Another possible configuration is for 2 stonith devices accessible
445
* through 2 different serial ports on nodeb:
447
* stonith nodeb NW_RPC100S /dev/ttyS0 nodea 0
448
* stonith nodeb NW_RPC100S /dev/ttyS1 nodec 0
452
RPC_parse_config_info(struct pluginDevice* ctx, const char * info)
460
* Connect to the given NW_RPC100S device.
462
* ctx->fd now contains a valid file descriptor to the serial port
463
* ??? LOCK THE SERIAL PORT ???
468
* S_TIMEOUT if the device did not respond
472
RPCConnect(struct pluginDevice * ctx)
475
/* Open the serial port if it isn't already open */
479
if (OurImports->TtyLock(ctx->device) < 0) {
480
LOG(PIL_CRIT, "%s: TtyLock failed.", pluginid);
484
ctx->fd = open (ctx->device, O_RDWR);
486
LOG(PIL_CRIT, "%s: Can't open %s : %s"
487
, pluginid, ctx->device, strerror(errno));
491
/* set the baudrate to 9600 8 - N - 1 */
492
memset (&tio, 0, sizeof(tio));
494
/* ??? ALAN - the -tradtitional flag on gcc causes the
495
CRTSCTS constant to generate a warning, and warnings
496
are treated as errors, so I can't set this flag! - EZA ???
498
Hmmm. now that I look at the documentation, RTS
499
is just wired high on this device! we don't need it.
501
/* tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD | CRTSCTS ;*/
502
tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD ;
503
tio.c_lflag = ICANON;
505
if (tcsetattr (ctx->fd, TCSANOW, &tio) < 0) {
506
LOG(PIL_CRIT, "%s: Can't set attributes %s : %s"
507
, pluginid, ctx->device, strerror(errno));
509
OurImports->TtyUnlock(ctx->device);
513
/* flush all data to and fro the serial port before we start */
514
if (tcflush (ctx->fd, TCIOFLUSH) < 0) {
515
LOG(PIL_CRIT, "%s: Can't flush %s : %s"
516
, pluginid, ctx->device, strerror(errno));
518
OurImports->TtyUnlock(ctx->device);
526
/* Send a BOGUS string */
527
SENDCMD("//0,0,BOGUS;\r\n", 10);
529
/* Should reply with "Invalid Command" */
531
LOG(PIL_DEBUG, "Waiting for \"Invalid Entry\"");
533
EXPECT(ctx->fd, NWtokInvalidEntry, 12);
535
LOG(PIL_DEBUG, "Got Invalid Entry");
537
EXPECT(ctx->fd, NWtokCRNL, 2);
539
LOG(PIL_DEBUG, "Got NL");
546
RPCDisconnect(struct pluginDevice * ctx)
550
/* Flush the serial port, we don't care what happens to the characters
551
and failing to do this can cause close to hang.
553
tcflush(ctx->fd, TCIOFLUSH);
555
if (ctx->device != NULL) {
556
OurImports->TtyUnlock(ctx->device);
565
* RPCNametoOutlet - Map a hostname to an outlet number on this stonith device.
568
* 0 on success ( the outlet number on the RPS10 - there is only one )
569
* -1 on failure (host not found in the config file)
573
RPCNametoOutlet ( struct pluginDevice * ctx, const char * host )
577
if (!strcasecmp(ctx->node, host)) {
586
* nw_rpc100s_reset - API call to Reset (reboot) the given host on
587
* this Stonith device. This involves toggling the power off
588
* and then on again, OR just calling the builtin reset command
589
* on the stonith device.
592
nw_rpc100s_reset_req(StonithPlugin * s, int request, const char * host)
597
struct pluginDevice* ctx;
600
LOG(PIL_DEBUG, "Calling nw_rpc100s_reset (%s)", pluginid);
603
ERRIFNOTCONFIGED(s,S_OOPS);
605
ctx = (struct pluginDevice*) s;
607
if ((rc = RPCConnect(ctx)) != S_OK) {
611
outletnum = RPCNametoOutlet(ctx, host);
612
LOG(PIL_DEBUG, "zk:outletname=%d", outletnum);
615
LOG(PIL_WARN, "%s doesn't control host [%s]"
616
, ctx->device, host);
623
#if defined(ST_POWERON)
625
rc = RPCOn(ctx, outletnum, host);
628
#if defined(ST_POWEROFF)
630
rc = RPCOff(ctx, outletnum, host);
633
case ST_GENERIC_RESET:
634
rc = RPCReset(ctx, outletnum, host);
641
lorc = RPCDisconnect(ctx);
643
return(rc != S_OK ? rc : lorc);
647
* Parse the information in the given string
648
* and stash it away...
651
nw_rpc100s_set_config(StonithPlugin* s, StonithNVpair *list)
653
struct pluginDevice* ctx;
654
StonithNamesToGet namestocopy [] =
656
, {ST_HOSTLIST, NULL}
662
ERRIFWRONGDEV(s,S_OOPS);
663
if (s->isconfigured) {
667
ctx = (struct pluginDevice*) s;
669
if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
672
ctx->device = namestocopy[0].s_value;
673
ctx->node = namestocopy[1].s_value;
679
* Return STONITH config vars
682
nw_rpc100s_get_confignames(StonithPlugin* p)
684
static const char * RpcParams[] = {ST_TTYDEV , ST_HOSTLIST, NULL };
691
* nw_rpc100s_getinfo - API entry point to retrieve something from the handle
694
nw_rpc100s_getinfo(StonithPlugin * s, int reqtype)
696
struct pluginDevice* ctx;
699
ERRIFWRONGDEV(s,NULL);
702
* We look in the ST_TEXTDOMAIN catalog for our messages
704
ctx = (struct pluginDevice *)s;
714
ret = "Micro Energetics Night/Ware RPC100S";
717
ret = "http://www.microenergeticscorp.com/";
719
case ST_CONF_XML: /* XML metadata */
730
* nw_rpc100s_destroy - API entry point to destroy a NW_RPC100S Stonith object.
733
nw_rpc100s_destroy(StonithPlugin *s)
735
struct pluginDevice* ctx;
737
VOIDERRIFWRONGDEV(s);
739
ctx = (struct pluginDevice *)s;
741
ctx->pluginid = NOTrpcid;
743
/* close the fd if open and set ctx->fd to invalid */
746
if (ctx->device != NULL) {
750
if (ctx->node != NULL) {
758
* nw_rpc100s_new - API entry point called to create a new NW_RPC100S Stonith
761
static StonithPlugin *
762
nw_rpc100s_new(const char *subplugin)
764
struct pluginDevice* ctx = ST_MALLOCT(struct pluginDevice);
767
LOG(PIL_CRIT, "out of memory");
770
memset(ctx, 0, sizeof(*ctx));
771
ctx->pluginid = pluginid;
775
ctx->idinfo = DEVICE;
776
ctx->sp.s_ops = &nw_rpc100sOps;