check_script.c revision 1.12
1/*	$OpenBSD: check_script.c,v 1.12 2011/05/09 12:08:47 reyk Exp $	*/
2
3/*
4 * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/param.h>
20#include <sys/queue.h>
21#include <sys/socket.h>
22#include <sys/wait.h>
23
24#include <net/if.h>
25
26#include <limits.h>
27#include <event.h>
28#include <errno.h>
29#include <unistd.h>
30#include <string.h>
31#include <stdlib.h>
32#include <signal.h>
33#include <pwd.h>
34#include <err.h>
35
36#include <openssl/ssl.h>
37
38#include "relayd.h"
39
40void	 script_sig_alarm(int);
41
42pid_t			 child = -1;
43
44void
45check_script(struct relayd *env, struct host *host)
46{
47	struct ctl_script	 scr;
48
49	host->last_up = host->up;
50	host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE);
51
52	scr.host = host->conf.id;
53	proc_compose_imsg(env->sc_ps, PROC_PARENT, 0, IMSG_SCRIPT,
54	    -1, &scr, sizeof(scr));
55}
56
57void
58script_done(struct relayd *env, struct ctl_script *scr)
59{
60	struct host		*host;
61
62	if ((host = host_find(env, scr->host)) == NULL)
63		fatalx("hce_dispatch_parent: invalid host id");
64
65	if (scr->retval < 0)
66		host->up = HOST_UNKNOWN;
67	else if (scr->retval == 0)
68		host->up = HOST_DOWN;
69	else
70		host->up = HOST_UP;
71	host->flags |= F_CHECK_DONE;
72
73	hce_notify_done(host, host->up == HOST_UP ?
74	    HCE_SCRIPT_OK : HCE_SCRIPT_FAIL);
75}
76
77void
78script_sig_alarm(int sig)
79{
80	int save_errno = errno;
81
82	if (child != -1)
83		kill(child, SIGKILL);
84	errno = save_errno;
85}
86
87int
88script_exec(struct relayd *env, struct ctl_script *scr)
89{
90	int			 status = 0, ret = 0;
91	sig_t			 save_quit, save_int, save_chld;
92	struct itimerval	 it;
93	struct timeval		*tv;
94	const char		*file, *arg;
95	struct host		*host;
96	struct table		*table;
97	struct passwd		*pw;
98
99	if ((host = host_find(env, scr->host)) == NULL)
100		fatalx("script_exec: invalid host id");
101	if ((table = table_find(env, host->conf.tableid)) == NULL)
102		fatalx("script_exec: invalid table id");
103
104	arg = host->conf.name;
105	file = table->conf.path;
106	tv = &table->conf.timeout;
107
108	save_quit = signal(SIGQUIT, SIG_IGN);
109	save_int = signal(SIGINT, SIG_IGN);
110	save_chld = signal(SIGCHLD, SIG_DFL);
111
112	switch (child = fork()) {
113	case -1:
114		ret = -1;
115		goto done;
116	case 0:
117		signal(SIGQUIT, SIG_DFL);
118		signal(SIGINT, SIG_DFL);
119		signal(SIGCHLD, SIG_DFL);
120
121		if ((pw = getpwnam(RELAYD_USER)) == NULL)
122			fatal("script_exec: getpwnam");
123		if (chdir("/") == -1)
124			fatal("script_exec: chdir(\"/\")");
125		if (setgroups(1, &pw->pw_gid) ||
126		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
127		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
128			fatal("script_exec: can't drop privileges");
129
130		/*
131		 * close fds before executing an external program, to
132		 * prevent access to internal fds, eg. IMSG connections
133		 * of internal processes.
134		 */
135		closefrom(STDERR_FILENO + 1);
136
137		execlp(file, file, arg, (char *)NULL);
138		_exit(0);
139		break;
140	default:
141		/* Kill the process after a timeout */
142		signal(SIGALRM, script_sig_alarm);
143		bzero(&it, sizeof(it));
144		bcopy(tv, &it.it_value, sizeof(it.it_value));
145		setitimer(ITIMER_REAL, &it, NULL);
146
147		waitpid(child, &status, 0);
148		break;
149	}
150
151	switch (ret) {
152	case -1:
153		ret = -1;
154		break;
155	default:
156		if (WIFEXITED(status))
157			ret = WEXITSTATUS(status);
158		else
159			ret = 0;
160	}
161
162 done:
163	/* Disable the process timeout timer */
164	bzero(&it, sizeof(it));
165	setitimer(ITIMER_REAL, &it, NULL);
166	child = -1;
167
168	signal(SIGQUIT, save_quit);
169	signal(SIGINT, save_int);
170	signal(SIGCHLD, save_chld);
171	signal(SIGALRM, SIG_DFL);
172
173	return (ret);
174}
175