1 /*
2 * Simple expect module for the STONITH library
3 *
4 * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22 #include <lha_internal.h>
23 #include <sys/types.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <syslog.h>
30 #include <sys/wait.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stddef.h>
34 #include <stdarg.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <time.h>
39 #include <sys/time.h>
40 #include <sys/times.h>
41 #include <sys/socket.h>
42 #include <netdb.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <stonith/st_ttylock.h>
46 #include <clplumbing/longclock.h>
47 #define ENABLE_PIL_DEFS_PRIVATE
48 #include <pils/plugin.h>
49
50 #ifdef _POSIX_PRIORITY_SCHEDULING
51 # include <sched.h>
52 #endif
53
54 #include <stonith/stonith.h>
55 #include <stonith/stonith_plugin.h>
56
57 extern PILPluginUniv* StonithPIsys;
58
59 #define LOG(args...) PILCallLog(StonithPIsys->imports->log, args)
60 #define DEBUG(args...) LOG(PIL_DEBUG, args)
61 #undef DEBUG
62 #define DEBUG(args...) PILCallLog(StonithPIsys->imports->log, PIL_DEBUG, args)
63 #define MALLOC StonithPIsys->imports->alloc
64 #define REALLOC StonithPIsys->imports->mrealloc
65 #define STRDUP StonithPIsys->imports->mstrdup
66 #define FREE(p) {StonithPIsys->imports->mfree(p); (p) = NULL;}
67
68 #ifdef TIMES_ALLOWS_NULL_PARAM
69 # define TIMES_PARAM NULL
70 #else
71 static struct tms dummy_longclock_tms_struct;
72 # define TIMES_PARAM &dummy_longclock_tms_struct
73 #endif
74
75 static unsigned long
our_times(void)76 our_times(void) /* Make times(2) behave rationally on Linux */
77 {
78 clock_t ret;
79 #ifndef DISABLE_TIMES_KLUDGE
80 int save_errno = errno;
81
82 /*
83 * This code copied from clplumbing/longclock.c to avoid
84 * making STONITH depend on clplumbing. See it for an explanation
85 */
86
87 errno = 0;
88 #endif /* DISABLE_TIMES_KLUDGE */
89
90 ret = times(TIMES_PARAM);
91
92 #ifndef DISABLE_TIMES_KLUDGE
93 if (errno != 0) {
94 ret = (clock_t) (-errno);
95 }
96 errno = save_errno;
97 #endif /* DISABLE_TIMES_KLUDGE */
98 return (unsigned long)ret;
99 }
100
101 /*
102 * Look for ('expect') any of a series of tokens in the input
103 * Return the token type for the given token or -1 on error.
104 */
105
106 static int
ExpectToken(int fd,struct Etoken * toklist,int to_secs,char * savebuf,int maxline,int Debug)107 ExpectToken(int fd, struct Etoken * toklist, int to_secs, char * savebuf
108 , int maxline, int Debug)
109 {
110 unsigned long starttime;
111 unsigned long endtime;
112 int wraparound=0;
113 unsigned Hertz = sysconf(_SC_CLK_TCK);
114 int tickstousec = (1000000/Hertz);
115 unsigned long now;
116 unsigned long ticks;
117 int nchars = 1; /* reserve space for an EOS */
118 struct timeval tv;
119 char * buf = savebuf;
120
121 struct Etoken * this;
122
123 /* Figure out when to give up. Handle lbolt wraparound */
124
125 starttime = our_times();
126 ticks = (to_secs*Hertz);
127 endtime = starttime + ticks;
128
129 if (endtime < starttime) {
130 wraparound = 1;
131 }
132
133 if (buf) {
134 *buf = EOS;
135 }
136
137 for (this=toklist; this->string; ++this) {
138 this->matchto = 0;
139 }
140
141
142 while (now = our_times(),
143 (wraparound && (now > starttime || now <= endtime))
144 || (!wraparound && now <= endtime)) {
145
146 fd_set infds;
147 char ch;
148 unsigned long timeleft;
149 int retval;
150
151 timeleft = endtime - now;
152
153 tv.tv_sec = timeleft / Hertz;
154 tv.tv_usec = (timeleft % Hertz) * tickstousec;
155
156 if (tv.tv_sec == 0 && tv.tv_usec < tickstousec) {
157 /* Give 'em a little chance */
158 tv.tv_usec = tickstousec;
159 }
160
161 /* Watch our FD to see when it has input. */
162 FD_ZERO(&infds);
163 FD_SET(fd, &infds);
164
165 retval = select(fd+1, &infds, NULL, NULL, &tv);
166 if (retval <= 0) {
167 errno = ETIMEDOUT;
168 return(-1);
169 }
170 /* Whew! All that work just to read one character! */
171
172 if (read(fd, &ch, sizeof(ch)) <= 0) {
173 return(-1);
174 }
175 /* Save the text, if we can */
176 if (buf && nchars < maxline-1) {
177 *buf = ch;
178 ++buf;
179 *buf = EOS;
180 ++nchars;
181 }
182 if (Debug > 1) {
183 DEBUG("Got '%c'", ch);
184 }
185
186 /* See how this character matches our expect strings */
187
188 for (this=toklist; this->string; ++this) {
189
190 if (ch == this->string[this->matchto]) {
191
192 /* It matches the current token */
193
194 ++this->matchto;
195 if (this->string[this->matchto] == EOS){
196 /* Hallelujah! We matched */
197 if (Debug) {
198 DEBUG("Matched [%s] [%d]"
199 , this->string
200 , this->toktype);
201 if (savebuf) {
202 DEBUG("Saved [%s]"
203 , savebuf);
204 }
205 }
206 return(this->toktype);
207 }
208 }else{
209
210 /* It doesn't appear to match this token */
211
212 int curlen;
213 int nomatch=1;
214 /*
215 * If we already had a match (matchto is
216 * greater than zero), we look for a match
217 * of the tail of the pattern matched so far
218 * (with the current character) against the
219 * head of the pattern.
220 */
221
222 /*
223 * This is to make the string "aab" match
224 * the pattern "ab" correctly
225 * Painful, but nice to do it right.
226 */
227
228 for (curlen = (this->matchto)
229 ; nomatch && curlen >= 0
230 ; --curlen) {
231 const char * tail;
232 tail=(this->string)
233 + this->matchto
234 - curlen;
235
236 if (strncmp(this->string, tail
237 , curlen) == 0
238 && this->string[curlen] == ch) {
239
240 if (this->string[curlen+1]==EOS){
241 /* We matched! */
242 /* (can't happen?) */
243 return(this->toktype);
244 }
245 this->matchto = curlen+1;
246 nomatch=0;
247 }
248 }
249 if (nomatch) {
250 this->matchto = 0;
251 }
252 }
253 }
254 }
255 errno = ETIMEDOUT;
256 return(-1);
257 }
258
259 /*
260 * Start a process with its stdin and stdout redirected to pipes
261 * so the parent process can talk to it.
262 */
263 static int
StartProcess(const char * cmd,int * readfd,int * writefd)264 StartProcess(const char * cmd, int * readfd, int * writefd)
265 {
266 pid_t pid;
267 int wrpipe[2]; /* The pipe the parent process writes to */
268 /* (which the child process reads from) */
269 int rdpipe[2]; /* The pipe the parent process reads from */
270 /* (which the child process writes to) */
271
272 if (pipe(wrpipe) < 0) {
273 perror("cannot create pipe\n");
274 return(-1);
275 }
276 if (pipe(rdpipe) < 0) {
277 perror("cannot create pipe\n");
278 close(wrpipe[0]);
279 close(wrpipe[1]);
280 return(-1);
281 }
282 switch(pid=fork()) {
283
284 case -1: perror("cannot StartProcess cmd");
285 close(rdpipe[0]);
286 close(wrpipe[1]);
287 close(wrpipe[0]);
288 close(rdpipe[1]);
289 return(-1);
290
291 case 0: /* We are the child */
292
293 /* Redirect stdin */
294 close(0);
295 dup2(wrpipe[0], 0);
296 close(wrpipe[0]);
297 close(wrpipe[1]);
298
299 /* Redirect stdout */
300 close(1);
301 dup2(rdpipe[1], 1);
302 close(rdpipe[0]);
303 close(rdpipe[1]);
304 #if defined(SCHED_OTHER) && !defined(ON_DARWIN)
305 {
306 /*
307 * Try and (re)set our scheduling to "normal"
308 * Sometimes our callers run in soft
309 * real-time mode. The program we exec might
310 * not be very well behaved - this is bad for
311 * operation in high-priority (soft real-time)
312 * mode. In particular, telnet is prone to
313 * going into infinite loops when killed.
314 */
315 struct sched_param sp;
316 memset(&sp, 0, sizeof(sp));
317 sp.sched_priority = 0;
318 sched_setscheduler(0, SCHED_OTHER, &sp);
319 }
320 #endif
321 execlp("/bin/sh", "sh", "-c", cmd, (const char *)NULL);
322 perror("cannot exec shell!");
323 exit(1);
324
325 default: /* We are the parent */
326 *readfd = rdpipe[0];
327 close(rdpipe[1]);
328
329 *writefd = wrpipe[1];
330 close(wrpipe[0]);
331 return(pid);
332 }
333 /*NOTREACHED*/
334 return(-1);
335 }
336
337 static char **
stonith_copy_hostlist(const char * const * hostlist)338 stonith_copy_hostlist(const char * const * hostlist)
339 {
340 int hlleng = 1;
341 const char * const * here = hostlist;
342 char ** hret;
343 char ** ret;
344
345 for (here = hostlist; *here; ++here) {
346 ++hlleng;
347 }
348 ret = (char**)MALLOC(hlleng * sizeof(char *));
349 if (ret == NULL) {
350 return ret;
351 }
352
353 hret = ret;
354 for (here = hostlist; *here; ++here,++hret) {
355 *hret = STRDUP(*here);
356 if (*hret == NULL) {
357 stonith_free_hostlist(ret);
358 return NULL;
359 }
360 }
361 *hret = NULL;
362 return ret;
363 }
364
365 static char **
StringToHostList(const char * s)366 StringToHostList(const char * s)
367 {
368 const char * here;
369 int hlleng = 0;
370 char ** ret;
371 char ** hret;
372 const char * delims = " \t\n\f\r,";
373
374 /* Count the number of strings (words) in the result */
375 here = s;
376 while (*here != EOS) {
377 /* skip delimiters */
378 here += strspn(here, delims);
379 if (*here == EOS) {
380 break;
381 }
382 /* skip over substring proper... */
383 here += strcspn(here, delims);
384 ++hlleng;
385 }
386
387
388 /* Malloc space for the result string pointers */
389 ret = (char**)MALLOC((hlleng+1) * sizeof(char *));
390 if (ret == NULL) {
391 return NULL;
392 }
393
394 hret = ret;
395 here = s;
396
397 /* Copy each substring into a separate string */
398 while (*here != EOS) {
399 int slen; /* substring length */
400
401 /* skip delimiters */
402 here += strspn(here, delims);
403 if (*here == EOS) {
404 break;
405 }
406 /* Compute substring length */
407 slen = strcspn(here, delims);
408 *hret = MALLOC((slen+1) * sizeof(char));
409 if (*hret == NULL) {
410 stonith_free_hostlist(hret);
411 return NULL;
412 }
413 /* Copy string (w/o EOS) */
414 memcpy(*hret, here, slen);
415 /* Add EOS to result string */
416 (*hret)[slen] = EOS;
417 strdown(*hret);
418 here += slen;
419 ++hret;
420 }
421 *hret = NULL;
422 return ret;
423 }
424
425
426 static const char *
GetValue(StonithNVpair * parameters,const char * name)427 GetValue(StonithNVpair* parameters, const char * name)
428 {
429 while (parameters->s_name) {
430 if (strcmp(name, parameters->s_name) == 0) {
431 return parameters->s_value;
432 }
433 ++parameters;
434 }
435 return NULL;
436 }
437
438 static int
CopyAllValues(StonithNamesToGet * output,StonithNVpair * input)439 CopyAllValues(StonithNamesToGet* output, StonithNVpair * input)
440 {
441 int j;
442 int rc;
443
444 for (j=0; output[j].s_name; ++j) {
445 const char * value = GetValue(input, output[j].s_name);
446 if (value == NULL) {
447 rc = S_INVAL;
448 output[j].s_value = NULL;
449 goto fail;
450 }
451 if ((output[j].s_value = STRDUP(value)) == NULL) {
452 rc = S_OOPS;
453 goto fail;
454 }
455 }
456 return S_OK;
457
458 fail:
459 for (j=0; output[j].s_value; ++j) {
460 FREE(output[j].s_value);
461 }
462 return rc;
463 }
464
465
466 static int
OpenStreamSocket(const char * host,int port,const char * service)467 OpenStreamSocket(const char * host, int port, const char * service)
468 {
469 union s_un {
470 struct sockaddr_in si4;
471 struct sockaddr_in6 si6;
472 }sockun;
473 int sock;
474 int addrlen = -1;
475
476
477 memset(&sockun, 0, sizeof(sockun));
478
479 if (inet_pton(AF_INET, host, (void*)&sockun.si4.sin_addr) < 0) {
480 sockun.si4.sin_family = AF_INET;
481 }else if (inet_pton(AF_INET6, host, (void*)&sockun.si6.sin6_addr)<0){
482 sockun.si6.sin6_family = AF_INET6;
483 }else{
484 struct hostent* hostp = gethostbyname(host);
485 if (hostp == NULL) {
486 errno = EINVAL;
487 return -1;
488 }
489 sockun.si4.sin_family = hostp->h_addrtype;
490 memcpy(&sockun.si4.sin_addr, hostp->h_addr, hostp->h_length);
491 }
492 if ((sock = socket(sockun.si4.sin_family, SOCK_STREAM, 0)) < 0) {
493 return -1;
494 }
495 if (service != NULL) {
496 struct servent* se = getservbyname(service, "tcp");
497 if (se != NULL) {
498 /* We convert it back later... */
499 port = ntohs(se->s_port);
500 }
501 }
502 if (port <= 0) {
503 errno = EINVAL;
504 return -1;
505 }
506 port = htons(port);
507 if (sockun.si6.sin6_family == AF_INET6) {
508 sockun.si6.sin6_port = port;
509 addrlen = sizeof(sockun.si6);
510 }else if (sockun.si4.sin_family == AF_INET) {
511 sockun.si4.sin_port = port;
512 addrlen = sizeof(sockun.si4);
513 }else{
514 errno = EINVAL;
515 return -1;
516 }
517
518 if (connect(sock, (struct sockaddr*)(&sockun), addrlen)< 0){
519 int save = errno;
520 perror("connect() failed");
521 close(sock);
522 errno = save;
523 return -1;
524 }
525 return sock;
526 }
527
528 StonithImports stonithimports = {
529 ExpectToken,
530 StartProcess,
531 OpenStreamSocket,
532 GetValue,
533 CopyAllValues,
534 StringToHostList,
535 stonith_copy_hostlist,
536 stonith_free_hostlist,
537 st_ttylock,
538 st_ttyunlock
539 };
540