3
* Copyright 2001 Mission Critical Linux, Inc.
8
* Stonith module for APC Master Switch (AP9211)
10
* Copyright (c) 2001 Mission Critical Linux, Inc.
11
* author: mike ledoux <mwl@mclinux.com>
12
* author: Todd Wheeling <wheeling@mclinux.com>
13
* mangled by Sun Jiang Dong, <sunjd@cn.ibm.com>, IBM, 2005
15
* Based strongly on original code from baytech.c by Alan Robertson.
17
* This library is free software; you can redistribute it and/or
18
* modify it under the terms of the GNU Lesser General Public
19
* License as published by the Free Software Foundation; either
20
* version 2.1 of the License, or (at your option) any later version.
22
* This library is distributed in the hope that it will be useful,
23
* but WITHOUT ANY WARRANTY; without even the implied warranty of
24
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25
* Lesser General Public License for more details.
27
* You should have received a copy of the GNU Lesser General Public
28
* License along with this library; if not, write to the Free Software
29
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35
* 1. The APC MasterSwitch, unlike the BayTech network power switch,
36
* accepts only one (telnet) connection/session at a time. When one
37
* session is active, any subsequent attempt to connect to the MasterSwitch
38
* will result in a connection refused/closed failure. In a cluster
39
* environment or other environment utilizing polling/monitoring of the
40
* MasterSwitch (from multiple nodes), this can clearly cause problems.
41
* Obviously the more nodes and the shorter the polling interval, the more
42
* frequently such errors/collisions may occur.
44
* 2. We observed that on busy networks where there may be high occurances
45
* of broadcasts, the MasterSwitch became unresponsive. In some
46
* configurations this necessitated placing the power switch onto a
50
#include <lha_internal.h>
52
#define DEVICE "APC MasterSwitch"
54
#define DOESNT_USE_STONITHKILLCOMM 1
56
#include "stonith_plugin_common.h"
58
#define PIL_PLUGIN apcmaster
59
#define PIL_PLUGIN_S "apcmaster"
60
#define PIL_PLUGINLICENSE LICENSE_LGPL
61
#define PIL_PLUGINLICENSEURL URL_LGPL
62
#include <pils/plugin.h>
64
#include "stonith_signal.h"
66
static StonithPlugin * apcmaster_new(const char *);
67
static void apcmaster_destroy(StonithPlugin *);
68
static const char ** apcmaster_get_confignames(StonithPlugin *);
69
static int apcmaster_set_config(StonithPlugin *, StonithNVpair *);
70
static const char * apcmaster_getinfo(StonithPlugin * s, int InfoType);
71
static int apcmaster_status(StonithPlugin * );
72
static int apcmaster_reset_req(StonithPlugin * s, int request, const char * host);
73
static char ** apcmaster_hostlist(StonithPlugin *);
75
static struct stonith_ops apcmasterOps ={
76
apcmaster_new, /* Create new STONITH object */
77
apcmaster_destroy, /* Destroy STONITH object */
78
apcmaster_getinfo, /* Return STONITH info string */
79
apcmaster_get_confignames, /* Get configuration parameters */
80
apcmaster_set_config, /* Set configuration */
81
apcmaster_status, /* Return STONITH device status */
82
apcmaster_reset_req, /* Request a reset */
83
apcmaster_hostlist, /* Return list of supported hosts */
86
PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
88
static const PILPluginImports* PluginImports;
89
static PILPlugin* OurPlugin;
90
static PILInterface* OurInterface;
91
static StonithImports* OurImports;
92
static void* interfprivate;
94
#include "stonith_expect_helpers.h"
97
PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
100
PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
102
/* Force the compiler to do a little type checking */
103
(void)(PILPluginInitFun)PIL_PLUGIN_INIT;
105
PluginImports = imports;
108
/* Register ourself as a plugin */
109
imports->register_plugin(us, &OurPIExports);
111
/* Register our interface implementation */
112
return imports->register_interface(us, PIL_PLUGINTYPE_S
122
* I have an AP9211. This code has been tested with this switch.
125
struct pluginDevice {
127
const char * pluginid;
137
static const char * pluginid = "APCMS-Stonith";
138
static const char * NOTpluginID = "APCMS device has been destroyed";
141
* Different expect strings that we get from the APC MasterSwitch
144
#define APCMSSTR "American Power Conversion"
146
static struct Etoken EscapeChar[] = { {"Escape character is '^]'.", 0, 0}
148
static struct Etoken login[] = { {"User Name :", 0, 0}, {NULL,0,0}};
149
static struct Etoken password[] = { {"Password :", 0, 0} ,{NULL,0,0}};
150
static struct Etoken Prompt[] = { {"> ", 0, 0} ,{NULL,0,0}};
151
static struct Etoken LoginOK[] = { {APCMSSTR, 0, 0}
152
, {"User Name :", 1, 0} ,{NULL,0,0}};
153
static struct Etoken Separator[] = { {"-----", 0, 0} ,{NULL,0,0}};
155
/* We may get a notice about rebooting, or a request for confirmation */
156
static struct Etoken Processing[] = { {"Press <ENTER> to continue", 0, 0}
157
, {"Enter 'YES' to continue", 1, 0}
160
#include "stonith_config_xml.h"
162
static const char *apcmasterXML =
169
static int MS_connect_device(struct pluginDevice * ms);
170
static int MSLogin(struct pluginDevice * ms);
171
static int MSRobustLogin(struct pluginDevice * ms);
172
static int MSNametoOutlet(struct pluginDevice*, const char * name);
173
static int MSReset(struct pluginDevice*, int outletNum, const char * host);
174
static int MSLogout(struct pluginDevice * ms);
176
#if defined(ST_POWERON) && defined(ST_POWEROFF)
177
static int apcmaster_onoff(struct pluginDevice*, int outletnum, const char * unitid
181
/* Login to the APC Master Switch */
184
MSLogin(struct pluginDevice * ms)
186
EXPECT(ms->rdfd, EscapeChar, 10);
189
* We should be looking at something like this:
192
EXPECT(ms->rdfd, login, 10);
193
SEND(ms->wrfd, ms->user);
194
SEND(ms->wrfd, "\r");
196
/* Expect "Password :" */
197
EXPECT(ms->rdfd, password, 10);
198
SEND(ms->wrfd, ms->passwd);
199
SEND(ms->wrfd, "\r");
201
switch (StonithLookFor(ms->rdfd, LoginOK, 30)) {
204
LOG(PIL_INFO, "Successful login to %s.", ms->idinfo);
207
case 1: /* Uh-oh - bad password */
208
LOG(PIL_CRIT, "Invalid password for %s.", ms->idinfo);
212
return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
218
/* Attempt to login up to 20 times... */
221
MSRobustLogin(struct pluginDevice * ms)
227
if (MS_connect_device(ms) == S_OK) {
243
/* Log out of the APC Master Switch */
246
int MSLogout(struct pluginDevice* ms)
250
/* Make sure we're in the right menu... */
251
/*SEND(ms->wrfd, "\033\033\033\033\033\033\033"); */
252
SEND(ms->wrfd, "\033");
253
EXPECT(ms->rdfd, Prompt, 5);
254
SEND(ms->wrfd, "\033");
255
EXPECT(ms->rdfd, Prompt, 5);
256
SEND(ms->wrfd, "\033");
257
EXPECT(ms->rdfd, Prompt, 5);
258
SEND(ms->wrfd, "\033");
259
EXPECT(ms->rdfd, Prompt, 5);
260
SEND(ms->wrfd, "\033");
263
rc = StonithLookFor(ms->rdfd, Prompt, 5);
266
SEND(ms->wrfd, "4\r");
270
ms->wrfd = ms->rdfd = -1;
272
return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS));
274
/* Reset (power-cycle) the given outlets */
276
MSReset(struct pluginDevice* ms, int outletNum, const char *host)
280
/* Make sure we're in the top level menu */
281
SEND(ms->wrfd, "\033");
282
EXPECT(ms->rdfd, Prompt, 5);
283
SEND(ms->wrfd, "\033");
284
EXPECT(ms->rdfd, Prompt, 5);
285
SEND(ms->wrfd, "\033");
286
EXPECT(ms->rdfd, Prompt, 5);
287
SEND(ms->wrfd, "\033");
288
EXPECT(ms->rdfd, Prompt, 5);
289
SEND(ms->wrfd, "\033");
292
EXPECT(ms->rdfd, Prompt, 5);
294
/* Request menu 1 (Device Control) */
295
SEND(ms->wrfd, "1\r");
297
/* Select requested outlet */
298
EXPECT(ms->rdfd, Prompt, 5);
299
snprintf(unum, sizeof(unum), "%i\r", outletNum);
300
SEND(ms->wrfd, unum);
302
/* Select menu 1 (Control Outlet) */
303
EXPECT(ms->rdfd, Prompt, 5);
304
SEND(ms->wrfd, "1\r");
306
/* Select menu 3 (Immediate Reboot) */
307
EXPECT(ms->rdfd, Prompt, 5);
308
SEND(ms->wrfd, "3\r");
310
/* Expect "Press <ENTER> " or "Enter 'YES'" (if confirmation turned on) */
312
switch (StonithLookFor(ms->rdfd, Processing, 5)) {
313
case 0: /* Got "Press <ENTER>" Do so */
314
SEND(ms->wrfd, "\r");
317
case 1: /* Got that annoying command confirmation :-( */
318
SEND(ms->wrfd, "YES\r");
322
return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
326
LOG(PIL_INFO, "Host being rebooted: %s", host);
329
if (StonithLookFor(ms->rdfd, Prompt, 10) < 0) {
330
return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
333
/* All Right! Power is back on. Life is Good! */
335
LOG(PIL_INFO, "Power restored to host: %s", host);
337
/* Return to top level menu */
338
SEND(ms->wrfd, "\033");
339
EXPECT(ms->rdfd, Prompt, 5);
340
SEND(ms->wrfd, "\033");
341
EXPECT(ms->rdfd, Prompt, 5);
342
SEND(ms->wrfd, "\033");
343
EXPECT(ms->rdfd, Prompt, 5);
344
SEND(ms->wrfd, "\033");
345
EXPECT(ms->rdfd, Prompt, 5);
346
SEND(ms->wrfd, "\033");
347
EXPECT(ms->rdfd, Prompt, 5);
348
SEND(ms->wrfd, "\033");
353
#if defined(ST_POWERON) && defined(ST_POWEROFF)
355
apcmaster_onoff(struct pluginDevice* ms, int outletNum, const char * unitid, int req)
359
const char * onoff = (req == ST_POWERON ? "1\r" : "2\r");
362
if ((rc = MSRobustLogin(ms) != S_OK)) {
363
LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
367
/* Make sure we're in the top level menu */
368
SEND(ms->wrfd, "\033");
369
EXPECT(ms->rdfd, Prompt, 5);
370
SEND(ms->wrfd, "\033");
371
EXPECT(ms->rdfd, Prompt, 5);
372
SEND(ms->wrfd, "\033");
373
EXPECT(ms->rdfd, Prompt, 5);
374
SEND(ms->wrfd, "\033");
375
EXPECT(ms->rdfd, Prompt, 5);
376
SEND(ms->wrfd, "\033");
379
EXPECT(ms->rdfd, Prompt, 5);
381
/* Request menu 1 (Device Control) */
382
SEND(ms->wrfd, "1\r");
384
/* Select requested outlet */
385
snprintf(unum, sizeof(unum), "%d\r", outletNum);
386
SEND(ms->wrfd, unum);
388
/* Select menu 1 (Control Outlet) */
389
SEND(ms->wrfd, "1\r");
391
/* Send ON/OFF command for given outlet */
392
SEND(ms->wrfd, onoff);
394
/* Expect "Press <ENTER> " or "Enter 'YES'" (if confirmation turned on) */
396
switch (StonithLookFor(ms->rdfd, Processing, 5)) {
397
case 0: /* Got "Press <ENTER>" Do so */
398
SEND(ms->wrfd, "\r");
401
case 1: /* Got that annoying command confirmation :-( */
402
SEND(ms->wrfd, "YES\r");
406
return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
409
EXPECT(ms->rdfd, Prompt, 10);
411
/* All Right! Command done. Life is Good! */
412
LOG(PIL_INFO, "Power to MS outlet(s) %d turned %s", outletNum, onoff);
413
/* Pop back to main menu */
414
SEND(ms->wrfd, "\033\033\033\033\033\033\033\r");
417
#endif /* defined(ST_POWERON) && defined(ST_POWEROFF) */
420
* Map the given host name into an (AC) Outlet number on the power strip
424
MSNametoOutlet(struct pluginDevice* ms, const char * name)
426
char NameMapping[128];
432
/* Verify that we're in the top-level menu */
433
EXPECT(ms->rdfd, Prompt, 5);
434
SEND(ms->wrfd, "\033");
435
EXPECT(ms->rdfd, Prompt, 5);
436
SEND(ms->wrfd, "\033");
437
EXPECT(ms->rdfd, Prompt, 5);
438
SEND(ms->wrfd, "\033");
439
EXPECT(ms->rdfd, Prompt, 5);
440
SEND(ms->wrfd, "\033");
443
EXPECT(ms->rdfd, Prompt, 5);
445
/* Request menu 1 (Device Control) */
446
SEND(ms->wrfd, "1\r");
448
/* Expect: "-----" so we can skip over it... */
449
EXPECT(ms->rdfd, Separator, 5);
450
EXPECT(ms->rdfd, CRNL, 5);
451
EXPECT(ms->rdfd, CRNL, 5);
453
/* Looks Good! Parse the status output */
457
NameMapping[0] = EOS;
458
SNARF(ms->rdfd, NameMapping, 5);
459
if (sscanf(NameMapping
460
, "%d- %23c",&sockno, sockname) == 2) {
462
char * last = sockname+23;
466
/* Strip off trailing blanks */
467
for(; last > sockname; --last) {
474
if (strcasecmp(name, sockname) == 0) {
478
} while (strlen(NameMapping) > 2 && times < 8);
480
/* Pop back out to the top level menu */
481
EXPECT(ms->rdfd, Prompt, 5);
482
SEND(ms->wrfd, "\033");
483
EXPECT(ms->rdfd, Prompt, 5);
484
SEND(ms->wrfd, "\033");
485
EXPECT(ms->rdfd, Prompt, 5);
486
SEND(ms->wrfd, "\033");
487
EXPECT(ms->rdfd, Prompt, 5);
488
SEND(ms->wrfd, "\033");
493
apcmaster_status(StonithPlugin *s)
495
struct pluginDevice* ms;
498
ERRIFNOTCONFIGED(s,S_OOPS);
500
ms = (struct pluginDevice*) s;
502
if ((rc = MSRobustLogin(ms) != S_OK)) {
503
LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
508
SEND(ms->wrfd, "\033\r");
509
EXPECT(ms->rdfd, Prompt, 5);
511
return(MSLogout(ms));
515
* Return the list of hosts (outlet names) for the devices on this MS unit
519
apcmaster_hostlist(StonithPlugin *s)
521
char NameMapping[128];
523
unsigned int numnames = 0;
525
struct pluginDevice* ms;
528
ERRIFNOTCONFIGED(s,NULL);
530
ms = (struct pluginDevice*) s;
532
if (MSRobustLogin(ms) != S_OK) {
533
LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
538
NULLEXPECT(ms->rdfd, Prompt, 10);
540
/* Request menu 1 (Device Control) */
541
SEND(ms->wrfd, "1\r");
543
/* Expect: "-----" so we can skip over it... */
544
NULLEXPECT(ms->rdfd, Separator, 5);
545
NULLEXPECT(ms->rdfd, CRNL, 5);
546
NULLEXPECT(ms->rdfd, CRNL, 5);
548
/* Looks Good! Parse the status output */
552
NameMapping[0] = EOS;
553
NULLSNARF(ms->rdfd, NameMapping, 5);
554
if (sscanf(NameMapping
555
, "%d- %23c",&sockno, sockname) == 2) {
557
char * last = sockname+23;
562
/* Strip off trailing blanks */
563
for(; last > sockname; --last) {
570
if (numnames >= DIMOF(NameList)-1) {
573
if ((nm = (char*)STRDUP(sockname)) == NULL) {
577
NameList[numnames] = nm;
579
NameList[numnames] = NULL;
581
} while (strlen(NameMapping) > 2);
583
/* Pop back out to the top level menu */
584
SEND(ms->wrfd, "\033");
585
NULLEXPECT(ms->rdfd, Prompt, 10);
586
SEND(ms->wrfd, "\033");
587
NULLEXPECT(ms->rdfd, Prompt, 10);
588
SEND(ms->wrfd, "\033");
589
NULLEXPECT(ms->rdfd, Prompt, 10);
590
SEND(ms->wrfd, "\033");
591
NULLEXPECT(ms->rdfd, Prompt, 10);
595
ret = (char **)MALLOC((numnames+1)*sizeof(char*));
599
memcpy(ret, NameList, (numnames+1)*sizeof(char*));
606
LOG(PIL_CRIT, "out of memory");
607
for (i=0; i<numnames; i++) {
614
* Connect to the given MS device. We should add serial support here
618
MS_connect_device(struct pluginDevice * ms)
620
int fd = OurImports->OpenStreamSocket(ms->device
621
, TELNET_PORT, TELNET_SERVICE);
626
ms->rdfd = ms->wrfd = fd;
631
* Reset the given host on this StonithPlugin device.
634
apcmaster_reset_req(StonithPlugin * s, int request, const char * host)
638
struct pluginDevice* ms;
640
ERRIFNOTCONFIGED(s,S_OOPS);
642
ms = (struct pluginDevice*) s;
644
if ((rc = MSRobustLogin(ms)) != S_OK) {
645
LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
649
noutlet = MSNametoOutlet(ms, host);
651
LOG(PIL_WARN, "%s doesn't control host [%s]"
657
#if defined(ST_POWERON) && defined(ST_POWEROFF)
659
rc = apcmaster_onoff(ms, noutlet, host, request);
662
rc = apcmaster_onoff(ms, noutlet, host, request);
665
case ST_GENERIC_RESET:
666
rc = MSReset(ms, noutlet, host);
675
return(rc != S_OK ? rc : lorc);
679
* Get the configuration parameters names
682
apcmaster_get_confignames(StonithPlugin * s)
684
static const char * ret[] = {ST_IPADDR, ST_LOGIN, ST_PASSWD, NULL};
689
* Set the configuration parameters
692
apcmaster_set_config(StonithPlugin * s, StonithNVpair * list)
694
struct pluginDevice* sd = (struct pluginDevice *)s;
696
StonithNamesToGet namestocopy [] =
703
ERRIFWRONGDEV(s, S_OOPS);
704
if (sd->sp.isconfigured) {
708
if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
711
sd->device = namestocopy[0].s_value;
712
sd->user = namestocopy[1].s_value;
713
sd->passwd = namestocopy[2].s_value;
719
apcmaster_getinfo(StonithPlugin * s, int reqtype)
721
struct pluginDevice* ms;
724
ERRIFWRONGDEV(s,NULL);
727
* We look in the ST_TEXTDOMAIN catalog for our messages
729
ms = (struct pluginDevice *)s;
736
case ST_DEVICENAME: /* Which particular device? */
741
ret = "APC MasterSwitch (via telnet)\n"
742
"NOTE: The APC MasterSwitch accepts only one (telnet)\n"
743
"connection/session a time. When one session is active,\n"
744
"subsequent attempts to connect to the MasterSwitch"
749
ret = "http://www.apc.com/";
752
case ST_CONF_XML: /* XML metadata */
764
* APC MasterSwitch StonithPlugin destructor...
767
apcmaster_destroy(StonithPlugin *s)
769
struct pluginDevice* ms;
771
VOIDERRIFWRONGDEV(s);
773
ms = (struct pluginDevice *)s;
775
ms->pluginid = NOTpluginID;
784
if (ms->device != NULL) {
788
if (ms->user != NULL) {
792
if (ms->passwd != NULL) {
799
/* Create a new APC Master Switch StonithPlugin device. */
801
static StonithPlugin *
802
apcmaster_new(const char *subplugin)
804
struct pluginDevice* ms = ST_MALLOCT(struct pluginDevice);
807
LOG(PIL_CRIT, "out of memory");
810
memset(ms, 0, sizeof(*ms));
811
ms->pluginid = pluginid;
819
ms->sp.s_ops = &apcmasterOps;