📄 rps10.c
字号:
/* $Id: rps10.c,v 1.8.2.2 2004/09/14 16:43:46 alan Exp $ *//* * Stonith module for WTI Remote Power Controllers (RPS-10M device) * * Original code from baytech.c by * Copyright (c) 2000 Alan Robertson <alanr@unix.sh> * * Modifications for WTI RPS10 * Copyright (c) 2000 Computer Generation Incorporated * Eric Z. Ayers <eric.ayers@compgen.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <portability.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <syslog.h>#include <libintl.h>#include <sys/wait.h>#include <termios.h>#include <sys/time.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <glib.h>#include <stonith/stonith.h>#define PIL_PLUGINTYPE STONITH_TYPE#define PIL_PLUGINTYPE_S STONITH_TYPE_S#define PIL_PLUGIN rps10#define PIL_PLUGIN_S "rps10"#define PIL_PLUGINLICENSE LICENSE_LGPL#define PIL_PLUGINLICENSEURL URL_LGPL#include <pils/plugin.h>/* * rps10close is called as part of unloading the rps10 STONITH plugin. * If there was any global data allocated, or file descriptors opened, etc. * which is associated with the plugin, and not a single interface * in particular, here's our chance to clean it up. */static voidrps10closepi(PILPlugin*pi){}/* * rps10closeintf called as part of shutting down the rps10 STONITH * interface. If there was any global data allocated, or file descriptors * opened, etc. which is associated with the rps10 implementation, * here's our chance to clean it up. */static PIL_rcrps10closeintf(PILInterface* pi, void* pd){ return PIL_OK;}static void * rps10_new(void);static void rps10_destroy(Stonith *);static int rps10_set_config_file(Stonith *, const char * cfgname);static int rps10_set_config_info(Stonith *, const char * info);static const char * rps10_getinfo(Stonith * s, int InfoType);static int rps10_status(Stonith * );static int rps10_reset_req(Stonith * s, int request, const char * host);static char ** rps10_hostlist(Stonith *);static void rps10_free_hostlist(char **);static struct stonith_ops rps10Ops ={ rps10_new, /* Create new STONITH object */ rps10_destroy, /* Destroy STONITH object */ rps10_set_config_file, /* set configuration from file */ rps10_set_config_info, /* Get configuration from file */ rps10_getinfo, /* Return STONITH info string */ rps10_status, /* Return STONITH device status */ rps10_reset_req, /* Request a reset */ rps10_hostlist, /* Return list of supported hosts */ rps10_free_hostlist /* free above list */};PIL_PLUGIN_BOILERPLATE("1.0", Debug, rps10closepi);static const PILPluginImports* PluginImports;static PILPlugin* OurPlugin;static PILInterface* OurInterface;static StonithImports* OurImports;static void* interfprivate;#define LOG PluginImports->log#define MALLOC PluginImports->alloc#define STRDUP PluginImports->mstrdup#define FREE PluginImports->mfree#define EXPECT_TOK OurImports->ExpectToken#define STARTPROC OurImports->StartProcessPIL_rcPIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);PIL_rcPIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports){ /* Force the compiler to do a little type checking */ (void)(PILPluginInitFun)PIL_PLUGIN_INIT; PluginImports = imports; OurPlugin = us; /* Register ourself as a plugin */ imports->register_plugin(us, &OurPIExports); /* Register our interface implementation */ return imports->register_interface(us, PIL_PLUGINTYPE_S , PIL_PLUGIN_S , &rps10Ops , rps10closeintf /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); }#define DEVICE "WTI RPS10 Power Switch"#define N_(text) (text)#define _(text) dgettext(ST_TEXTDOMAIN, text)/* * This was written for a Western Telematic Inc. (WTI) * Remote Power Switch - RPS-10M. * * It has a DB9 serial port, a Rotary Address Switch, * and a pair of RJ-11 jacks for linking multiple switches * together. The 'M' unit is a master unit which can control * up to 9 additional slave units. (the master unit also has an * A/C outlet, so you can control up to 10 devices) * * There are a set of dip switches. The default shipping configuration * is with all dip switches down. I highly recommend that you flip * switch #3 up, so that when the device is plugged in, the power * to the unit comes on. * * The serial interface is fixed at 9600 BPS (well, you *CAN* * select 2400 BPS with a dip switch, but why?) 8-N-1 * * The ASCII command string is: * * ^B^X^X^B^X^Xac^M * * ^B^X^X^B^X^X "fixed password" prefix (CTRL-B CTRL-X ... ) * ^M the carriage return character * * a = 0-9 Indicates the address of the module to receive the command * a = * Sends the command to all modules * * c = 0 Switch the AC outlet OFF * Returns: * Plug 0 Off * Complete * * c = 1 Switch the AC outlet ON * Returns: * Plug 0 On * Complete * * c = T Toggle AC OFF (delay) then back ON * Returns: * Plug 0 Off * Plug 0 On * Complete * * c = ? Read and display status of the selected module * Returns: * Plug 0 On # or Plug 0 Off * Complete * * e.g. ^B^X^X^B^X^X0T^M toggles the power on plug 0 OFF and then ON * * 21 September 2000 * Eric Z. Ayers * Computer Generation, Inc. */struct cntrlr_str { char outlet_id; /* value 0-9, '*' */ char * node; /* name of the node attached to this outlet */};struct WTI_RPS10 { const char * WTIid; char * idinfo; /* ??? What's this for Alan ??? */ char * unitid; /* ??? What's this for Alan ??? */ int fd; /* FD open to the serial port */ int config; /* 0 if not configured, 1 if configured with rps10_set_config_info() or rps10_set_config_file() */ char * device; /* Serial device name to use to communicate to this RPS10 */#define WTI_NUM_CONTROLLERS 10 struct cntrlr_str controllers[WTI_NUM_CONTROLLERS]; /* one master switch can address 10 controllers */ /* Number of actually configured units */ int unit_count;};/* This string is used to identify this type of object in the config file */static const char * WTIid = "WTI_RPS10";static const char * NOTwtiid = "OBJECT DESTROYED: (WTI RPS-10)";/* WTIpassword - The fixed string ^B^X^X^B^X^X */static const char WTIpassword[7] = {2,24,24,2,24,24,0}; #ifndef DEBUG#define DEBUG 0#endifstatic int gbl_debug = DEBUG;#define ISWTIRPS10(i) (((i)!= NULL && (i)->pinfo != NULL) \ && ((struct WTI_RPS10 *)(i->pinfo))->WTIid == WTIid)#define ISCONFIGED(i) (ISWTIRPS10(i) && ((struct WTI_RPS10 *)(i->pinfo))->config)#ifndef MALLOCT# define MALLOCT(t) ((t *)(MALLOC(sizeof(t)))) #endif#define WHITESPACE " \t\n\r\f"#define REPLSTR(s,v) { \ if ((s) != NULL) { \ FREE(s); \ (s)=NULL; \ } \ (s) = STRDUP(v); \ if ((s) == NULL) { \ syslog(LOG_ERR, _("out of memory"));\ } \ }/* * Different expect strings that we get from the WTI_RPS10 * Remote Power Controllers... */static struct Etoken WTItokReady[] = { {"RPS-10 Ready", 0, 0}, {NULL,0,0}};static struct Etoken WTItokComplete[] = { {"Complete", 0, 0} ,{NULL,0,0}};static struct Etoken WTItokPlug[] = { {"Plug", 0, 0}, {NULL,0,0}};static struct Etoken WTItokOutlet[] = { {"0", 0, 0}, {"1", 0, 0}, {"2", 0, 0}, {"3", 0, 0}, {"4", 0, 0}, {"5", 0, 0}, {"6", 0, 0}, {"7", 0, 0}, {"8", 0, 0}, {"9", 0, 0}, {NULL,0,0}};static struct Etoken WTItokOff[] = { {"Off", 0, 0}, {NULL,0,0}};/* * Tokens currently not used because they don't show up on all RPS10 units: * */static struct Etoken WTItokOn[] = { {"On", 0, 0}, {NULL,0,0}};/* Accept either a CR/NL or an NL/CR */static struct Etoken WTItokCRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};static int RPSConnect(struct WTI_RPS10 * ctx);static int RPSDisconnect(struct WTI_RPS10 * ctx);static int RPSReset(struct WTI_RPS10*, char unit_id, const char * rebootid);#if defined(ST_POWERON) static int RPSOn(struct WTI_RPS10*, char unit_id, const char * rebootid);#endif#if defined(ST_POWEROFF) static int RPSOff(struct WTI_RPS10*, char unit_id, const char * rebootid);#endifstatic signed char RPSNametoOutlet ( struct WTI_RPS10 * ctx, const char * host );static int RPS_parse_config_info(struct WTI_RPS10* ctx, const char * info);/* * We do these things a lot. Here are a few shorthand macros. */#define SEND(outlet, cmd, timeout) { \ int return_val = RPSSendCommand(ctx, outlet, cmd, timeout); \ if (return_val != S_OK) return return_val; \}#define EXPECT(p,t) { \ if (RPSLookFor(ctx, p, t) < 0) \ return(errno == ETIMEDOUT \ ? S_TIMEOUT : S_OOPS); \ }#ifdef WEDONTUSETHESE#define NULLEXPECT(p,t) { \ if (RPSLookFor(ctx, p, t) < 0) \ return(NULL); \ }#define SNARF(s, to) { \ if (RPSScanLine(ctx,to,(s),sizeof(s)) \ != S_OK) \ return(S_OOPS); \ }#define NULLSNARF(s, to) { \ if (RPSScanLine(ctx,to,(s),sizeof(s)) \ != S_OK) \ return(NULL); \ }#endif/* Look for any of the given patterns. We don't care which */static intRPSLookFor(struct WTI_RPS10* ctx, struct Etoken * tlist, int timeout){ int rc; if ((rc = EXPECT_TOK(ctx->fd, tlist, timeout, NULL, 0)) < 0) { syslog(LOG_ERR, _("Did not find string: '%s' from " DEVICE ".") , tlist[0].string); RPSDisconnect(ctx); return(-1); } return(rc);}/* * RPSSendCommand - send a command to the specified outlet */static intRPSSendCommand (struct WTI_RPS10 *ctx, char outlet, char command, int timeout){ char writebuf[10]; /* all commands are 9 chars long! */ int return_val; /* system call result */ fd_set rfds, wfds, xfds; /* list of FDs for select() */ struct timeval tv; /* */ FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&xfds); snprintf (writebuf, sizeof(writebuf), "%s%c%c\r", WTIpassword, outlet, command); if (gbl_debug) printf ("Sending %s\n", writebuf); /* Make sure the serial port won't block on us. use select() */ FD_SET(ctx->fd, &wfds); FD_SET(ctx->fd, &xfds); tv.tv_sec = timeout; tv.tv_usec = 0; return_val = select(ctx->fd+1, NULL, &wfds,&xfds, &tv); if (return_val == 0) { /* timeout waiting on serial port */ syslog (LOG_ERR, "%s: Timeout writing to %s", WTIid, ctx->device); return S_TIMEOUT; } else if ((return_val == -1) || FD_ISSET(ctx->fd, &xfds)) { /* an error occured */ syslog (LOG_ERR, "%s: Error before writing to %s: %s", WTIid, ctx->device, strerror(errno)); return S_OOPS; } /* send the command */ if (write(ctx->fd, writebuf, strlen(writebuf)) != (int)strlen(writebuf)) { syslog (LOG_ERR, "%s: Error writing to %s : %s", WTIid, ctx->device, strerror(errno)); return S_OOPS; } /* suceeded! */ return S_OK;} /* end RPSSendCommand() *//* * RPSReset - Reset (power-cycle) the given outlet id */static intRPSReset(struct WTI_RPS10* ctx, char unit_id, const char * rebootid){ if (gbl_debug) printf ("Calling RPSReset (%s)\n", WTIid); if (ctx->fd < 0) { syslog(LOG_ERR, "%s: device %s is not open!", WTIid, ctx->device); return S_OOPS; } /* send the "toggle power" command */ SEND(unit_id, 'T', 10); /* Expect "Plug 0 Off" */ /* Note: If asked to control "*", the RPS10 will report all units it * separately; however, we don't know how many, so we can only wait * for the first unit to report something and then wait until the * "Complete" */ EXPECT(WTItokPlug, 5); if (gbl_debug) printf ("Got Plug\n"); EXPECT(WTItokOutlet, 2); if (gbl_debug) printf ("Got Outlet #\n"); EXPECT(WTItokOff, 2); if (gbl_debug) printf ("Got Off\n"); EXPECT(WTItokCRNL, 2); syslog(LOG_INFO, _("Host %s being rebooted."), rebootid); /* Expect "Complete" */ EXPECT(WTItokComplete, 14); if (gbl_debug) printf ("Got Complete\n"); EXPECT(WTItokCRNL, 2); if (gbl_debug) printf ("Got NL\n"); return(S_OK);} /* end RPSReset() */#if defined(ST_POWERON) /* * RPSOn - Turn OFF the given outlet id */static intRPSOn(struct WTI_RPS10* ctx, char unit_id, const char * host){ if (ctx->fd < 0) { syslog(LOG_ERR, "%s: device %s is not open!", WTIid, ctx->device); return S_OOPS; } /* send the "On" command */ SEND(unit_id, '1', 10); /* Expect "Plug 0 On" */ EXPECT(WTItokPlug, 5); EXPECT(WTItokOutlet, 2); EXPECT(WTItokOn, 2); EXPECT(WTItokCRNL, 2); syslog(LOG_INFO, _("Host %s being turned on."), host); /* Expect "Complete" */ EXPECT(WTItokComplete, 5); EXPECT(WTItokCRNL, 2); return(S_OK);} /* end RPSOn() */#endif#if defined(ST_POWEROFF) /* * RPSOff - Turn Off the given outlet id */static intRPSOff(struct WTI_RPS10* ctx, char unit_id, const char * host){ if (ctx->fd < 0) { syslog(LOG_ERR, "%s: device %s is not open!", WTIid, ctx->device); return S_OOPS; } /* send the "Off" command */ SEND(unit_id, '0', 10); /* Expect "Plug 0 Off" */ EXPECT(WTItokPlug, 5); EXPECT(WTItokOutlet, 2); EXPECT(WTItokOff, 2); EXPECT(WTItokCRNL, 2); syslog(LOG_INFO, _("Host %s being turned on."), host); /* Expect "Complete" */ EXPECT(WTItokComplete, 5); EXPECT(WTItokCRNL, 2); return(S_OK);} /* end RPSOff() */#endif/* * rps10_status - API entry point to probe the status of the stonith device * (basically just "is it reachable and functional?", not the * status of the individual outlets) * * Returns: * S_OOPS - some error occured * S_OK - if the stonith device is reachable and online. */static intrps10_status(Stonith *s){ struct WTI_RPS10* ctx; if (gbl_debug) printf ("Calling rps10_status (%s)\n", WTIid); if (!ISWTIRPS10(s)) { syslog(LOG_ERR, "invalid argument to RPS_status"); return(S_OOPS); } if (!ISCONFIGED(s)) { syslog(LOG_ERR , "unconfigured stonith object in RPS_status"); return(S_OOPS); } ctx = (struct WTI_RPS10*) s->pinfo; if (RPSConnect(ctx) != S_OK) { return(S_OOPS); } /* The "connect" really does enough work to see if the controller is alive... It verifies that it is returning RPS-10 Ready */ return(RPSDisconnect(ctx));}/* * rps10_hostlist - API entry point to return the list of hosts * for the devices on this WTI_RPS10 unit * * This type of device is configured from the config file, * so we don't actually have to connect to figure this * out, just peruse the 'ctx' structure. * Returns: * NULL on error * a malloced array, terminated with a NULL, * of null-terminated malloc'ed strings. */static char **rps10_hostlist(Stonith *s){ char ** ret = NULL; /* list to return */ int i; int j; struct WTI_RPS10* ctx;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -