/* $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 * * 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 #include #include #include #include #include #include #include #include #include #include #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 #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 void baytechclosepi(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_rc baytechcloseintf(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->StartProcess PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports); PIL_rc PIL_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 512 static int RPCLookFor(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 int RPCScanLine(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 int RPCLogin(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 int RPCRobustLogin(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 int RPCLogout(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 void RPCkillcomm(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 int RPCReset(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 int RPC_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); SEND(unum); /* Expect "RPC->x "... or "(Y/N)" (if confirmation turned on) */ if (RPCLookFor(bt, RPC, 10) == 1) { /* They've turned on that annoying command confirmation :-( */ SEND("Y\r"); EXPECT(RPC, 10); } EXPECT(GTSign, 10); /* All Right! Command done. Life is Good! */ syslog(LOG_NOTICE, _("Power to host %s turned %s."), unitid, onoff); /* Pop back to main menu */ SEND("MENU\r"); return(S_OK); } #endif /* defined(ST_POWERON) && defined(ST_POWEROFF) */ /* * Map the given host name into an (AC) Outlet number on the power strip */ static int RPCNametoOutlet(struct BayTech* bt, const char * name) { char NameMapping[128]; int sockno; char sockname[32]; int ret = -1; /* Verify that we're in the top-level menu */ SEND("\r"); /* 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); /* The status command output contains mapping of hosts to outlets */ SEND("STATUS\r"); /* Expect: "emperature:" so we can skip over it... */ EXPECT(bt->modelinfo->expect, 5); EXPECT(CRNL, 5); /* Looks Good! Parse the status output */ do { char * last; NameMapping[0] = EOS; SNARF(NameMapping, 5); if (!parse_socket_line(bt, NameMapping, &sockno, sockname)) { continue; } last = sockname+bt->modelinfo->socklen; *last = EOS; --last; /* Strip off trailing blanks */ for(; last > sockname; --last) { if (*last == ' ') { *last = EOS; }else{ break; } } g_strdown(sockname); if (strcmp(name, sockname) == 0) { ret = sockno; } } while (strlen(NameMapping) > 2 && ret < 0); /* Pop back out to the top level menu */ SEND("MENU\r"); return(ret); } static int baytech_status(Stonith *s) { struct BayTech* bt; int rc; if (!ISBAYTECH(s)) { syslog(LOG_ERR, "invalid argument to RPC_status"); return(S_OOPS); } if (!ISCONFIGED(s)) { syslog(LOG_ERR , "unconfigured stonith object in RPC_status"); return(S_OOPS); } bt = (struct BayTech*) s->pinfo; if ((rc = RPCRobustLogin(bt) != S_OK)) { syslog(LOG_ERR, _("Cannot log into " DEVICE ".")); return(rc); } /* Verify that we're in the top-level menu */ SEND("\r"); /* Expect "RPC-x Menu" */ EXPECT(RPC, 5); EXPECT(Menu, 5); return(RPCLogout(bt)); } /* * Return the list of hosts (outlet names) for the devices on this BayTech unit */ static char ** baytech_hostlist(Stonith *s) { char NameMapping[128]; char* NameList[64]; unsigned int numnames = 0; char ** ret = NULL; struct BayTech* bt; if (!ISBAYTECH(s)) { syslog(LOG_ERR, "invalid argument to baytech_hostlist"); return(NULL); } if (!ISCONFIGED(s)) { syslog(LOG_ERR , "unconfigured stonith object in baytech_hostlist"); return(NULL); } bt = (struct BayTech*) s->pinfo; if (RPCRobustLogin(bt) != S_OK) { syslog(LOG_ERR, _("Cannot log into " DEVICE ".")); return(NULL); } /* Verify that we're in the top-level menu */ SEND("\r"); /* Expect "RPC-x Menu" */ NULLEXPECT(RPC, 5); NULLEXPECT(Menu, 5); /* OK. Request sub-menu 1 (Outlet Control) */ SEND("1\r"); /* Verify that we're in the sub-menu */ /* Expect: "RPC-x>" */ NULLEXPECT(RPC, 5); NULLEXPECT(GTSign, 5); /* The status command output contains mapping of hosts to outlets */ SEND("STATUS\r"); /* Expect: "emperature:" so we can skip over it... */ NULLEXPECT(bt->modelinfo->expect, 5); NULLEXPECT(CRNL, 5); /* Looks Good! Parse the status output */ do { int sockno; char sockname[64]; char * last; char * nm; NameMapping[0] = EOS; NULLSNARF(NameMapping, 5); if (!parse_socket_line(bt, NameMapping, &sockno, sockname)) { continue; } last = sockname+bt->modelinfo->socklen; *last = EOS; --last; /* Strip off trailing blanks */ for(; last > sockname; --last) { if (*last == ' ') { *last = EOS; }else{ break; } } if (numnames >= DIMOF(NameList)-1) { break; } if ((nm = (char*)STRDUP(sockname)) == NULL) { syslog(LOG_ERR, "out of memory"); return(NULL); } g_strdown(nm); NameList[numnames] = nm; ++numnames; NameList[numnames] = NULL; } while (strlen(NameMapping) > 2); /* Pop back out to the top level menu */ SEND("MENU\r"); if (numnames >= 1) { ret = (char **)MALLOC((numnames+1)*sizeof(char*)); if (ret == NULL) { syslog(LOG_ERR, "out of memory"); }else{ memcpy(ret, NameList, (numnames+1)*sizeof(char*)); } } (void)RPCLogout(bt); return(ret); } static void baytech_free_hostlist (char ** hlist) { char ** hl = hlist; if (hl == NULL) { return; } while (*hl) { FREE(*hl); *hl = NULL; ++hl; } FREE(hlist); } /* * Parse the given configuration information, and stash it away... */ static int RPC_parse_config_info(struct BayTech* bt, const char * info) { static char dev[1024]; static char user[1024]; static char passwd[1024]; if (bt->config) { return(S_OOPS); } if (sscanf(info, "%s %s %[^\n\r\t]", dev, user, passwd) == 3 && strlen(passwd) > 1) { if ((bt->device = STRDUP(dev)) == NULL) { syslog(LOG_ERR, "out of memory"); return(S_OOPS); } if ((bt->user = STRDUP(user)) == NULL) { FREE(bt->device); bt->device=NULL; syslog(LOG_ERR, "out of memory"); return(S_OOPS); } if ((bt->passwd = STRDUP(passwd)) == NULL) { FREE(bt->user); bt->user=NULL; FREE(bt->device); bt->device=NULL; syslog(LOG_ERR, "out of memory"); return(S_OOPS); } bt->config = 1; return(S_OK); } return(S_BADCONFIG); } /* * Connect to the given BayTech device. We should add serial support here * eventually... */ static int RPC_connect_device(struct BayTech * bt) { char TelnetCommand[256]; snprintf(TelnetCommand, sizeof(TelnetCommand) , "exec telnet %s 2>/dev/null", bt->device); bt->pid=STARTPROC(TelnetCommand, &bt->rdfd, &bt->wrfd); if (bt->pid <= 0) { return(S_OOPS); } return(S_OK); } /* * Reset the given host on this Stonith device. */ static int baytech_reset_req(Stonith * s, int request, const char * host) { int rc = 0; int lorc = 0; struct BayTech* bt; if (!ISBAYTECH(s)) { syslog(LOG_ERR, "invalid argument to baytech_reset"); return(S_OOPS); } if (!ISCONFIGED(s)) { syslog(LOG_ERR , "unconfigured stonith object in baytech_reset"); return(S_OOPS); } bt = (struct BayTech*) s->pinfo; if ((rc = RPCRobustLogin(bt)) != S_OK) { syslog(LOG_ERR, _("Cannot log into " DEVICE ".")); }else{ int noutlet; noutlet = RPCNametoOutlet(bt, host); if (noutlet < 1) { syslog(LOG_WARNING, _("%s %s " "doesn't control host [%s]."), bt->idinfo , bt->unitid, host); RPCkillcomm(bt); return(S_BADHOST); } switch(request) { #if defined(ST_POWERON) && defined(ST_POWEROFF) case ST_POWERON: case ST_POWEROFF: rc = RPC_onoff(bt, noutlet, host, request); break; #endif case ST_GENERIC_RESET: rc = RPCReset(bt, noutlet, host); break; default: rc = S_INVAL; break; } } lorc = RPCLogout(bt); RPCkillcomm(bt); return(rc != S_OK ? rc : lorc); } /* * Parse the information in the given configuration file, * and stash it away... */ static int baytech_set_config_file(Stonith* s, const char * configname) { FILE * cfgfile; char RPCid[256]; struct BayTech* bt; if (!ISBAYTECH(s)) { syslog(LOG_ERR, "invalid argument to baytech_set"); return(S_OOPS); } bt = (struct BayTech*) s->pinfo; if ((cfgfile = fopen(configname, "r")) == NULL) { syslog(LOG_ERR, _("Cannot open %s"), configname); return(S_BADCONFIG); } while (fgets(RPCid, sizeof(RPCid), cfgfile) != NULL){ if (*RPCid == '#' || *RPCid == '\n' || *RPCid == EOS) { continue; } return(RPC_parse_config_info(bt, RPCid)); } return(S_BADCONFIG); } /* * Parse the config information in the given string, and stash it away... */ static int baytech_set_config_info(Stonith* s, const char * info) { struct BayTech* bt; if (!ISBAYTECH(s)) { syslog(LOG_ERR, "baytech_set_config_info: invalid argument"); return(S_OOPS); } bt = (struct BayTech *)s->pinfo; return(RPC_parse_config_info(bt, info)); } static const char * baytech_getinfo(Stonith * s, int reqtype) { struct BayTech* bt; const char * ret; if (!ISBAYTECH(s)) { syslog(LOG_ERR, "RPC_idinfo: invalid argument"); return NULL; } /* * We look in the ST_TEXTDOMAIN catalog for our messages */ bt = (struct BayTech *)s->pinfo; switch (reqtype) { case ST_DEVICEID: /* Exactly what type of device? */ ret = bt->idinfo; break; case ST_DEVICENAME: /* Which particular individual device? */ ret = bt->device; break; case ST_CONF_INFO_SYNTAX: ret = _("IP-address login password\n" "The IP-address and login are white-space delimited."); break; case ST_CONF_FILE_SYNTAX: ret = _("IP-address login password\n" "The IP-address and login are white-space delimited. " "All three items must be on one line. " "Blank lines and lines beginning with # are ignored"); break; case ST_DEVICEDESCR: /* Description of device type */ ret = _("Bay Technical Associates (Baytech) RPC " "series power switches (via telnet).\n" "The RPC-5, RPC-3 and RPC-3A switches are well tested."); break; case ST_DEVICEURL: /* Manufacturer's web site */ ret = "http://www.baytech.net/"; break; default: ret = NULL; break; } return ret; } /* * Baytech Stonith destructor... */ static void baytech_destroy(Stonith *s) { struct BayTech* bt; if (!ISBAYTECH(s)) { syslog(LOG_ERR, "baytech_del: invalid argument"); return; } bt = (struct BayTech *)s->pinfo; bt->BTid = NOTbtid; RPCkillcomm(bt); if (bt->rdfd >= 0) { close(bt->rdfd); bt->rdfd = -1; } if (bt->wrfd >= 0) { close(bt->wrfd); bt->wrfd = -1; } if (bt->device != NULL) { FREE(bt->device); bt->device = NULL; } if (bt->user != NULL) { FREE(bt->user); bt->user = NULL; } if (bt->passwd != NULL) { FREE(bt->passwd); bt->passwd = NULL; } if (bt->idinfo != NULL) { FREE(bt->idinfo); bt->idinfo = NULL; } if (bt->unitid != NULL) { FREE(bt->unitid); bt->unitid = NULL; } } /* Create a new BayTech Stonith device. */ static void * baytech_new(void) { struct BayTech* bt = MALLOCT(struct BayTech); if (bt == NULL) { syslog(LOG_ERR, "out of memory"); return(NULL); } memset(bt, 0, sizeof(*bt)); bt->BTid = BTid; bt->pid = -1; bt->rdfd = -1; bt->wrfd = -1; bt->config = 0; bt->user = NULL; bt->device = NULL; bt->passwd = NULL; bt->idinfo = NULL; bt->unitid = NULL; REPLSTR(bt->idinfo, DEVICE); bt->modelinfo = &ModelInfo[0]; return((void *)bt); } static int parse_socket_line(struct BayTech * bt, const char *NameMapping , int *sockno, char *sockname) { #if 0 char format[64]; snprintf(format, sizeof(format), "%%7d %%%dc" , bt->modelinfo->socklen); /* 7 digits, 7 blanks, then 'socklen' characters */ /* [0-6]: digits, NameMapping[13] begins the sockname */ /* NameMapping strlen must be >= socklen + 14 */ if (sscanf(NameMapping, format, sockno, sockname) != 2) { return FALSE; } #else # define OFFSET 14 if (sscanf(NameMapping, "%7d", sockno) != 1 || strlen(NameMapping) < OFFSET+bt->modelinfo->socklen) { return FALSE; } strncpy(sockname, NameMapping+OFFSET, bt->modelinfo->socklen); sockname[bt->modelinfo->socklen] = EOS; #endif return TRUE; }