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