summaryrefslogtreecommitdiffstats
path: root/lib/stonith/expect.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stonith/expect.c')
-rw-r--r--lib/stonith/expect.c539
1 files changed, 539 insertions, 0 deletions
diff --git a/lib/stonith/expect.c b/lib/stonith/expect.c
new file mode 100644
index 0000000..bb1f818
--- /dev/null
+++ b/lib/stonith/expect.c
@@ -0,0 +1,539 @@
+/*
+ * 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 <lha_internal.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>
+#include <clplumbing/longclock.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;}
+
+#ifdef TIMES_ALLOWS_NULL_PARAM
+# define TIMES_PARAM NULL
+#else
+ static struct tms dummy_longclock_tms_struct;
+# define TIMES_PARAM &dummy_longclock_tms_struct
+#endif
+
+static unsigned long
+our_times(void) /* Make times(2) behave rationally on Linux */
+{
+ clock_t ret;
+#ifndef DISABLE_TIMES_KLUDGE
+ int save_errno = errno;
+
+ /*
+ * This code copied from clplumbing/longclock.c to avoid
+ * making STONITH depend on clplumbing. See it for an explanation
+ */
+
+ errno = 0;
+#endif /* DISABLE_TIMES_KLUDGE */
+
+ ret = times(TIMES_PARAM);
+
+#ifndef DISABLE_TIMES_KLUDGE
+ if (errno != 0) {
+ ret = (clock_t) (-errno);
+ }
+ errno = save_errno;
+#endif /* DISABLE_TIMES_KLUDGE */
+ return (unsigned long)ret;
+}
+
+/*
+ * 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)
+{
+ unsigned long starttime;
+ unsigned long endtime;
+ int wraparound=0;
+ unsigned Hertz = sysconf(_SC_CLK_TCK);
+ int tickstousec = (1000000/Hertz);
+ 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 = our_times();
+ ticks = (to_secs*Hertz);
+ endtime = starttime + ticks;
+
+ if (endtime < starttime) {
+ wraparound = 1;
+ }
+
+ if (buf) {
+ *buf = EOS;
+ }
+
+ for (this=toklist; this->string; ++this) {
+ this->matchto = 0;
+ }
+
+
+ while (now = our_times(),
+ (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 / Hertz;
+ tv.tv_usec = (timeleft % Hertz) * 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 > 1) {
+ 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 * const * hostlist)
+{
+ int hlleng = 1;
+ const char * const * 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;
+ 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
+};