Logo Search packages:      
Sourcecode: heartbeat version File versions

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 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;
}

Generated by  Doxygen 1.6.0   Back to index