check_script.c revision 1.21
1/*	$OpenBSD: check_script.c,v 1.21 2017/05/28 10:39:15 benno Exp $	*/
2
3/*
4 * Copyright (c) 2007 - 2014 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/wait.h>
20#include <sys/time.h>
21
22#include <errno.h>
23#include <unistd.h>
24#include <string.h>
25#include <stdlib.h>
26#include <signal.h>
27#include <pwd.h>
28
29#include "relayd.h"
30
31void	 script_sig_alarm(int);
32
33pid_t			 child = -1;
34
35void
36check_script(struct relayd *env, struct host *host)
37{
38	struct ctl_script	 scr;
39	struct table		*table;
40
41	if ((table = table_find(env, host->conf.tableid)) == NULL)
42		fatalx("%s: invalid table id", __func__);
43
44	host->last_up = host->up;
45	host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE);
46
47	scr.host = host->conf.id;
48	if ((strlcpy(scr.name, host->conf.name,sizeof(scr.name)) >=
49	    sizeof(scr.name)) ||
50	    (strlcpy(scr.path, table->conf.path, sizeof(scr.path)) >=
51	    sizeof(scr.path)))
52		fatalx("invalid script path");
53	memcpy(&scr.timeout, &table->conf.timeout, sizeof(scr.timeout));
54
55	proc_compose(env->sc_ps, PROC_PARENT, IMSG_SCRIPT, &scr, sizeof(scr));
56}
57
58void
59script_done(struct relayd *env, struct ctl_script *scr)
60{
61	struct host		*host;
62
63	if ((host = host_find(env, scr->host)) == NULL)
64		fatalx("%s: invalid host id", __func__);
65
66	if (scr->retval < 0)
67		host->up = HOST_UNKNOWN;
68	else if (scr->retval == 0)
69		host->up = HOST_DOWN;
70	else
71		host->up = HOST_UP;
72	host->flags |= F_CHECK_DONE;
73
74	hce_notify_done(host, host->up == HOST_UP ?
75	    HCE_SCRIPT_OK : HCE_SCRIPT_FAIL);
76}
77
78void
79script_sig_alarm(int sig)
80{
81	int save_errno = errno;
82
83	if (child != -1)
84		kill(child, SIGKILL);
85	errno = save_errno;
86}
87
88int
89script_exec(struct relayd *env, struct ctl_script *scr)
90{
91	int			 status = 0, ret = 0;
92	sig_t			 save_quit, save_int, save_chld;
93	struct itimerval	 it;
94	struct timeval		*tv;
95	const char		*file, *arg;
96	struct passwd		*pw;
97
98	if ((env->sc_conf.flags & F_SCRIPT) == 0) {
99		log_warnx("%s: script disabled", __func__);
100		return (-1);
101	}
102
103	DPRINTF("%s: running script %s, host %s",
104	    __func__, scr->path, scr->name);
105
106	arg = scr->name;
107	file = scr->path;
108	tv = &scr->timeout;
109
110	save_quit = signal(SIGQUIT, SIG_IGN);
111	save_int = signal(SIGINT, SIG_IGN);
112	save_chld = signal(SIGCHLD, SIG_DFL);
113
114	switch (child = fork()) {
115	case -1:
116		ret = -1;
117		goto done;
118	case 0:
119		signal(SIGQUIT, SIG_DFL);
120		signal(SIGINT, SIG_DFL);
121		signal(SIGCHLD, SIG_DFL);
122
123		if ((pw = getpwnam(RELAYD_USER)) == NULL)
124			fatal("%s: getpwnam", __func__);
125		if (chdir("/") == -1)
126			fatal("%s: chdir(\"/\")", __func__);
127		if (setgroups(1, &pw->pw_gid) ||
128		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
129		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
130			fatal("%s: can't drop privileges", __func__);
131
132		/*
133		 * close fds before executing an external program, to
134		 * prevent access to internal fds, eg. IMSG connections
135		 * of internal processes.
136		 */
137		closefrom(STDERR_FILENO + 1);
138
139		execlp(file, file, arg, (char *)NULL);
140		_exit(0);
141		break;
142	default:
143		/* Kill the process after a timeout */
144		signal(SIGALRM, script_sig_alarm);
145		bzero(&it, sizeof(it));
146		bcopy(tv, &it.it_value, sizeof(it.it_value));
147		setitimer(ITIMER_REAL, &it, NULL);
148
149		waitpid(child, &status, 0);
150		break;
151	}
152
153	switch (ret) {
154	case -1:
155		ret = -1;
156		break;
157	default:
158		if (WIFEXITED(status))
159			ret = WEXITSTATUS(status);
160		else
161			ret = 0;
162	}
163
164 done:
165	/* Disable the process timeout timer */
166	bzero(&it, sizeof(it));
167	setitimer(ITIMER_REAL, &it, NULL);
168	child = -1;
169
170	signal(SIGQUIT, save_quit);
171	signal(SIGINT, save_int);
172	signal(SIGCHLD, save_chld);
173	signal(SIGALRM, SIG_DFL);
174
175	return (ret);
176}
177