1 /*
2 * Stonith module for WTI Remote Power Controllers (RPS-10M device)
3 *
4 * Original code from baytech.c by
5 * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
6 *
7 * Modifications for WTI RPS10
8 * Copyright (c) 2000 Computer Generation Incorporated
9 * Eric Z. Ayers <eric.ayers@compgen.com>
10 *
11 * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
12 *
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.
17 *
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.
22 *
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
26 *
27 */
28
29 #include <lha_internal.h>
30
31 #define DEVICE "WTI RPS10 Power Switch"
32 #include "stonith_plugin_common.h"
33
34 #include <termios.h>
35 #define PIL_PLUGIN rps10
36 #define PIL_PLUGIN_S "rps10"
37 #define PIL_PLUGINLICENSE LICENSE_LGPL
38 #define PIL_PLUGINLICENSEURL URL_LGPL
39 #define ST_RPS10 "serial_to_targets"
40 #define MAX_PRSID 256
41 #include <pils/plugin.h>
42
43 static StonithPlugin * rps10_new(const char *);
44 static void rps10_destroy(StonithPlugin *);
45 static int rps10_set_config(StonithPlugin *, StonithNVpair *);
46 static const char * const * rps10_get_confignames(StonithPlugin *);
47 static const char * rps10_getinfo(StonithPlugin * s, int InfoType);
48 static int rps10_status(StonithPlugin * );
49 static int rps10_reset_req(StonithPlugin * s, int request, const char * host);
50 static char ** rps10_hostlist(StonithPlugin *);
51
52 static struct stonith_ops rps10Ops ={
53 rps10_new, /* Create new STONITH object */
54 rps10_destroy, /* Destroy STONITH object */
55 rps10_getinfo, /* Return STONITH info string */
56 rps10_get_confignames, /* Return STONITH info string */
57 rps10_set_config, /* Get configuration from NVpairs */
58 rps10_status, /* Return STONITH device status */
59 rps10_reset_req, /* Request a reset */
60 rps10_hostlist, /* Return list of supported hosts */
61 };
62
63 PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
64 static const PILPluginImports* PluginImports;
65 static PILPlugin* OurPlugin;
66 static PILInterface* OurInterface;
67 static StonithImports* OurImports;
68 static void* interfprivate;
69
70 #include "stonith_signal.h"
71 #define DOESNT_USE_STONITHKILLCOMM
72 #define DOESNT_USE_STONITHSCANLINE
73 #include "stonith_expect_helpers.h"
74
75 PIL_rc
76 PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
77
78 PIL_rc
PIL_PLUGIN_INIT(PILPlugin * us,const PILPluginImports * imports)79 PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
80 {
81 /* Force the compiler to do a little type checking */
82 (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
83
84 PluginImports = imports;
85 OurPlugin = us;
86
87 /* Register ourself as a plugin */
88 imports->register_plugin(us, &OurPIExports);
89
90 /* Register our interface implementation */
91 return imports->register_interface(us, PIL_PLUGINTYPE_S
92 , PIL_PLUGIN_S
93 , &rps10Ops
94 , NULL /*close */
95 , &OurInterface
96 , (void*)&OurImports
97 , &interfprivate);
98 }
99
100 /*
101 * This was written for a Western Telematic Inc. (WTI)
102 * Remote Power Switch - RPS-10M.
103 *
104 * It has a DB9 serial port, a Rotary Address Switch,
105 * and a pair of RJ-11 jacks for linking multiple switches
106 * together. The 'M' unit is a master unit which can control
107 * up to 9 additional slave units. (the master unit also has an
108 * A/C outlet, so you can control up to 10 devices)
109 *
110 * There are a set of dip switches. The default shipping configuration
111 * is with all dip switches down. I highly recommend that you flip
112 * switch #3 up, so that when the device is plugged in, the power
113 * to the unit comes on.
114 *
115 * The serial interface is fixed at 9600 BPS (well, you *CAN*
116 * select 2400 BPS with a dip switch, but why?) 8-N-1
117 *
118 * The ASCII command string is:
119 *
120 * ^B^X^X^B^X^Xac^M
121 *
122 * ^B^X^X^B^X^X "fixed password" prefix (CTRL-B CTRL-X ... )
123 * ^M the carriage return character
124 *
125 * a = 0-9 Indicates the address of the module to receive the command
126 * a = * Sends the command to all modules
127 *
128 * c = 0 Switch the AC outlet OFF
129 * Returns:
130 * Plug 0 Off
131 * Complete
132 *
133 * c = 1 Switch the AC outlet ON
134 * Returns:
135 * Plug 0 On
136 * Complete
137 *
138 * c = T Toggle AC OFF (delay) then back ON
139 * Returns:
140 * Plug 0 Off
141 * Plug 0 On
142 * Complete
143 *
144 * c = ? Read and display status of the selected module
145 * Returns:
146 * Plug 0 On # or Plug 0 Off
147 * Complete
148 *
149 * e.g. ^B^X^X^B^X^X0T^M toggles the power on plug 0 OFF and then ON
150 *
151 * 21 September 2000
152 * Eric Z. Ayers
153 * Computer Generation, Inc.
154 */
155
156 struct cntrlr_str {
157 char outlet_id; /* value 0-9, '*' */
158 char * node; /* name of the node attached to this outlet */
159 };
160
161 struct pluginDevice {
162 StonithPlugin sp;
163 const char * pluginid;
164 const char * idinfo;
165
166 int fd; /* FD open to the serial port */
167
168 char * device; /* Serial device name to use to communicate
169 to this RPS10
170 */
171
172 #define WTI_NUM_CONTROLLERS 10
173 struct cntrlr_str
174 controllers[WTI_NUM_CONTROLLERS];
175 /* one master switch can address 10 controllers */
176
177 /* Number of actually configured units */
178 int unit_count;
179
180 };
181
182 /* This string is used to identify this type of object in the config file */
183 static const char * pluginid = "WTI_RPS10";
184 static const char * NOTwtiid = "OBJECT DESTROYED: (WTI RPS-10)";
185
186 #include "stonith_config_xml.h"
187
188 #define XML_RPS10_SHORTDESC \
189 XML_PARM_SHORTDESC_BEGIN("en") \
190 "Value in the format \"serial_device remotenode outlet [remotenode outlet]...\"" \
191 XML_PARM_SHORTDESC_END
192
193 #define XML_RPS10_LONGDESC \
194 XML_PARM_LONGDESC_BEGIN("en") \
195 "The RPS-10 STONITH device configuration information in the format \"serial_device remotenode outlet [remotenode outlet]...\"" \
196 XML_PARM_LONGDESC_END
197
198 #define XML_RPS10_PARM \
199 XML_PARAMETER_BEGIN(ST_RPS10, "string", "1", "1") \
200 XML_RPS10_SHORTDESC \
201 XML_RPS10_LONGDESC \
202 XML_PARAMETER_END
203
204 static const char *rps10XML =
205 XML_PARAMETERS_BEGIN
206 XML_RPS10_PARM
207 XML_PARAMETERS_END;
208
209 /* WTIpassword - The fixed string ^B^X^X^B^X^X */
210 static const char WTIpassword[7] = {2,24,24,2,24,24,0};
211
212 /*
213 * Different expect strings that we get from the WTI_RPS10
214 * Remote Power Controllers...
215 */
216
217 static struct Etoken WTItokReady[] = { {"RPS-10 Ready", 0, 0}, {NULL,0,0}};
218 static struct Etoken WTItokComplete[] = { {"Complete", 0, 0} ,{NULL,0,0}};
219 static struct Etoken WTItokPlug[] = { {"Plug", 0, 0}, {NULL,0,0}};
220 static struct Etoken WTItokOutlet[] = { {"0", 0, 0},
221 {"1", 0, 0},
222 {"2", 0, 0},
223 {"3", 0, 0},
224 {"4", 0, 0},
225 {"5", 0, 0},
226 {"6", 0, 0},
227 {"7", 0, 0},
228 {"8", 0, 0},
229 {"9", 0, 0},
230 {NULL,0,0}};
231
232 static struct Etoken WTItokOff[] = { {"Off", 0, 0}, {NULL,0,0}};
233
234 /*
235 * Tokens currently not used because they don't show up on all RPS10 units:
236 *
237 */
238 static struct Etoken WTItokOn[] = { {"On", 0, 0}, {NULL,0,0}};
239
240 /* Accept either a CR/NL or an NL/CR */
241 static struct Etoken WTItokCRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};
242
243 static int RPSConnect(struct pluginDevice * ctx);
244 static int RPSDisconnect(struct pluginDevice * ctx);
245
246 static int RPSReset(struct pluginDevice*, char unit_id, const char * rebootid);
247 #if defined(ST_POWERON)
248 static int RPSOn(struct pluginDevice*, char unit_id, const char * rebootid);
249 #endif
250 #if defined(ST_POWEROFF)
251 static int RPSOff(struct pluginDevice*, char unit_id, const char * rebootid);
252 #endif
253 static signed char RPSNametoOutlet ( struct pluginDevice * ctx, const char * host );
254
255 static int RPS_parse_config_info(struct pluginDevice* ctx, const char * info);
256
257 #define SENDCMD(outlet, cmd, timeout) { \
258 int ret_val = RPSSendCommand(ctx, outlet, cmd, timeout);\
259 if (ret_val != S_OK) { \
260 return ret_val; \
261 } \
262 }
263
264 /*
265 * RPSSendCommand - send a command to the specified outlet
266 */
267 static int
RPSSendCommand(struct pluginDevice * ctx,char outlet,char command,int timeout)268 RPSSendCommand (struct pluginDevice *ctx, char outlet, char command, int timeout)
269 {
270 char writebuf[10]; /* all commands are 9 chars long! */
271 int return_val; /* system call result */
272 fd_set rfds, wfds, xfds;
273 struct timeval tv; /* */
274
275 /* list of FDs for select() */
276 if (Debug) {
277 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
278 }
279
280 FD_ZERO(&rfds);
281 FD_ZERO(&wfds);
282 FD_ZERO(&xfds);
283
284 snprintf (writebuf, sizeof(writebuf), "%s%c%c\r",
285 WTIpassword, outlet, command);
286
287 if (Debug) {
288 LOG(PIL_DEBUG, "Sending %s\n", writebuf);
289 }
290
291 /* Make sure the serial port won't block on us. use select() */
292 FD_SET(ctx->fd, &wfds);
293 FD_SET(ctx->fd, &xfds);
294
295 tv.tv_sec = timeout;
296 tv.tv_usec = 0;
297
298 return_val = select(ctx->fd+1, NULL, &wfds,&xfds, &tv);
299 if (return_val == 0) {
300 /* timeout waiting on serial port */
301 LOG(PIL_CRIT, "%s: Timeout writing to %s",
302 pluginid, ctx->device);
303 return S_TIMEOUT;
304 } else if ((return_val == -1) || FD_ISSET(ctx->fd, &xfds)) {
305 /* an error occured */
306 LOG(PIL_CRIT, "%s: Error before writing to %s: %s",
307 pluginid, ctx->device, strerror(errno));
308 return S_OOPS;
309 }
310
311 /* send the command */
312 if (write(ctx->fd, writebuf, strlen(writebuf)) !=
313 (int)strlen(writebuf)) {
314 LOG(PIL_CRIT, "%s: Error writing to %s : %s",
315 pluginid, ctx->device, strerror(errno));
316 return S_OOPS;
317 }
318
319 /* suceeded! */
320 return S_OK;
321
322 } /* end RPSSendCommand() */
323
324 /*
325 * RPSReset - Reset (power-cycle) the given outlet id
326 */
327 static int
RPSReset(struct pluginDevice * ctx,char unit_id,const char * rebootid)328 RPSReset(struct pluginDevice* ctx, char unit_id, const char * rebootid)
329 {
330
331 if (Debug) {
332 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
333 }
334
335 if (ctx->fd < 0) {
336 LOG(PIL_CRIT, "%s: device %s is not open!", pluginid,
337 ctx->device);
338 return S_OOPS;
339 }
340
341 /* send the "toggle power" command */
342 SENDCMD(unit_id, 'T', 10);
343
344 /* Expect "Plug 0 Off" */
345 /* Note: If asked to control "*", the RPS10 will report all units it
346 * separately; however, we don't know how many, so we can only wait
347 * for the first unit to report something and then wait until the
348 * "Complete" */
349 EXPECT(ctx->fd, WTItokPlug, 5);
350 if (Debug) {
351 LOG(PIL_DEBUG, "Got Plug\n");
352 }
353 EXPECT(ctx->fd, WTItokOutlet, 2);
354 if (Debug) {
355 LOG(PIL_DEBUG, "Got Outlet #\n");
356 }
357 EXPECT(ctx->fd, WTItokOff, 2);
358 if (Debug) {
359 LOG(PIL_DEBUG, "Got Off\n");
360 }
361 EXPECT(ctx->fd, WTItokCRNL, 2);
362 LOG(PIL_INFO, "Host is being rebooted: %s", rebootid);
363
364 /* Expect "Complete" */
365 EXPECT(ctx->fd, WTItokComplete, 14);
366 if (Debug) {
367 LOG(PIL_DEBUG, "Got Complete\n");
368 }
369 EXPECT(ctx->fd, WTItokCRNL, 2);
370 if (Debug) {
371 LOG(PIL_DEBUG, "Got NL\n");
372 }
373
374 return(S_OK);
375
376 } /* end RPSReset() */
377
378
379 #if defined(ST_POWERON)
380 /*
381 * RPSOn - Turn OFF the given outlet id
382 */
383 static int
RPSOn(struct pluginDevice * ctx,char unit_id,const char * host)384 RPSOn(struct pluginDevice* ctx, char unit_id, const char * host)
385 {
386 if (Debug) {
387 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
388 }
389
390 if (ctx->fd < 0) {
391 LOG(PIL_CRIT, "%s: device %s is not open!", pluginid,
392 ctx->device);
393 return S_OOPS;
394 }
395
396 /* send the "On" command */
397 SENDCMD(unit_id, '1', 10);
398
399 /* Expect "Plug 0 On" */
400 EXPECT(ctx->fd, WTItokPlug, 5);
401 EXPECT(ctx->fd, WTItokOutlet, 2);
402 EXPECT(ctx->fd, WTItokOn, 2);
403 EXPECT(ctx->fd, WTItokCRNL, 2);
404 LOG(PIL_INFO, "Host is being turned on: %s", host);
405
406 /* Expect "Complete" */
407 EXPECT(ctx->fd, WTItokComplete, 5);
408 EXPECT(ctx->fd, WTItokCRNL, 2);
409
410 return(S_OK);
411
412 } /* end RPSOn() */
413 #endif
414
415
416 #if defined(ST_POWEROFF)
417 /*
418 * RPSOff - Turn Off the given outlet id
419 */
420 static int
RPSOff(struct pluginDevice * ctx,char unit_id,const char * host)421 RPSOff(struct pluginDevice* ctx, char unit_id, const char * host)
422 {
423
424 if (Debug) {
425 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
426 }
427
428 if (ctx->fd < 0) {
429 LOG(PIL_CRIT, "%s: device %s is not open!", pluginid,
430 ctx->device);
431 return S_OOPS;
432 }
433
434 /* send the "Off" command */
435 SENDCMD(unit_id, '0', 10);
436
437 /* Expect "Plug 0 Off" */
438 EXPECT(ctx->fd, WTItokPlug, 5);
439 EXPECT(ctx->fd, WTItokOutlet, 2);
440 EXPECT(ctx->fd, WTItokOff, 2);
441 EXPECT(ctx->fd, WTItokCRNL, 2);
442 LOG(PIL_INFO, "Host is being turned on: %s", host);
443
444 /* Expect "Complete" */
445 EXPECT(ctx->fd, WTItokComplete, 5);
446 EXPECT(ctx->fd, WTItokCRNL, 2);
447
448 return(S_OK);
449
450 } /* end RPSOff() */
451 #endif
452
453
454 /*
455 * rps10_status - API entry point to probe the status of the stonith device
456 * (basically just "is it reachable and functional?", not the
457 * status of the individual outlets)
458 *
459 * Returns:
460 * S_OOPS - some error occured
461 * S_OK - if the stonith device is reachable and online.
462 */
463 static int
rps10_status(StonithPlugin * s)464 rps10_status(StonithPlugin *s)
465 {
466 struct pluginDevice* ctx;
467
468 if (Debug) {
469 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
470 }
471
472 ERRIFNOTCONFIGED(s,S_OOPS);
473
474 ctx = (struct pluginDevice*) s;
475 if (RPSConnect(ctx) != S_OK) {
476 return(S_OOPS);
477 }
478
479 /* The "connect" really does enough work to see if the
480 controller is alive... It verifies that it is returning
481 RPS-10 Ready
482 */
483
484 return(RPSDisconnect(ctx));
485 }
486
487 /*
488 * rps10_hostlist - API entry point to return the list of hosts
489 * for the devices on this WTI_RPS10 unit
490 *
491 * This type of device is configured from the config file,
492 * so we don't actually have to connect to figure this
493 * out, just peruse the 'ctx' structure.
494 * Returns:
495 * NULL on error
496 * a malloced array, terminated with a NULL,
497 * of null-terminated malloc'ed strings.
498 */
499 static char **
rps10_hostlist(StonithPlugin * s)500 rps10_hostlist(StonithPlugin *s)
501 {
502 char ** ret = NULL; /* list to return */
503 int i;
504 int j;
505 struct pluginDevice* ctx;
506
507 if (Debug) {
508 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
509 }
510
511 ERRIFNOTCONFIGED(s,NULL);
512
513 ctx = (struct pluginDevice*) s;
514
515 if (ctx->unit_count >= 1) {
516 ret = (char **)MALLOC((ctx->unit_count+1)*sizeof(char*));
517 if (ret == NULL) {
518 LOG(PIL_CRIT, "out of memory");
519 return ret;
520 }
521 ret[ctx->unit_count]=NULL; /* null terminate the array */
522 for (i=0; i < ctx->unit_count; i++) {
523 ret[i] = STRDUP(ctx->controllers[i].node);
524 if (ret[i] == NULL) {
525 for(j=0; j<i; j++) {
526 FREE(ret[j]);
527 }
528 FREE(ret); ret = NULL;
529 break;
530 }
531 } /* end for each possible outlet */
532 } /* end if any outlets are configured */
533 return(ret);
534 } /* end si_hostlist() */
535
536 /*
537 * Parse the given configuration information, and stash
538 * it away...
539 *
540 * The format of <info> for this module is:
541 * <serial device> <remotenode> <outlet> [<remotenode> <outlet>] ...
542 *
543 * e.g. A machine named 'nodea' can kill a machine named 'nodeb' through
544 * a device attached to serial port /dev/ttyS0.
545 * A machine named 'nodeb' can kill machines 'nodea' and 'nodec'
546 * through a device attached to serial port /dev/ttyS1 (outlets 0
547 * and 1 respectively)
548 *
549 * <assuming this is the heartbeat configuration syntax:>
550 *
551 * stonith nodea rps10 /dev/ttyS0 nodeb 0
552 * stonith nodeb rps10 /dev/ttyS0 nodea 0 nodec 1
553 *
554 * Another possible configuration is for 2 stonith devices
555 * accessible through 2 different serial ports on nodeb:
556 *
557 * stonith nodeb rps10 /dev/ttyS0 nodea 0
558 * stonith nodeb rps10 /dev/ttyS1 nodec 0
559 */
560
561 /*
562 * OOPS!
563 *
564 * Most of the large block of comments above is incorrect as far as this
565 * module is concerned. It is somewhat applicable to the heartbeat code,
566 * but not to this Stonith module.
567 *
568 * The format of parameter string for this module is:
569 * <serial device> <remotenode> <outlet> [<remotenode> <outlet>] ...
570 */
571
572 static int
RPS_parse_config_info(struct pluginDevice * ctx,const char * info)573 RPS_parse_config_info(struct pluginDevice* ctx, const char * info)
574 {
575 char *copy;
576 char *token;
577 char *outlet, *node;
578
579 if (Debug) {
580 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
581 }
582
583 /* strtok() is nice to use to parse a string with
584 (other than it isn't threadsafe), but it is destructive, so
585 we're going to alloc our own private little copy for the
586 duration of this function.
587 */
588
589 copy = STRDUP(info);
590 if (!copy) {
591 LOG(PIL_CRIT, "out of memory");
592 return S_OOPS;
593 }
594
595 /* Grab the serial device */
596 token = strtok (copy, " \t");
597
598 if (!token) {
599 LOG(PIL_CRIT, "%s: Can't find serial device on config line '%s'",
600 pluginid, info);
601 goto token_error;
602 }
603
604 ctx->device = STRDUP(token);
605 if (!ctx->device) {
606 LOG(PIL_CRIT, "out of memory");
607 goto token_error;
608 }
609
610 /* Loop through the rest of the command line which should consist of */
611 /* <nodename> <outlet> pairs */
612 while ((node = strtok (NULL, " \t"))
613 && (outlet = strtok (NULL, " \t\n"))) {
614 char outlet_id;
615
616 /* validate the outlet token */
617 if ((sscanf (outlet, "%c", &outlet_id) != 1)
618 || !( ((outlet_id >= '0') && (outlet_id <= '9'))
619 || (outlet_id == '*') || (outlet_id == 'A') )
620 ) {
621 LOG(PIL_CRIT
622 , "%s: the outlet_id %s must be between"
623 " 0 and 9 or '*' / 'A'",
624 pluginid, outlet);
625 goto token_error;
626 }
627
628 if (outlet_id == 'A') {
629 /* Remap 'A' to '*'; in some configurations,
630 * a '*' can't be configured because it breaks
631 * scripts -- lmb */
632 outlet_id = '*';
633 }
634
635 if (ctx->unit_count >= WTI_NUM_CONTROLLERS) {
636 LOG(PIL_CRIT,
637 "%s: Tried to configure too many controllers",
638 pluginid);
639 goto token_error;
640 }
641
642 ctx->controllers[ctx->unit_count].node = STRDUP(node);
643 strdown(ctx->controllers[ctx->unit_count].node);
644 ctx->controllers[ctx->unit_count].outlet_id = outlet_id;
645 ctx->unit_count++;
646
647 }
648
649 /* free our private copy of the string we've been destructively
650 * parsing with strtok()
651 */
652 FREE(copy);
653 return ((ctx->unit_count > 0) ? S_OK : S_BADCONFIG);
654
655 token_error:
656 FREE(copy);
657 if (ctx->device) {
658 FREE(ctx->device);
659 ctx->device = NULL;
660 }
661 return(S_BADCONFIG);
662 }
663
664
665 /*
666 * dtrtoggle - toggle DTR on the serial port
667 *
668 * snarfed from minicom, sysdep1.c, a well known POSIX trick.
669 *
670 */
dtrtoggle(int fd)671 static void dtrtoggle(int fd) {
672 struct termios tty, old;
673 int sec = 2;
674
675 if (Debug) {
676 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
677 }
678
679 tcgetattr(fd, &tty);
680 tcgetattr(fd, &old);
681 cfsetospeed(&tty, B0);
682 cfsetispeed(&tty, B0);
683 tcsetattr(fd, TCSANOW, &tty);
684 if (sec>0) {
685 sleep(sec);
686 tcsetattr(fd, TCSANOW, &old);
687 }
688
689 if (Debug) {
690 LOG(PIL_DEBUG, "dtrtoggle Complete (%s)\n", pluginid);
691 }
692 }
693
694 /*
695 * RPSConnect -
696 *
697 * Connect to the given WTI_RPS10 device.
698 * Side Effects
699 * DTR on the serial port is toggled
700 * ctx->fd now contains a valid file descriptor to the serial port
701 * ??? LOCK THE SERIAL PORT ???
702 *
703 * Returns
704 * S_OK on success
705 * S_OOPS on error
706 * S_TIMEOUT if the device did not respond
707 *
708 */
709 static int
RPSConnect(struct pluginDevice * ctx)710 RPSConnect(struct pluginDevice * ctx)
711 {
712 if (Debug) {
713 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
714 }
715
716 /* Open the serial port if it isn't already open */
717 if (ctx->fd < 0) {
718 struct termios tio;
719
720 if (OurImports->TtyLock(ctx->device) < 0) {
721 LOG(PIL_CRIT, "%s: TtyLock failed.", pluginid);
722 return S_OOPS;
723 }
724
725 ctx->fd = open (ctx->device, O_RDWR);
726 if (ctx->fd <0) {
727 LOG(PIL_CRIT, "%s: Can't open %s : %s",
728 pluginid, ctx->device, strerror(errno));
729 return S_OOPS;
730 }
731
732 /* set the baudrate to 9600 8 - N - 1 */
733 memset (&tio, 0, sizeof(tio));
734
735 /* ??? ALAN - the -tradtitional flag on gcc causes the
736 CRTSCTS constant to generate a warning, and warnings
737 are treated as errors, so I can't set this flag! - EZA ???
738
739 Hmmm. now that I look at the documentation, RTS
740 is just wired high on this device! we don't need it.
741 */
742 /* tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD | CRTSCTS ;*/
743 tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD ;
744 tio.c_lflag = ICANON;
745
746 if (tcsetattr (ctx->fd, TCSANOW, &tio) < 0) {
747 LOG(PIL_CRIT, "%s: Can't set attributes %s : %s",
748 pluginid, ctx->device, strerror(errno));
749 close (ctx->fd);
750 OurImports->TtyUnlock(ctx->device);
751 ctx->fd=-1;
752 return S_OOPS;
753 }
754 /* flush all data to and fro the serial port before we start */
755 if (tcflush (ctx->fd, TCIOFLUSH) < 0) {
756 LOG(PIL_CRIT, "%s: Can't flush %s : %s",
757 pluginid, ctx->device, strerror(errno));
758 close (ctx->fd);
759 OurImports->TtyUnlock(ctx->device);
760 ctx->fd=-1;
761 return S_OOPS;
762 }
763
764 }
765
766 /* Toggle DTR - this 'resets' the controller serial port interface
767 In minicom, try CTRL-A H to hangup and you can see this behavior.
768 */
769 dtrtoggle(ctx->fd);
770
771 /* Wait for the switch to respond with "RPS-10 Ready".
772 Emperically, this usually takes 5-10 seconds...
773 ... If this fails, this may be a hint that you got
774 a broken serial cable, which doesn't connect hardware
775 flow control.
776 */
777 if (Debug) {
778 LOG(PIL_DEBUG, "Waiting for READY\n");
779 }
780 EXPECT(ctx->fd, WTItokReady, 12);
781 if (Debug) {
782 LOG(PIL_DEBUG, "Got READY\n");
783 }
784 EXPECT(ctx->fd, WTItokCRNL, 2);
785 if (Debug) {
786 LOG(PIL_DEBUG, "Got NL\n");
787 }
788
789 return(S_OK);
790 }
791
792 static int
RPSDisconnect(struct pluginDevice * ctx)793 RPSDisconnect(struct pluginDevice * ctx)
794 {
795
796 if (Debug) {
797 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
798 }
799
800 if (ctx->fd >= 0) {
801 /* Flush the serial port, we don't care what happens to the
802 * characters and failing to do this can cause close to hang.
803 */
804 tcflush(ctx->fd, TCIOFLUSH);
805 close (ctx->fd);
806 if (ctx->device != NULL) {
807 OurImports->TtyUnlock(ctx->device);
808 }
809 }
810 ctx->fd = -1;
811
812 return S_OK;
813 }
814
815 /*
816 * RPSNametoOutlet - Map a hostname to an outlet on this stonith device.
817 *
818 * Returns:
819 * 0-9, * on success ( the outlet id on the RPS10 )
820 * -1 on failure (host not found in the config file)
821 *
822 */
823 static signed char
RPSNametoOutlet(struct pluginDevice * ctx,const char * host)824 RPSNametoOutlet ( struct pluginDevice * ctx, const char * host )
825 {
826 int i=0;
827
828 if (Debug) {
829 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
830 }
831
832 /* scan the controllers[] array to see if this host is there */
833 for (i=0;i<ctx->unit_count;i++) {
834 /* return the outlet id */
835 if ( ctx->controllers[i].node
836 && !strcasecmp(host, ctx->controllers[i].node)) {
837 /* found it! */
838 break;
839 }
840 }
841
842 if (i == ctx->unit_count) {
843 return -1;
844 } else {
845 return ctx->controllers[i].outlet_id;
846 }
847 }
848
849
850 /*
851 * rps10_reset - API call to Reset (reboot) the given host on
852 * this Stonith device. This involves toggling the power off
853 * and then on again, OR just calling the builtin reset command
854 * on the stonith device.
855 */
856 static int
rps10_reset_req(StonithPlugin * s,int request,const char * host)857 rps10_reset_req(StonithPlugin * s, int request, const char * host)
858 {
859 int rc = S_OK;
860 int lorc = S_OK;
861 signed char outlet_id = -1;
862 struct pluginDevice* ctx;
863
864 if (Debug) {
865 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
866 }
867
868 ERRIFNOTCONFIGED(s,S_OOPS);
869
870 ctx = (struct pluginDevice*) s;
871
872 if ((rc = RPSConnect(ctx)) != S_OK) {
873 return(rc);
874 }
875
876 outlet_id = RPSNametoOutlet(ctx, host);
877
878 if (outlet_id < 0) {
879 LOG(PIL_WARN, "%s: %s doesn't control host [%s]"
880 , pluginid, ctx->device, host );
881 RPSDisconnect(ctx);
882 return(S_BADHOST);
883 }
884
885 switch(request) {
886
887 #if defined(ST_POWERON)
888 case ST_POWERON:
889 rc = RPSOn(ctx, outlet_id, host);
890 break;
891 #endif
892 #if defined(ST_POWEROFF)
893 case ST_POWEROFF:
894 rc = RPSOff(ctx, outlet_id, host);
895 break;
896 #endif
897 case ST_GENERIC_RESET:
898 rc = RPSReset(ctx, outlet_id, host);
899 break;
900 default:
901 rc = S_INVAL;
902 break;
903 }
904
905 lorc = RPSDisconnect(ctx);
906
907 return(rc != S_OK ? rc : lorc);
908 }
909
910 /*
911 * Parse the information in the given string,
912 * and stash it away...
913 */
914 static int
rps10_set_config(StonithPlugin * s,StonithNVpair * list)915 rps10_set_config(StonithPlugin* s, StonithNVpair* list)
916 {
917 struct pluginDevice* ctx;
918 StonithNamesToGet namestocopy [] =
919 { {ST_RPS10, NULL}
920 , {NULL, NULL}
921 };
922 int rc=0;
923
924 if (Debug) {
925 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
926 }
927
928 ERRIFWRONGDEV(s,S_OOPS);
929
930 if (s->isconfigured) {
931 /* The module is already configured. */
932 return(S_OOPS);
933 }
934
935 ctx = (struct pluginDevice*) s;
936
937 if((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK){
938 LOG(PIL_DEBUG , "get all calues failed");
939 return rc;
940 }
941
942 rc = RPS_parse_config_info(ctx, namestocopy[0].s_value);
943 FREE(namestocopy[0].s_value);
944 return rc;
945 }
946
947 /*
948 * Return the Stonith plugin configuration parameter
949 *
950 */
951 static const char * const *
rps10_get_confignames(StonithPlugin * p)952 rps10_get_confignames(StonithPlugin* p)
953 {
954 static const char * Rps10Params[] = {ST_RPS10 ,NULL };
955
956 if (Debug) {
957 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
958 }
959
960 return Rps10Params;
961 }
962
963 /*
964 * rps10_getinfo - API entry point to retrieve something from the handle
965 */
966 static const char *
rps10_getinfo(StonithPlugin * s,int reqtype)967 rps10_getinfo(StonithPlugin * s, int reqtype)
968 {
969 struct pluginDevice* ctx;
970 const char * ret;
971
972 if (Debug) {
973 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
974 }
975
976 ERRIFWRONGDEV(s,NULL);
977
978 /*
979 * We look in the ST_TEXTDOMAIN catalog for our messages
980 */
981 ctx = (struct pluginDevice *)s;
982
983 switch (reqtype) {
984 case ST_DEVICEID:
985 ret = ctx->idinfo;
986 break;
987 case ST_DEVICENAME:
988 ret = ctx->device;
989 break;
990 case ST_DEVICEDESCR:
991 ret = "Western Telematic Inc. (WTI) "
992 "Remote Power Switch - RPS-10M.\n";
993 break;
994 case ST_DEVICEURL:
995 ret = "http://www.wti.com/";
996 break;
997 case ST_CONF_XML: /* XML metadata */
998 ret = rps10XML;
999 break;
1000 default:
1001 ret = NULL;
1002 break;
1003 }
1004 return ret;
1005 }
1006
1007 /*
1008 * rps10_destroy - API entry point to destroy a WTI_RPS10 Stonith object.
1009 */
1010 static void
rps10_destroy(StonithPlugin * s)1011 rps10_destroy(StonithPlugin *s)
1012 {
1013 struct pluginDevice* ctx;
1014 int i;
1015
1016 if (Debug) {
1017 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
1018 }
1019
1020 VOIDERRIFWRONGDEV(s);
1021
1022 ctx = (struct pluginDevice *)s;
1023
1024 ctx->pluginid = NOTwtiid;
1025
1026 /* close the fd if open and set ctx->fd to invalid */
1027 RPSDisconnect(ctx);
1028
1029 if (ctx->device != NULL) {
1030 FREE(ctx->device);
1031 ctx->device = NULL;
1032 }
1033 if (ctx->unit_count > 0) {
1034 for (i = 0; i < ctx->unit_count; i++) {
1035 if (ctx->controllers[i].node != NULL) {
1036 FREE(ctx->controllers[i].node);
1037 ctx->controllers[i].node = NULL;
1038 }
1039 }
1040 }
1041 FREE(ctx);
1042 }
1043
1044 /*
1045 * rps10_new - API entry point called to create a new WTI_RPS10 Stonith device
1046 * object.
1047 */
1048 static StonithPlugin *
rps10_new(const char * subplugin)1049 rps10_new(const char *subplugin)
1050 {
1051 struct pluginDevice* ctx = ST_MALLOCT(struct pluginDevice);
1052
1053 if (Debug) {
1054 LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
1055 }
1056
1057 if (ctx == NULL) {
1058 LOG(PIL_CRIT, "out of memory");
1059 return(NULL);
1060 }
1061 memset(ctx, 0, sizeof(*ctx));
1062 ctx->pluginid = pluginid;
1063 ctx->fd = -1;
1064 ctx->unit_count = 0;
1065 ctx->device = NULL;
1066 ctx->idinfo = DEVICE;
1067 ctx->sp.s_ops = &rps10Ops;
1068
1069 return &(ctx->sp);
1070 }
1071