Blob


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