xref: /openbsd-src/usr.sbin/relayd/check_script.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
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 
31 void	 script_sig_alarm(int);
32 
33 pid_t			 child = -1;
34 
35 void
36 check_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 
58 void
59 script_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 
78 void
79 script_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 
88 int
89 script_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