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