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 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 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 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 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