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