Logo Search packages:      
Sourcecode: heartbeat-2 version File versions  Download package

expect.c

/* $Id: expect.c,v 1.21 2005/05/11 11:49:20 andrew Exp $ */
/*
 * Simple expect module for the STONITH library
 *
 *    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 <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stonith/st_ttylock.h>
#define ENABLE_PIL_DEFS_PRIVATE
#include <pils/plugin.h>

#ifdef _POSIX_PRIORITY_SCHEDULING
#     include <sched.h>
#endif

#include <stonith/stonith.h>
#include <stonith/stonith_plugin.h>

extern      PILPluginUniv*    StonithPIsys;

#define     LOG(args...)   PILCallLog(StonithPIsys->imports->log, args)
#define DEBUG(args...) LOG(PIL_DEBUG, args)
#undef DEBUG
#define     DEBUG(args...) PILCallLog(StonithPIsys->imports->log, PIL_DEBUG, args)
#define MALLOC           StonithPIsys->imports->alloc
#define REALLOC          StonithPIsys->imports->mrealloc
#define STRDUP         StonithPIsys->imports->mstrdup
#define FREE(p)          {StonithPIsys->imports->mfree(p); (p) = NULL;}

/*
 *    Look for ('expect') any of a series of tokens in the input
 *    Return the token type for the given token or -1 on error.
 */

static int
ExpectToken(int   fd, struct Etoken * toklist, int to_secs, char * savebuf
,     int maxline, int Debug)
{
      /*
       * We use unsigned long instead of clock_t here because it's signed,
       * but the return value from times() is basically unsigned...
       * This is broken, but according to POSIX ;-)
       */
      unsigned long     starttime;
      unsigned long     endtime;
      int         wraparound=0;
      unsigned    hz =  sysconf(_SC_CLK_TCK);
      int         tickstousec = (1000000/hz);
      unsigned long     now;
      unsigned long     ticks;
      int         nchars = 1; /* reserve space for an EOS */
      struct timeval    tv;
      char *            buf = savebuf;

      struct Etoken *   this;

      /* Figure out when to give up.  Handle lbolt wraparound */

      starttime = times(NULL);
      ticks = (to_secs*hz);
      endtime = starttime + ticks;

      if (endtime < starttime) {
            wraparound = 1;
      }

      if (buf) {
            *buf = EOS;
      }

      for (this=toklist; this->string; ++this) {
            this->matchto = 0;
      }


      while (now = times(NULL),
            (wraparound && (now > starttime || now <= endtime))
            ||    (!wraparound && now <= endtime)) {

            fd_set infds;
            char  ch;
            unsigned long     timeleft;
            int         retval;

            timeleft = endtime - now;

            tv.tv_sec = timeleft / hz;
            tv.tv_usec = (timeleft % hz) * tickstousec;

            if (tv.tv_sec == 0 && tv.tv_usec < tickstousec) {
                  /* Give 'em a little chance */
                  tv.tv_usec = tickstousec;
            }

            /* Watch our FD to see when it has input. */
            FD_ZERO(&infds);
            FD_SET(fd, &infds);

            retval = select(fd+1, &infds, NULL, NULL, &tv); 
            if (retval <= 0) {
                  errno = ETIMEDOUT;
                  return(-1);
            }
            /* Whew!  All that work just to read one character! */
            
            if (read(fd, &ch, sizeof(ch)) <= 0) {
                  return(-1);
            }
            /* Save the text, if we can */
            if (buf && nchars < maxline-1) {
                  *buf = ch;
                  ++buf;
                  *buf = EOS;
                  ++nchars;
            }
            if (Debug) {
                  DEBUG("Got '%c'", ch);
            }

            /* See how this character matches our expect strings */

            for (this=toklist; this->string; ++this) {

                  if (ch == this->string[this->matchto]) {

                        /* It matches the current token */

                        ++this->matchto;
                        if (this->string[this->matchto] == EOS){
                              /* Hallelujah! We matched */
                              if (Debug) {
                                    DEBUG("Matched [%s] [%d]"
                                    ,     this->string
                                    ,     this->toktype);
                                    if (savebuf) {
                                          DEBUG("Saved [%s]"
                                          ,     savebuf);
                                    }
                              }
                              return(this->toktype);
                        }
                  }else{

                        /* It doesn't appear to match this token */

                        int   curlen;
                        int   nomatch=1;
                        /*
                         * If we already had a match (matchto is
                         * greater than zero), we look for a match
                         * of the tail of the pattern matched so far
                         * (with the current character) against the
                         * head of the pattern.
                         */

                        /*
                         * This is to make the string "aab" match
                         * the pattern "ab" correctly 
                         * Painful, but nice to do it right.
                         */

                        for (curlen = (this->matchto)
                        ;     nomatch && curlen >= 0
                        ;     --curlen)               {
                              const char *      tail;
                              tail=(this->string)
                              +     this->matchto
                              -     curlen;

                              if (strncmp(this->string, tail
                              ,     curlen) == 0
                              &&    this->string[curlen] == ch)  {
                                    
                                    if (this->string[curlen+1]==EOS){
                                          /* We matched!  */
                                          /* (can't happen?) */
                                          return(this->toktype);
                                    }
                                    this->matchto = curlen+1;
                                    nomatch=0;
                              }
                        }
                        if (nomatch) {
                              this->matchto = 0;
                        }
                  }
            }
      }
      errno = ETIMEDOUT;
      return(-1);
}

/*
 * Start a process with its stdin and stdout redirected to pipes
 * so the parent process can talk to it.
 */
static int
StartProcess(const char * cmd, int * readfd, int * writefd)
{
      pid_t pid;
      int   wrpipe[2];  /* The pipe the parent process writes to */
                        /* (which the child process reads from) */
      int   rdpipe[2];  /* The pipe the parent process reads from */
                        /* (which the child process writes to) */

      if (pipe(wrpipe) < 0) {
            perror("cannot create pipe\n");
            return(-1);
      }
      if (pipe(rdpipe) < 0) {
            perror("cannot create pipe\n");
            close(wrpipe[0]);
            close(wrpipe[1]);
            return(-1);
      }
      switch(pid=fork()) {

            case -1:    perror("cannot StartProcess cmd");
                        close(rdpipe[0]);
                        close(wrpipe[1]);
                        close(wrpipe[0]);
                        close(rdpipe[1]);
                        return(-1);

            case 0:           /* We are the child */

                        /* Redirect stdin */
                        close(0);
                        dup2(wrpipe[0], 0);
                        close(wrpipe[0]);
                        close(wrpipe[1]);

                        /* Redirect stdout */
                        close(1);
                        dup2(rdpipe[1], 1);
                        close(rdpipe[0]);
                        close(rdpipe[1]);
#if defined(SCHED_OTHER) && !defined(ON_DARWIN)
                  {
                        /*
                         * Try and (re)set our scheduling to "normal"
                         * Sometimes our callers run in soft
                         * real-time mode.  The program we exec might
                         * not be very well behaved - this is bad for
                         * operation in high-priority (soft real-time)
                         * mode.  In particular, telnet is prone to
                         * going into infinite loops when killed.
                         */
                        struct sched_param      sp;
                        memset(&sp, 0, sizeof(sp));
                        sp.sched_priority = 0;
                        sched_setscheduler(0, SCHED_OTHER, &sp);
                  }
#endif
                        execlp("/bin/sh", "sh", "-c", cmd, (const char *)NULL);
                        perror("cannot exec shell!");
                        exit(1);

            default:    /* We are the parent */
                        *readfd = rdpipe[0];
                        close(rdpipe[1]);

                        *writefd = wrpipe[1];
                        close(wrpipe[0]);
                        return(pid);
      }
      /*NOTREACHED*/
      return(-1);
}

static char **
stonith_copy_hostlist(const char** hostlist)
{
      int hlleng = 1;
      const char ** here = hostlist;
      char ** hret;
      char **     ret;

      for (here = hostlist; *here; ++here) {
            ++hlleng;
      }
      ret = (char**)MALLOC(hlleng * sizeof(char *));
      if (ret == NULL) {
            return ret;
      }
      
      hret = ret;
      for (here = hostlist; *here; ++here,++hret) {
            *hret = STRDUP(*here);
            if (*hret == NULL) {
                  stonith_free_hostlist(ret);
                  return NULL; 
            }
      }
      *hret = NULL;
      return ret;
}

static char **
StringToHostList(const char * s)
{
      const char *      here;
      int         hlleng = 0;
      char **           ret;
      char **           hret;
      const char *      delims = " \t\n\f\r,";

      /* Count the number of strings (words) in the result */
      here = s;
      while (*here != EOS) {
            /* skip delimiters */
            here += strspn(here, delims);
            if (*here == EOS) {
                  break;
            }
            /* skip over substring proper... */
            here += strcspn(here, delims);
            ++hlleng;
      }


      /* Malloc space for the result string pointers */
      ret = (char**)MALLOC((hlleng+1) * sizeof(char *));
      if (ret == NULL) {
            return NULL;
      }
      
      hret = ret;
      here = s;

      /* Copy each substring into a separate string */
      while (*here != EOS) {
            int   slen; /* substring length */

            /* skip delimiters */
            here += strspn(here, delims);
            if (*here == EOS) {
                  break;
            }
            /* Compute substring length */
            slen = strcspn(here, delims);
            *hret = MALLOC((slen+1) * sizeof(char));
            if (*hret == NULL) {
                  stonith_free_hostlist(hret);
                  return NULL; 
            }
            /* Copy string (w/o EOS) */
            memcpy(*hret, here, slen);
            /* Add EOS to result string */
            (*hret)[slen] = EOS;
            g_strdown(*hret);
            here += slen;
            ++hret;
      }
      *hret = NULL;
      return ret;
}


static const char *
GetValue(StonithNVpair* parameters, const char * name)
{
      while (parameters->s_name) {
            if (strcmp(name, parameters->s_name) == 0) {
                  return parameters->s_value;
            }
            ++parameters;
      }
      return NULL;
}

static int
CopyAllValues(StonithNamesToGet* output, StonithNVpair * input)
{
      int   j;
      int   rc;

      for (j=0; output[j].s_name; ++j) {
            const char * value = GetValue(input, output[j].s_name);
            if (value == NULL) {
                  rc = S_INVAL;
                  output[j].s_value = NULL;
                  goto fail;
            }
            if ((output[j].s_value = STRDUP(value)) == NULL) {
                  rc = S_OOPS;
                  goto fail;
            }
      }
      return S_OK;

fail:
      for (j=0; output[j].s_value; ++j) {
            FREE(output[j].s_value);
      }
      return rc;
}


static int
OpenStreamSocket(const char * host, int port, const char * service)
{
      union s_un {
            struct sockaddr_in      si4;
            struct sockaddr_in6     si6;
      }sockun;
      int               sock;
      int               addrlen = -1;


      memset(&sockun, 0, sizeof(sockun));

      if (inet_pton(AF_INET, host, (void*)&sockun.si4.sin_addr) < 0) {
            sockun.si4.sin_family = AF_INET;
      }else if (inet_pton(AF_INET6, host, (void*)&sockun.si6.sin6_addr)<0){
            sockun.si6.sin6_family = AF_INET6;
      }else{
            struct hostent*   hostp = gethostbyname(host);
            if (hostp == NULL) {
                  errno = EINVAL;
                  return -1;
            }
            sockun.si4.sin_family = hostp->h_addrtype;
            memcpy(&sockun.si4.sin_addr, hostp->h_addr, hostp->h_length);
      }
      if ((sock = socket(sockun.si4.sin_family, SOCK_STREAM, 0)) < 0) {
            return -1;
      }
      if (service != NULL) {
            struct servent*   se = getservbyname(service, "tcp");
            if (se != NULL) {
                  /* We convert it back later... */
                  port = ntohs(se->s_port);
            }
      }
      if (port <= 0) {
            errno = EINVAL;
            return -1;
      }
      port = htons(port);
      if (sockun.si6.sin6_family == AF_INET6) {
            sockun.si6.sin6_port = port;
            addrlen = sizeof(sockun.si6);
      }else if (sockun.si4.sin_family == AF_INET) {
            sockun.si4.sin_port = port;
            addrlen = sizeof(sockun.si4);
      }else{
            errno = EINVAL;
            return -1;
      }
            
      if (connect(sock, (struct sockaddr*)(&sockun), addrlen)< 0){
            int   save = errno;
            perror("connect() failed");
            close(sock);
            errno = save;
            return -1;
      }
      return sock;
}

StonithImports          stonithimports = {
      ExpectToken,
      StartProcess,
      OpenStreamSocket,
      GetValue,
      CopyAllValues,
      StringToHostList,
      stonith_copy_hostlist,
      stonith_free_hostlist,
      st_ttylock,
      st_ttyunlock
};

Generated by  Doxygen 1.6.0   Back to index