xref: /openbsd/usr.sbin/relayd/hce.c (revision 85a8c65f)
1*85a8c65fSreyk /*	$OpenBSD: hce.c,v 1.58 2011/05/05 12:01:43 reyk Exp $	*/
2feb9ff76Sreyk 
3feb9ff76Sreyk /*
436f5dc5eSpyr  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
5feb9ff76Sreyk  *
6feb9ff76Sreyk  * Permission to use, copy, modify, and distribute this software for any
7feb9ff76Sreyk  * purpose with or without fee is hereby granted, provided that the above
8feb9ff76Sreyk  * copyright notice and this permission notice appear in all copies.
9feb9ff76Sreyk  *
10feb9ff76Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11feb9ff76Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12feb9ff76Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13feb9ff76Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14feb9ff76Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15feb9ff76Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16feb9ff76Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17feb9ff76Sreyk  */
18feb9ff76Sreyk 
19feb9ff76Sreyk #include <sys/param.h>
200ca734d7Sreyk #include <sys/queue.h>
21feb9ff76Sreyk #include <sys/time.h>
22feb9ff76Sreyk #include <sys/stat.h>
23feb9ff76Sreyk #include <sys/socket.h>
24feb9ff76Sreyk #include <sys/un.h>
250ca734d7Sreyk 
260ca734d7Sreyk #include <net/if.h>
27feb9ff76Sreyk #include <netinet/in_systm.h>
28feb9ff76Sreyk #include <netinet/in.h>
29feb9ff76Sreyk #include <netinet/ip.h>
300ca734d7Sreyk 
31feb9ff76Sreyk #include <errno.h>
32feb9ff76Sreyk #include <event.h>
33feb9ff76Sreyk #include <fcntl.h>
34feb9ff76Sreyk #include <stdlib.h>
35feb9ff76Sreyk #include <string.h>
36feb9ff76Sreyk #include <unistd.h>
37feb9ff76Sreyk #include <err.h>
38feb9ff76Sreyk #include <pwd.h>
39feb9ff76Sreyk 
40e8fb3979Spyr #include <openssl/ssl.h>
41e8fb3979Spyr 
42748ceb64Sreyk #include "relayd.h"
43feb9ff76Sreyk 
44c99a0f3cSblambert __dead void hce_shutdown(void);
45feb9ff76Sreyk void	hce_sig_handler(int sig, short, void *);
46feb9ff76Sreyk void	hce_dispatch_imsg(int, short, void *);
47feb9ff76Sreyk void	hce_dispatch_parent(int, short, void *);
48feb9ff76Sreyk void	hce_launch_checks(int, short, void *);
49641b8bafSpyr void	hce_setup_events(void);
50641b8bafSpyr void	hce_disable_events(void);
51feb9ff76Sreyk 
52748ceb64Sreyk static struct relayd *env = NULL;
53f07d0e3bSpyr struct imsgev		*iev_pfe;
54f07d0e3bSpyr struct imsgev		*iev_main;
55780e8f79Spyr int			 running = 0;
56feb9ff76Sreyk 
57feb9ff76Sreyk void
58feb9ff76Sreyk hce_sig_handler(int sig, short event, void *arg)
59feb9ff76Sreyk {
60feb9ff76Sreyk 	switch (sig) {
61feb9ff76Sreyk 	case SIGINT:
62feb9ff76Sreyk 	case SIGTERM:
63feb9ff76Sreyk 		hce_shutdown();
64780e8f79Spyr 		break;
65c7867281Sreyk 	case SIGCHLD:
66c7867281Sreyk 	case SIGHUP:
67c7867281Sreyk 	case SIGPIPE:
68c7867281Sreyk 		/* ignore */
69c7867281Sreyk 		break;
70feb9ff76Sreyk 	default:
71feb9ff76Sreyk 		fatalx("hce_sig_handler: unexpected signal");
72feb9ff76Sreyk 	}
73feb9ff76Sreyk }
74feb9ff76Sreyk 
75feb9ff76Sreyk pid_t
76748ceb64Sreyk hce(struct relayd *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2],
77053249cdSpyr     int pipe_parent2relay[RELAY_MAXPROC][2], int pipe_pfe2hce[2],
782edd718bSreyk     int pipe_pfe2relay[RELAY_MAXPROC][2])
79feb9ff76Sreyk {
80feb9ff76Sreyk 	pid_t		 pid;
81feb9ff76Sreyk 	struct passwd	*pw;
822edd718bSreyk 	int		 i;
83feb9ff76Sreyk 
84feb9ff76Sreyk 	switch (pid = fork()) {
85feb9ff76Sreyk 	case -1:
86feb9ff76Sreyk 		fatal("hce: cannot fork");
87feb9ff76Sreyk 	case 0:
88feb9ff76Sreyk 		break;
89feb9ff76Sreyk 	default:
90feb9ff76Sreyk 		return (pid);
91feb9ff76Sreyk 	}
92feb9ff76Sreyk 
93feb9ff76Sreyk 	env = x_env;
949591a9f7Spyr 	purge_config(env, PURGE_RDRS|PURGE_RELAYS|PURGE_PROTOS);
95feb9ff76Sreyk 
96748ceb64Sreyk 	if ((pw = getpwnam(RELAYD_USER)) == NULL)
97feb9ff76Sreyk 		fatal("hce: getpwnam");
98feb9ff76Sreyk 
99adf98c11Spyr #ifndef DEBUG
100feb9ff76Sreyk 	if (chroot(pw->pw_dir) == -1)
101feb9ff76Sreyk 		fatal("hce: chroot");
102feb9ff76Sreyk 	if (chdir("/") == -1)
103feb9ff76Sreyk 		fatal("hce: chdir(\"/\")");
104adf98c11Spyr #else
105adf98c11Spyr #warning disabling privilege revocation and chroot in DEBUG mode
106adf98c11Spyr #endif
107feb9ff76Sreyk 
108feb9ff76Sreyk 	setproctitle("host check engine");
109748ceb64Sreyk 	relayd_process = PROC_HCE;
110feb9ff76Sreyk 
11101d85ec5Sreyk 	/* this is needed for icmp tests */
11201d85ec5Sreyk 	icmp_init(env);
11301d85ec5Sreyk 
114adf98c11Spyr #ifndef DEBUG
115feb9ff76Sreyk 	if (setgroups(1, &pw->pw_gid) ||
116feb9ff76Sreyk 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
117feb9ff76Sreyk 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
118feb9ff76Sreyk 		fatal("hce: can't drop privileges");
119adf98c11Spyr #endif
120feb9ff76Sreyk 
121641b8bafSpyr 	event_init();
122feb9ff76Sreyk 
123bb8fa3feSreyk 	/* Allow maximum available sockets for TCP checks */
124bb8fa3feSreyk 	socket_rlimit(-1);
125bb8fa3feSreyk 
126f07d0e3bSpyr 	if ((iev_pfe = calloc(1, sizeof(struct imsgev))) == NULL ||
127f07d0e3bSpyr 	    (iev_main = calloc(1, sizeof(struct imsgev))) == NULL)
128641b8bafSpyr 		fatal("hce");
129f07d0e3bSpyr 	imsg_init(&iev_pfe->ibuf, pipe_pfe2hce[0]);
130f07d0e3bSpyr 	iev_pfe->handler = hce_dispatch_imsg;
131f07d0e3bSpyr 	imsg_init(&iev_main->ibuf, pipe_parent2hce[1]);
132f07d0e3bSpyr 	iev_main->handler = hce_dispatch_parent;
133641b8bafSpyr 
134f07d0e3bSpyr 	iev_pfe->events = EV_READ;
135f07d0e3bSpyr 	event_set(&iev_pfe->ev, iev_pfe->ibuf.fd, iev_pfe->events,
136f07d0e3bSpyr 	    iev_pfe->handler, iev_pfe);
137f07d0e3bSpyr 	event_add(&iev_pfe->ev, NULL);
138641b8bafSpyr 
139f07d0e3bSpyr 	iev_main->events = EV_READ;
140f07d0e3bSpyr 	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
141f07d0e3bSpyr 	    iev_main->handler, iev_main);
142f07d0e3bSpyr 	event_add(&iev_main->ev, NULL);
143641b8bafSpyr 
144c7867281Sreyk 	signal_set(&env->sc_evsigint, SIGINT, hce_sig_handler, env);
145c7867281Sreyk 	signal_set(&env->sc_evsigterm, SIGTERM, hce_sig_handler, env);
146c7867281Sreyk 	signal_set(&env->sc_evsigchld, SIGCHLD, hce_sig_handler, env);
147c7867281Sreyk 	signal_set(&env->sc_evsighup, SIGHUP, hce_sig_handler, env);
148c7867281Sreyk 	signal_set(&env->sc_evsigpipe, SIGPIPE, hce_sig_handler, env);
149c7867281Sreyk 
150c7867281Sreyk 	signal_add(&env->sc_evsigint, NULL);
151c7867281Sreyk 	signal_add(&env->sc_evsigterm, NULL);
152c7867281Sreyk 	signal_add(&env->sc_evsigchld, NULL);
153c7867281Sreyk 	signal_add(&env->sc_evsighup, NULL);
154c7867281Sreyk 	signal_add(&env->sc_evsigpipe, NULL);
155feb9ff76Sreyk 
156feb9ff76Sreyk 	/* setup pipes */
157feb9ff76Sreyk 	close(pipe_pfe2hce[1]);
158feb9ff76Sreyk 	close(pipe_parent2hce[0]);
159feb9ff76Sreyk 	close(pipe_parent2pfe[0]);
160feb9ff76Sreyk 	close(pipe_parent2pfe[1]);
16135d10c30Sreyk 	for (i = 0; i < env->sc_prefork_relay; i++) {
162053249cdSpyr 		close(pipe_parent2relay[i][0]);
163053249cdSpyr 		close(pipe_parent2relay[i][1]);
1642edd718bSreyk 		close(pipe_pfe2relay[i][0]);
1652edd718bSreyk 		close(pipe_pfe2relay[i][1]);
1662edd718bSreyk 	}
167feb9ff76Sreyk 
168641b8bafSpyr 	hce_setup_events();
169641b8bafSpyr 	event_dispatch();
170780e8f79Spyr 	hce_shutdown();
171780e8f79Spyr 
172780e8f79Spyr 	return (0);
173780e8f79Spyr }
174780e8f79Spyr 
175780e8f79Spyr void
176641b8bafSpyr hce_setup_events(void)
177780e8f79Spyr {
178780e8f79Spyr 	struct timeval	 tv;
179780e8f79Spyr 	struct table	*table;
180780e8f79Spyr 
181f07d0e3bSpyr 	snmp_init(env, iev_main);
182fe250497Sreyk 
18335d10c30Sreyk 	if (!TAILQ_EMPTY(env->sc_tables)) {
18435d10c30Sreyk 		evtimer_set(&env->sc_ev, hce_launch_checks, env);
18501d85ec5Sreyk 		bzero(&tv, sizeof(tv));
18635d10c30Sreyk 		evtimer_add(&env->sc_ev, &tv);
1872edd718bSreyk 	}
188feb9ff76Sreyk 
18935d10c30Sreyk 	if (env->sc_flags & F_SSL) {
190e8fb3979Spyr 		ssl_init(env);
19135d10c30Sreyk 		TAILQ_FOREACH(table, env->sc_tables, entry) {
19268b79041Spyr 			if (!(table->conf.flags & F_SSL))
193e8fb3979Spyr 				continue;
194e8fb3979Spyr 			table->ssl_ctx = ssl_ctx_create(env);
195e8fb3979Spyr 		}
196e8fb3979Spyr 	}
197641b8bafSpyr }
198641b8bafSpyr 
199641b8bafSpyr void
200641b8bafSpyr hce_disable_events(void)
201641b8bafSpyr {
202641b8bafSpyr 	struct table	*table;
203641b8bafSpyr 	struct host	*host;
204641b8bafSpyr 
20535d10c30Sreyk 	evtimer_del(&env->sc_ev);
20635d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
207641b8bafSpyr 		TAILQ_FOREACH(host, &table->hosts, entry) {
208c0dc99f6Sreyk 			host->he = HCE_ABORT;
209641b8bafSpyr 			event_del(&host->cte.ev);
210641b8bafSpyr 			close(host->cte.s);
211641b8bafSpyr 		}
212641b8bafSpyr 	}
21335d10c30Sreyk 	if (env->sc_has_icmp) {
21435d10c30Sreyk 		event_del(&env->sc_icmp_send.ev);
21535d10c30Sreyk 		event_del(&env->sc_icmp_recv.ev);
216641b8bafSpyr 	}
21735d10c30Sreyk 	if (env->sc_has_icmp6) {
21835d10c30Sreyk 		event_del(&env->sc_icmp6_send.ev);
21935d10c30Sreyk 		event_del(&env->sc_icmp6_recv.ev);
220641b8bafSpyr 	}
221feb9ff76Sreyk }
222feb9ff76Sreyk 
223feb9ff76Sreyk void
224feb9ff76Sreyk hce_launch_checks(int fd, short event, void *arg)
225feb9ff76Sreyk {
226feb9ff76Sreyk 	struct host		*host;
227feb9ff76Sreyk 	struct table		*table;
22801d85ec5Sreyk 	struct timeval		 tv;
22901d85ec5Sreyk 
23001d85ec5Sreyk 	/*
23101d85ec5Sreyk 	 * notify pfe checks are done and schedule next check
23201d85ec5Sreyk 	 */
233f07d0e3bSpyr 	imsg_compose_event(iev_pfe, IMSG_SYNC, 0, 0, -1, NULL, 0);
23435d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
23501d85ec5Sreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
236c0dc99f6Sreyk 			if ((host->flags & F_CHECK_DONE) == 0)
237c0dc99f6Sreyk 				host->he = HCE_INTERVAL_TIMEOUT;
23801d85ec5Sreyk 			host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE);
23901d85ec5Sreyk 			event_del(&host->cte.ev);
24001d85ec5Sreyk 		}
24101d85ec5Sreyk 	}
24201d85ec5Sreyk 
2436a899317Sthib 	if (gettimeofday(&tv, NULL) == -1)
24401d85ec5Sreyk 		fatal("hce_launch_checks: gettimeofday");
245feb9ff76Sreyk 
24635d10c30Sreyk 	TAILQ_FOREACH(table, env->sc_tables, entry) {
24768b79041Spyr 		if (table->conf.flags & F_DISABLE)
2487e351ffdSreyk 			continue;
24918de159aSpyr 		if (table->conf.skip_cnt) {
25018de159aSpyr 			if (table->skipped++ > table->conf.skip_cnt)
25118de159aSpyr 				table->skipped = 0;
25218de159aSpyr 			if (table->skipped != 1)
25318de159aSpyr 				continue;
25418de159aSpyr 		}
25568b79041Spyr 		if (table->conf.check == CHECK_NOCHECK)
2567e351ffdSreyk 			fatalx("hce_launch_checks: unknown check type");
25701d85ec5Sreyk 
2587e351ffdSreyk 		TAILQ_FOREACH(host, &table->hosts, entry) {
259c723f8edSreyk 			if (host->flags & F_DISABLE || host->conf.parentid)
2607e351ffdSreyk 				continue;
261a7da800aSsthen 			bcopy(&tv, &host->cte.tv_start,
262a7da800aSsthen 			    sizeof(host->cte.tv_start));
2634156152fSreyk 			switch (table->conf.check) {
2644156152fSreyk 			case CHECK_ICMP:
26501d85ec5Sreyk 				schedule_icmp(env, host);
2664156152fSreyk 				break;
2674156152fSreyk 			case CHECK_SCRIPT:
2684156152fSreyk 				check_script(host);
2694156152fSreyk 				break;
2704156152fSreyk 			default:
27101d85ec5Sreyk 				/* Any other TCP-style checks */
2727e351ffdSreyk 				host->last_up = host->up;
2737e351ffdSreyk 				host->cte.host = host;
2747e351ffdSreyk 				host->cte.table = table;
2757e351ffdSreyk 				check_tcp(&host->cte);
2764156152fSreyk 				break;
2774156152fSreyk 			}
2787e351ffdSreyk 		}
2797e351ffdSreyk 	}
28001d85ec5Sreyk 	check_icmp(env, &tv);
28101d85ec5Sreyk 
28235d10c30Sreyk 	bcopy(&env->sc_interval, &tv, sizeof(tv));
28335d10c30Sreyk 	evtimer_add(&env->sc_ev, &tv);
2847e351ffdSreyk }
2857e351ffdSreyk 
2867e351ffdSreyk void
287c0dc99f6Sreyk hce_notify_done(struct host *host, enum host_error he)
2887e351ffdSreyk {
289609cf3a7Sreyk 	struct table		*table;
2907e351ffdSreyk 	struct ctl_status	 st;
291609cf3a7Sreyk 	struct timeval		 tv_now, tv_dur;
292609cf3a7Sreyk 	u_long			 duration;
293609cf3a7Sreyk 	u_int			 logopt;
294c723f8edSreyk 	struct host		*h;
295c723f8edSreyk 	int			 hostup;
296c0dc99f6Sreyk 	const char		*msg;
297c723f8edSreyk 
298c723f8edSreyk 	hostup = host->up;
299c0dc99f6Sreyk 	host->he = he;
3007e351ffdSreyk 
3012edd718bSreyk 	if (host->up == HOST_DOWN && host->retry_cnt) {
302*85a8c65fSreyk 		log_debug("%s: host %s retry %d", __func__,
30368b79041Spyr 		    host->conf.name, host->retry_cnt);
304bd2508bcSreyk 		host->up = host->last_up;
3052edd718bSreyk 		host->retry_cnt--;
3062edd718bSreyk 	} else
30768b79041Spyr 		host->retry_cnt = host->conf.retry;
3082edd718bSreyk 	if (host->up != HOST_UNKNOWN) {
3092edd718bSreyk 		host->check_cnt++;
3102edd718bSreyk 		if (host->up == HOST_UP)
3112edd718bSreyk 			host->up_cnt++;
3122edd718bSreyk 	}
31368b79041Spyr 	st.id = host->conf.id;
314feb9ff76Sreyk 	st.up = host->up;
3152edd718bSreyk 	st.check_cnt = host->check_cnt;
3162edd718bSreyk 	st.retry_cnt = host->retry_cnt;
317c0dc99f6Sreyk 	st.he = he;
31801d85ec5Sreyk 	host->flags |= (F_CHECK_SENT|F_CHECK_DONE);
319c0dc99f6Sreyk 	msg = host_error(he);
3207e351ffdSreyk 	if (msg)
321*85a8c65fSreyk 		log_debug("%s: %s (%s)", __func__, host->conf.name, msg);
322609cf3a7Sreyk 
323f07d0e3bSpyr 	imsg_compose_event(iev_pfe, IMSG_HOST_STATUS,
324069bf5e4Spyr 	    0, 0, -1, &st, sizeof(st));
3252edd718bSreyk 	if (host->up != host->last_up)
326748ceb64Sreyk 		logopt = RELAYD_OPT_LOGUPDATE;
3272edd718bSreyk 	else
328748ceb64Sreyk 		logopt = RELAYD_OPT_LOGNOTIFY;
329609cf3a7Sreyk 
3306a899317Sthib 	if (gettimeofday(&tv_now, NULL) == -1)
331609cf3a7Sreyk 		fatal("hce_notify_done: gettimeofday");
332609cf3a7Sreyk 	timersub(&tv_now, &host->cte.tv_start, &tv_dur);
333609cf3a7Sreyk 	if (timercmp(&host->cte.tv_start, &tv_dur, >))
334609cf3a7Sreyk 		duration = (tv_dur.tv_sec * 1000) + (tv_dur.tv_usec / 1000.0);
335609cf3a7Sreyk 	else
336609cf3a7Sreyk 		duration = 0;
337609cf3a7Sreyk 
33868b79041Spyr 	if ((table = table_find(env, host->conf.tableid)) == NULL)
3392edd718bSreyk 		fatalx("hce_notify_done: invalid table id");
3402edd718bSreyk 
34135d10c30Sreyk 	if (env->sc_opts & logopt) {
3422edd718bSreyk 		log_info("host %s, check %s%s (%lums), state %s -> %s, "
3432edd718bSreyk 		    "availability %s",
34468b79041Spyr 		    host->conf.name, table_check(table->conf.check),
34568b79041Spyr 		    (table->conf.flags & F_SSL) ? " use ssl" : "", duration,
3462edd718bSreyk 		    host_status(host->last_up), host_status(host->up),
3472edd718bSreyk 		    print_availability(host->check_cnt, host->up_cnt));
348feb9ff76Sreyk 	}
349fe250497Sreyk 
350fe250497Sreyk 	if (host->last_up != host->up)
351fe250497Sreyk 		snmp_hosttrap(table, host);
352fe250497Sreyk 
353609cf3a7Sreyk 	host->last_up = host->up;
354c723f8edSreyk 
3551584e35dSreyk 	if (SLIST_EMPTY(&host->children))
356c723f8edSreyk 		return;
357c723f8edSreyk 
358c723f8edSreyk 	/* Notify for all other hosts that inherit the state from this one */
3591584e35dSreyk 	SLIST_FOREACH(h, &host->children, child) {
360c723f8edSreyk 		h->up = hostup;
361c0dc99f6Sreyk 		hce_notify_done(h, he);
362c723f8edSreyk 	}
363feb9ff76Sreyk }
364feb9ff76Sreyk 
365feb9ff76Sreyk void
366feb9ff76Sreyk hce_shutdown(void)
367feb9ff76Sreyk {
368feb9ff76Sreyk 	log_info("host check engine exiting");
369feb9ff76Sreyk 	_exit(0);
370feb9ff76Sreyk }
371feb9ff76Sreyk 
372feb9ff76Sreyk void
373feb9ff76Sreyk hce_dispatch_imsg(int fd, short event, void *ptr)
374feb9ff76Sreyk {
375f07d0e3bSpyr 	struct imsgev		*iev;
376feb9ff76Sreyk 	struct imsgbuf		*ibuf;
377feb9ff76Sreyk 	struct imsg		 imsg;
378feb9ff76Sreyk 	ssize_t			 n;
379feb9ff76Sreyk 	objid_t			 id;
380feb9ff76Sreyk 	struct host		*host;
381feb9ff76Sreyk 	struct table		*table;
382f579a0f7Sjsg 	int			 verbose;
383feb9ff76Sreyk 
384f07d0e3bSpyr 	iev = ptr;
385f07d0e3bSpyr 	ibuf = &iev->ibuf;
3862668107aSreyk 
3872668107aSreyk 	if (event & EV_READ) {
388feb9ff76Sreyk 		if ((n = imsg_read(ibuf)) == -1)
389feb9ff76Sreyk 			fatal("hce_dispatch_imsg: imsg_read_error");
390ddfd9103Spyr 		if (n == 0) {
391ddfd9103Spyr 			/* this pipe is dead, so remove the event handler */
392f07d0e3bSpyr 			event_del(&iev->ev);
393ddfd9103Spyr 			event_loopexit(NULL);
394ddfd9103Spyr 			return;
395ddfd9103Spyr 		}
3962668107aSreyk 	}
3972668107aSreyk 
3982668107aSreyk 	if (event & EV_WRITE) {
399feb9ff76Sreyk 		if (msgbuf_write(&ibuf->w) == -1)
400feb9ff76Sreyk 			fatal("hce_dispatch_imsg: msgbuf_write");
401feb9ff76Sreyk 	}
402feb9ff76Sreyk 
403feb9ff76Sreyk 	for (;;) {
404feb9ff76Sreyk 		if ((n = imsg_get(ibuf, &imsg)) == -1)
405feb9ff76Sreyk 			fatal("hce_dispatch_imsg: imsg_read error");
406feb9ff76Sreyk 		if (n == 0)
407feb9ff76Sreyk 			break;
408feb9ff76Sreyk 
409feb9ff76Sreyk 		switch (imsg.hdr.type) {
410feb9ff76Sreyk 		case IMSG_HOST_DISABLE:
411feb9ff76Sreyk 			memcpy(&id, imsg.data, sizeof(id));
412feb9ff76Sreyk 			if ((host = host_find(env, id)) == NULL)
413feb9ff76Sreyk 				fatalx("hce_dispatch_imsg: desynchronized");
414feb9ff76Sreyk 			host->flags |= F_DISABLE;
415feb9ff76Sreyk 			host->up = HOST_UNKNOWN;
4162edd718bSreyk 			host->check_cnt = 0;
4172edd718bSreyk 			host->up_cnt = 0;
418c0dc99f6Sreyk 			host->he = HCE_NONE;
419feb9ff76Sreyk 			break;
420feb9ff76Sreyk 		case IMSG_HOST_ENABLE:
421feb9ff76Sreyk 			memcpy(&id, imsg.data, sizeof(id));
422feb9ff76Sreyk 			if ((host = host_find(env, id)) == NULL)
423feb9ff76Sreyk 				fatalx("hce_dispatch_imsg: desynchronized");
424feb9ff76Sreyk 			host->flags &= ~(F_DISABLE);
425feb9ff76Sreyk 			host->up = HOST_UNKNOWN;
426c0dc99f6Sreyk 			host->he = HCE_NONE;
427feb9ff76Sreyk 			break;
428feb9ff76Sreyk 		case IMSG_TABLE_DISABLE:
429feb9ff76Sreyk 			memcpy(&id, imsg.data, sizeof(id));
430feb9ff76Sreyk 			if ((table = table_find(env, id)) == NULL)
431feb9ff76Sreyk 				fatalx("hce_dispatch_imsg: desynchronized");
43268b79041Spyr 			table->conf.flags |= F_DISABLE;
433feb9ff76Sreyk 			TAILQ_FOREACH(host, &table->hosts, entry)
434feb9ff76Sreyk 				host->up = HOST_UNKNOWN;
435feb9ff76Sreyk 			break;
436feb9ff76Sreyk 		case IMSG_TABLE_ENABLE:
437feb9ff76Sreyk 			memcpy(&id, imsg.data, sizeof(id));
438feb9ff76Sreyk 			if ((table = table_find(env, id)) == NULL)
439feb9ff76Sreyk 				fatalx("hce_dispatch_imsg: desynchronized");
44068b79041Spyr 			table->conf.flags &= ~(F_DISABLE);
441feb9ff76Sreyk 			TAILQ_FOREACH(host, &table->hosts, entry)
442feb9ff76Sreyk 				host->up = HOST_UNKNOWN;
443feb9ff76Sreyk 			break;
444cd65ce7bSpyr 		case IMSG_CTL_POLL:
44535d10c30Sreyk 			evtimer_del(&env->sc_ev);
44635d10c30Sreyk 			TAILQ_FOREACH(table, env->sc_tables, entry)
447f98352d8Spyr 				table->skipped = 0;
448cd65ce7bSpyr 			hce_launch_checks(-1, EV_TIMEOUT, env);
449cd65ce7bSpyr 			break;
450f579a0f7Sjsg 		case IMSG_CTL_LOG_VERBOSE:
451f579a0f7Sjsg 			memcpy(&verbose, imsg.data, sizeof(verbose));
452f579a0f7Sjsg 			log_verbose(verbose);
453f579a0f7Sjsg 			break;
454feb9ff76Sreyk 		default:
455*85a8c65fSreyk 			log_debug("%s: unexpected imsg %d", __func__,
456feb9ff76Sreyk 			    imsg.hdr.type);
457feb9ff76Sreyk 			break;
458feb9ff76Sreyk 		}
459feb9ff76Sreyk 		imsg_free(&imsg);
460feb9ff76Sreyk 	}
461f07d0e3bSpyr 	imsg_event_add(iev);
462feb9ff76Sreyk }
463feb9ff76Sreyk 
464feb9ff76Sreyk void
465feb9ff76Sreyk hce_dispatch_parent(int fd, short event, void * ptr)
466feb9ff76Sreyk {
467f07d0e3bSpyr 	struct imsgev		*iev;
468feb9ff76Sreyk 	struct imsgbuf		*ibuf;
469feb9ff76Sreyk 	struct imsg		 imsg;
4704156152fSreyk 	struct ctl_script	 scr;
471feb9ff76Sreyk 	ssize_t			 n;
472adf98c11Spyr 	size_t			 len;
473adf98c11Spyr 	static struct table	*table = NULL;
474212c0cdeSreyk 	struct host		*host, *parent;
475feb9ff76Sreyk 
476f07d0e3bSpyr 	iev = ptr;
477f07d0e3bSpyr 	ibuf = &iev->ibuf;
4782668107aSreyk 
4792668107aSreyk 	if (event & EV_READ) {
480feb9ff76Sreyk 		if ((n = imsg_read(ibuf)) == -1)
481feb9ff76Sreyk 			fatal("hce_dispatch_parent: imsg_read error");
482ddfd9103Spyr 		if (n == 0) {
483ddfd9103Spyr 			/* this pipe is dead, so remove the event handler */
484f07d0e3bSpyr 			event_del(&iev->ev);
485ddfd9103Spyr 			event_loopexit(NULL);
486ddfd9103Spyr 			return;
487ddfd9103Spyr 		}
4882668107aSreyk 	}
4892668107aSreyk 
4902668107aSreyk 	if (event & EV_WRITE) {
491feb9ff76Sreyk 		if (msgbuf_write(&ibuf->w) == -1)
492feb9ff76Sreyk 			fatal("hce_dispatch_parent: msgbuf_write");
493feb9ff76Sreyk 	}
494feb9ff76Sreyk 
495feb9ff76Sreyk 	for (;;) {
496feb9ff76Sreyk 		if ((n = imsg_get(ibuf, &imsg)) == -1)
497feb9ff76Sreyk 			fatal("hce_dispatch_parent: imsg_read error");
498feb9ff76Sreyk 		if (n == 0)
499feb9ff76Sreyk 			break;
500feb9ff76Sreyk 
501feb9ff76Sreyk 		switch (imsg.hdr.type) {
5024156152fSreyk 		case IMSG_SCRIPT:
5034156152fSreyk 			if (imsg.hdr.len - IMSG_HEADER_SIZE !=
5044156152fSreyk 			    sizeof(scr))
5054156152fSreyk 				fatalx("hce_dispatch_parent: "
5064156152fSreyk 				    "invalid size of script request");
5074156152fSreyk 			bcopy(imsg.data, &scr, sizeof(scr));
5084156152fSreyk 			script_done(env, &scr);
5094156152fSreyk 			break;
510adf98c11Spyr 		case IMSG_RECONF:
511*85a8c65fSreyk 			log_debug("%s: reloading configuration", __func__);
512adf98c11Spyr 			if (imsg.hdr.len !=
513748ceb64Sreyk 			    sizeof(struct relayd) + IMSG_HEADER_SIZE)
514adf98c11Spyr 				fatalx("corrupted reload data");
515adf98c11Spyr 			hce_disable_events();
516adf98c11Spyr 			purge_config(env, PURGE_TABLES);
517748ceb64Sreyk 			merge_config(env, (struct relayd *)imsg.data);
518adf98c11Spyr 
51935d10c30Sreyk 			env->sc_tables = calloc(1, sizeof(*env->sc_tables));
52035d10c30Sreyk 			if (env->sc_tables == NULL)
521adf98c11Spyr 				fatal(NULL);
522adf98c11Spyr 
52335d10c30Sreyk 			TAILQ_INIT(env->sc_tables);
524adf98c11Spyr 			break;
525adf98c11Spyr 		case IMSG_RECONF_TABLE:
526adf98c11Spyr 			if ((table = calloc(1, sizeof(*table))) == NULL)
527adf98c11Spyr 				fatal(NULL);
528adf98c11Spyr 			memcpy(&table->conf, imsg.data, sizeof(table->conf));
529adf98c11Spyr 			TAILQ_INIT(&table->hosts);
53035d10c30Sreyk 			TAILQ_INSERT_TAIL(env->sc_tables, table, entry);
531adf98c11Spyr 			break;
532adf98c11Spyr 		case IMSG_RECONF_SENDBUF:
533adf98c11Spyr 			len = imsg.hdr.len - IMSG_HEADER_SIZE;
534adf98c11Spyr 			table->sendbuf = calloc(1, len);
535adf98c11Spyr 			(void)strlcpy(table->sendbuf, (char *)imsg.data, len);
536adf98c11Spyr 			break;
537adf98c11Spyr 		case IMSG_RECONF_HOST:
538adf98c11Spyr 			if ((host = calloc(1, sizeof(*host))) == NULL)
539adf98c11Spyr 				fatal(NULL);
540adf98c11Spyr 			memcpy(&host->conf, imsg.data, sizeof(host->conf));
541adf98c11Spyr 			host->tablename = table->conf.name;
542adf98c11Spyr 			TAILQ_INSERT_TAIL(&table->hosts, host, entry);
543212c0cdeSreyk 			if (host->conf.parentid) {
544212c0cdeSreyk 				parent = host_find(env, host->conf.parentid);
545212c0cdeSreyk 				SLIST_INSERT_HEAD(&parent->children,
546212c0cdeSreyk 				    host, child);
547212c0cdeSreyk 			}
548adf98c11Spyr 			break;
549adf98c11Spyr 		case IMSG_RECONF_END:
550*85a8c65fSreyk 			log_warnx("%s: configuration reloaded", __func__);
551adf98c11Spyr 			hce_setup_events();
552adf98c11Spyr 			break;
553feb9ff76Sreyk 		default:
554*85a8c65fSreyk 			log_debug("%s: unexpected imsg %d", __func__,
555feb9ff76Sreyk 			    imsg.hdr.type);
556feb9ff76Sreyk 			break;
557feb9ff76Sreyk 		}
558feb9ff76Sreyk 		imsg_free(&imsg);
559feb9ff76Sreyk 	}
560f07d0e3bSpyr 	imsg_event_add(iev);
561feb9ff76Sreyk }
562