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