xref: /openbsd/usr.sbin/relayd/check_script.c (revision 17df1aa7)
1 /*	$OpenBSD: check_script.c,v 1.10 2009/06/05 23:39:51 pyr Exp $	*/
2 
3 /*
4  * Copyright (c) 2007, 2008 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/param.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <sys/wait.h>
23 
24 #include <net/if.h>
25 
26 #include <limits.h>
27 #include <event.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <signal.h>
33 #include <pwd.h>
34 #include <err.h>
35 
36 #include <openssl/ssl.h>
37 
38 #include "relayd.h"
39 
40 void	 script_sig_alarm(int);
41 
42 extern struct imsgev		*iev_main;
43 pid_t				 child = -1;
44 
45 void
46 check_script(struct host *host)
47 {
48 	struct ctl_script	 scr;
49 
50 	host->last_up = host->up;
51 	host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE);
52 
53 	scr.host = host->conf.id;
54 	imsg_compose_event(iev_main, IMSG_SCRIPT, 0, 0, -1, &scr, sizeof(scr));
55 }
56 
57 void
58 script_done(struct relayd *env, struct ctl_script *scr)
59 {
60 	struct host		*host;
61 
62 	if ((host = host_find(env, scr->host)) == NULL)
63 		fatalx("hce_dispatch_parent: invalid host id");
64 
65 	if (scr->retval < 0)
66 		host->up = HOST_UNKNOWN;
67 	else if (scr->retval == 0)
68 		host->up = HOST_DOWN;
69 	else
70 		host->up = HOST_UP;
71 	host->flags |= F_CHECK_DONE;
72 
73 	hce_notify_done(host, host->up == HOST_UP ?
74 	    HCE_SCRIPT_OK : HCE_SCRIPT_FAIL);
75 }
76 
77 void
78 script_sig_alarm(int sig)
79 {
80 	int save_errno = errno;
81 
82 	if (child != -1)
83 		kill(child, SIGKILL);
84 	errno = save_errno;
85 }
86 
87 int
88 script_exec(struct relayd *env, struct ctl_script *scr)
89 {
90 	int			 status = 0, ret = 0;
91 	sig_t			 save_quit, save_int, save_chld;
92 	struct itimerval	 it;
93 	struct timeval		*tv;
94 	const char		*file, *arg;
95 	struct host		*host;
96 	struct table		*table;
97 	struct passwd		*pw;
98 
99 	if ((host = host_find(env, scr->host)) == NULL)
100 		fatalx("script_exec: invalid host id");
101 	if ((table = table_find(env, host->conf.tableid)) == NULL)
102 		fatalx("script_exec: invalid table id");
103 
104 	arg = host->conf.name;
105 	file = table->conf.path;
106 	tv = &table->conf.timeout;
107 
108 	save_quit = signal(SIGQUIT, SIG_IGN);
109 	save_int = signal(SIGINT, SIG_IGN);
110 	save_chld = signal(SIGCHLD, SIG_DFL);
111 
112 	switch (child = fork()) {
113 	case -1:
114 		ret = -1;
115 		goto done;
116 	case 0:
117 		signal(SIGQUIT, SIG_DFL);
118 		signal(SIGINT, SIG_DFL);
119 		signal(SIGCHLD, SIG_DFL);
120 
121 		if ((pw = getpwnam(RELAYD_USER)) == NULL)
122 			fatal("script_exec: getpwnam");
123 		if (chdir("/") == -1)
124 			fatal("script_exec: chdir(\"/\")");
125 		if (setgroups(1, &pw->pw_gid) ||
126 		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
127 		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
128 			fatal("script_exec: can't drop privileges");
129 
130 		/*
131 		 * close fds before executing an external program, to
132 		 * prevent access to internal fds, eg. IMSG connections
133 		 * of internal processes.
134 		 */
135 		closefrom(STDERR_FILENO + 1);
136 
137 		execlp(file, file, arg, (char *)NULL);
138 		_exit(0);
139 		break;
140 	default:
141 		/* Kill the process after a timeout */
142 		signal(SIGALRM, script_sig_alarm);
143 		bzero(&it, sizeof(it));
144 		bcopy(tv, &it.it_value, sizeof(it.it_value));
145 		setitimer(ITIMER_REAL, &it, NULL);
146 
147 		waitpid(child, &status, 0);
148 		break;
149 	}
150 
151 	switch (ret) {
152 	case -1:
153 		ret = -1;
154 		break;
155 	default:
156 		if (WIFEXITED(status))
157 			ret = WEXITSTATUS(status);
158 		else
159 			ret = -1;
160 	}
161 
162  done:
163 	/* Disable the process timeout timer */
164 	bzero(&it, sizeof(it));
165 	setitimer(ITIMER_REAL, &it, NULL);
166 	child = -1;
167 
168 	signal(SIGQUIT, save_quit);
169 	signal(SIGINT, save_int);
170 	signal(SIGCHLD, save_chld);
171 	signal(SIGALRM, SIG_DFL);
172 
173 	return (ret);
174 }
175