📄 baytech.c
字号:
/* $Id: baytech.c,v 1.13.2.1 2004/04/20 08:50:06 alan Exp $ *//* * Stonith module for BayTech Remote Power Controllers (RPC-x devices) * * Copyright (c) 2000 Alan Robertson <alanr@unix.sh> * * 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 <glib.h>#include <stonith/stonith.h>#define PIL_PLUGINTYPE STONITH_TYPE#define PIL_PLUGINTYPE_S STONITH_TYPE_S#define PIL_PLUGIN baytech#define PIL_PLUGIN_S "baytech"#define PIL_PLUGINLICENSE LICENSE_LGPL#define PIL_PLUGINLICENSEURL URL_LGPL#include <pils/plugin.h>#include "stonith_signal.h"/* * baytechclosepi is called as part of unloading the baytech 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 voidbaytechclosepi(PILPlugin*pi){}/* * baytechcloseintf called as part of shutting down the baytech STONITH * interface. If there was any global data allocated, or file descriptors * opened, etc. which is associated with the baytech implementation, * here's our chance to clean it up. */static PIL_rcbaytechcloseintf(PILInterface* pi, void* pd){ return PIL_OK;}static void * baytech_new(void);static void baytech_destroy(Stonith *);static int baytech_set_config_file(Stonith *, const char * cfgname);static int baytech_set_config_info(Stonith *, const char * info);static const char * baytech_getinfo(Stonith * s, int InfoType);static int baytech_status(Stonith * );static int baytech_reset_req(Stonith * s, int request, const char * host);static char ** baytech_hostlist(Stonith *);static void baytech_free_hostlist(char **);static struct stonith_ops baytechOps ={ baytech_new, /* Create new STONITH object */ baytech_destroy, /* Destroy STONITH object */ baytech_set_config_file, /* set configuration from file */ baytech_set_config_info, /* Get configuration from file */ baytech_getinfo, /* Return STONITH info string */ baytech_status, /* Return STONITH device status */ baytech_reset_req, /* Request a reset */ baytech_hostlist, /* Return list of supported hosts */ baytech_free_hostlist /* free above list */};PIL_PLUGIN_BOILERPLATE("1.0", Debug, baytechclosepi);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 , &baytechOps , baytechcloseintf /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); }#define DEVICE "BayTech power switch"#define N_(text) (text)#define _(text) dgettext(ST_TEXTDOMAIN, text)/* * I have an RPC-5. This code has been tested with this switch. * * The BayTech switches are quite nice, but the dialogues are a bit of a * pain for mechanical parsing. */struct BayTech { const char * BTid; char * idinfo; char * unitid; const struct BayTechModelInfo* modelinfo; pid_t pid; int rdfd; int wrfd; int config; char * device; char * user; char * passwd;};struct BayTechModelInfo { const char * type; /* Baytech model info */ size_t socklen; /* Length of socket name string */ struct Etoken * expect; /* Expect string before outlet list */};static int parse_socket_line(struct BayTech*,const char *, int *, char *);static const char * BTid = "BayTech-Stonith";static const char * NOTbtid = "Hey, dummy this has been destroyed (BayTech)";#define ISBAYTECH(i) (((i)!= NULL && (i)->pinfo != NULL) \ && ((struct BayTech *)(i->pinfo))->BTid == BTid)#ifndef MALLOCT# define MALLOCT(t) ((t *)(MALLOC(sizeof(t))))#endif#define ISCONFIGED(i) (ISBAYTECH(i) && ((struct BayTech *)(i->pinfo))->config)#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 Baytech * Remote Power Controllers... */#define BAYTECHASSOC "Bay Technical Associates"static struct Etoken EscapeChar[] = { {"Escape character is '^]'.", 0, 0} , {NULL,0,0}};static struct Etoken BayTechAssoc[] = { {BAYTECHASSOC, 0, 0}, {NULL,0,0}};static struct Etoken UnitId[] = { {"Unit ID: ", 0, 0}, {NULL,0,0}};static struct Etoken login[] = { {"username>", 0, 0} ,{NULL,0,0}};static struct Etoken password[] = { {"password>", 0, 0} , {"username>", 0, 0} ,{NULL,0,0}};static struct Etoken Selection[] = { {"election>", 0, 0} ,{NULL,0,0}};static struct Etoken RPC[] = { {"RPC", 0, 0} ,{NULL,0,0}};static struct Etoken LoginOK[] = { {"RPC", 0, 0}, {"Invalid password", 1, 0} , {NULL,0,0}};static struct Etoken GTSign[] = { {">", 0, 0} ,{NULL,0,0}};static struct Etoken Menu[] = { {"Menu:", 0, 0} ,{NULL,0,0}};static struct Etoken Temp[] = { {"emperature: ", 0, 0} , {NULL,0,0}};static struct Etoken Break[] = { {"reaker: ", 0, 0} , {NULL,0,0}};static struct Etoken PowerApplied[] = { {"ower applied to outlet", 0, 0} , {NULL,0,0}};/* Accept either a CR/NL or an NL/CR */static struct Etoken CRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};/* We may get a notice about rebooting, or a request for confirmation */static struct Etoken Rebooting[] = { {"ebooting selected outlet", 0, 0} , {"(Y/N)>", 1, 0} , {"already off.", 2, 0} , {NULL,0,0}};static struct BayTechModelInfo ModelInfo [] = { {"RPC-5", 18, Temp}, /* This first model will be the default */ {"RPC-3", 10, Break}, {"RPC-3A", 10, Break}, {NULL, 0, NULL},};static int RPCLookFor(struct BayTech* bt, struct Etoken * tlist, int timeout);static int RPC_connect_device(struct BayTech * bt);static int RPCLogin(struct BayTech * bt);static int RPCRobustLogin(struct BayTech * bt);static int RPCNametoOutlet(struct BayTech*, const char * name);static int RPCReset(struct BayTech*, int unitnum, const char * rebootid);static int RPCScanLine(struct BayTech* bt, int timeout, char * buf, int max);static int RPCLogout(struct BayTech * bt);static void RPCkillcomm(struct BayTech * bt);static int RPC_parse_config_info(struct BayTech* bt, const char * info);#if defined(ST_POWERON) && defined(ST_POWEROFF)static int RPC_onoff(struct BayTech*, int unitnum, const char * unitid, int request);#endif/* * We do these things a lot. Here are a few shorthand macros. */#define SEND(s) (write(bt->wrfd, (s), strlen(s)))#define EXPECT(p,t) { \ if (RPCLookFor(bt, p, t) < 0) \ return(errno == ETIMEDOUT \ ? S_TIMEOUT : S_OOPS); \ }#define NULLEXPECT(p,t) { \ if (RPCLookFor(bt, p, t) < 0) \ return(NULL); \ }#define SNARF(s, to) { \ if (RPCScanLine(bt,to,(s),sizeof(s)) \ != S_OK) \ return(S_OOPS); \ }#define NULLSNARF(s, to) { \ if (RPCScanLine(bt,to,(s),sizeof(s)) \ != S_OK) \ return(NULL); \ }/* Look for any of the given patterns. We don't care which */#define MAXSAVE 512static intRPCLookFor(struct BayTech* bt, struct Etoken * tlist, int timeout){ int rc; char savebuf[MAXSAVE]; savebuf[MAXSAVE-1] = EOS; savebuf[0] = EOS; if ((rc = EXPECT_TOK(bt->rdfd, tlist, timeout, savebuf, MAXSAVE)) < 0){ syslog(LOG_ERR, _("Did not find string: '%s' from " DEVICE ".") , tlist[0].string); syslog(LOG_ERR, _("Got '%s' from " DEVICE " instead.") , savebuf); RPCkillcomm(bt); return(-1); }#if 0 syslog(LOG_INFO , "BayTech: Expecting [%s] Received [%s]" , tlist[0].string , savebuf);#endif return(rc);}/* Read and return the rest of the line */static intRPCScanLine(struct BayTech* bt, int timeout, char * buf, int max){ if (EXPECT_TOK(bt->rdfd, CRNL, timeout, buf, max) < 0) { syslog(LOG_ERR, ("Could not read line from " DEVICE ".")); RPCkillcomm(bt); bt->pid = -1; return(S_OOPS); } return(S_OK);}/* Login to the Baytech Remote Power Controller (RPC) */static intRPCLogin(struct BayTech * bt){ char IDinfo[128]; static char IDbuf[128]; char * idptr = IDinfo; char * delim; int j; EXPECT(EscapeChar, 10); /* Look for the unit type info */ if (EXPECT_TOK(bt->rdfd, BayTechAssoc, 2, IDinfo , sizeof(IDinfo)) < 0) { syslog(LOG_ERR, _("No initial response from " DEVICE ".")); RPCkillcomm(bt); return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS); } idptr += strspn(idptr, WHITESPACE); /* * We should be looking at something like this: * RPC-5 Telnet Host * Revision F 4.22, (C) 1999 * Bay Technical Associates */ /* Truncate the result after the RPC-5 part */ if ((delim = strchr(idptr, ' ')) != NULL) { *delim = EOS; } snprintf(IDbuf, sizeof(IDbuf), "BayTech %s", idptr); REPLSTR(bt->idinfo, IDbuf); bt->modelinfo = &ModelInfo[0]; for (j=0; ModelInfo[j].type != NULL; ++j) { /* * TIMXXX - * Look at device ID as this really describes the model. */ if (strcasecmp(ModelInfo[j].type, idptr) == 0) { bt->modelinfo = &ModelInfo[j]; break; } } /* Look for the unit id info */ EXPECT(UnitId, 10); SNARF(IDbuf, 2); delim = IDbuf + strcspn(IDbuf, WHITESPACE); *delim = EOS; REPLSTR(bt->unitid, IDbuf); /* Expect "username>" */ EXPECT(login, 2); SEND(bt->user); SEND("\r"); /* Expect "password>" */ switch (RPCLookFor(bt, password, 5)) { case 0: /* Good! */ break; case 1: /* OOPS! got another username prompt */ syslog(LOG_ERR, _("Invalid username for " DEVICE ".")); return(S_ACCESS); default: return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS); } SEND(bt->passwd); SEND("\r"); /* Expect "RPC-x Menu" */ switch (RPCLookFor(bt, LoginOK, 5)) { case 0: /* Good! */ break; case 1: /* Uh-oh - bad password */ syslog(LOG_ERR, _("Invalid password for " DEVICE ".")); return(S_ACCESS); default: RPCkillcomm(bt); return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS); } EXPECT(Menu, 2); return(S_OK);}static intRPCRobustLogin(struct BayTech * bt){ int rc=S_OOPS; int j; for (j=0; j < 20 && rc != S_OK; ++j) { if (bt->pid > 0) { RPCkillcomm(bt); } if (RPC_connect_device(bt) != S_OK) { RPCkillcomm(bt); continue; } rc = RPCLogin(bt); } return rc;}/* Log out of the Baytech RPC */static intRPCLogout(struct BayTech* bt){ int rc; /* Make sure we're in the right menu... */ SEND("\r"); /* Expect "Selection>" */ rc = RPCLookFor(bt, Selection, 5); /* Option 6 is Logout */ SEND("6\r"); close(bt->wrfd); close(bt->rdfd); bt->wrfd = bt->rdfd = -1; RPCkillcomm(bt); return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS));}static voidRPCkillcomm(struct BayTech* bt){ if (bt->pid > 0) { STONITH_KILL(bt->pid, SIGKILL); (void)waitpid(bt->pid, NULL, 0); bt->pid = -1; }}/* Reset (power-cycle) the given outlet number */static intRPCReset(struct BayTech* bt, int unitnum, const char * rebootid){ char unum[32]; SEND("\r"); /* Make sure we're in the top level menu */ /* Expect "RPC-x Menu" */ EXPECT(RPC, 5); EXPECT(Menu, 5); /* OK. Request sub-menu 1 (Outlet Control) */ SEND("1\r"); /* Verify that we're in the sub-menu */ /* Expect: "RPC-x>" */ EXPECT(RPC, 5); EXPECT(GTSign, 5); /* Send REBOOT command for given outlet */ snprintf(unum, sizeof(unum), "REBOOT %d\r", unitnum); SEND(unum); /* Expect "ebooting "... or "(Y/N)" (if confirmation turned on) */ retry: switch (RPCLookFor(bt, Rebooting, 5)) { case 0: /* Got "Rebooting" Do nothing */ break; case 1: /* Got that annoying command confirmation :-( */ SEND("Y\r"); goto retry; case 2: /* Outlet is turned off */ syslog(LOG_ERR, _("Host %s is OFF."), rebootid); return(S_ISOFF); default: return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS); } syslog(LOG_INFO, _("Host %s being rebooted."), rebootid); /* Expect "ower applied to outlet" */ if (RPCLookFor(bt, PowerApplied, 30) < 0) { return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS); } /* All Right! Power is back on. Life is Good! */ syslog(LOG_INFO, _("Power restored to host %s."), rebootid); /* Expect: "RPC-x>" */ EXPECT(RPC,5); EXPECT(GTSign, 5); /* Pop back to main menu */ SEND("MENU\r"); return(S_OK);}#if defined(ST_POWERON) && defined(ST_POWEROFF)static intRPC_onoff(struct BayTech* bt, int unitnum, const char * unitid, int req){ char unum[32]; const char * onoff = (req == ST_POWERON ? "on" : "off"); int rc; if ((rc = RPCRobustLogin(bt) != S_OK)) { syslog(LOG_ERR, _("Cannot log into " DEVICE ".")); return(rc); } SEND("\r"); /* Make sure we're in the top level menu */ /* Expect "RPC-x Menu" */ EXPECT(RPC, 5); EXPECT(Menu, 5); /* OK. Request sub-menu 1 (Outlet Control) */ SEND("1\r"); /* Verify that we're in the sub-menu */ /* Expect: "RPC-x>" */ EXPECT(RPC, 5); EXPECT(GTSign, 5); /* Send ON/OFF command for given outlet */ snprintf(unum, sizeof(unum), "%s %d\r" , onoff, unitnum);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -