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