~ampelbein/ubuntu/oneiric/heartbeat/lp-770743

« back to all changes in this revision

Viewing changes to lib/plugins/stonith/rps10.c

  • Committer: Bazaar Package Importer
  • Author(s): Ante Karamatic
  • Date: 2010-02-17 21:59:18 UTC
  • mfrom: (1.1.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20100217215918-06paxph5do4saw8v
Tags: 3.0.2-0ubuntu1
* New upstream release
* Drop hard dep on pacemaker for heartbet; moved to Recommends
* debian/heartbeat.install:
  - follow upstream changes
* debian/control:
  - added docbook-xsl and xsltproc to build depends

Show diffs side-by-side

added added

removed removed

Lines of Context:
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**     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
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") \
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
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
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
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
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
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 **
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
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
 
                g_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
 
 */
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
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
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
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
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
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**
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 *
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
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 * 
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
 
}