hastd.c revision 218375
1233447Sedwin/*-
2192886Sedwin * Copyright (c) 2009-2010 The FreeBSD Foundation
3192886Sedwin * Copyright (c) 2010-2011 Pawel Jakub Dawidek <pjd@FreeBSD.org>
464499Swollman * All rights reserved.
52742Swollman *
62742Swollman * This software was developed by Pawel Jakub Dawidek under sponsorship from
72742Swollman * the FreeBSD Foundation.
82742Swollman *
9158421Swollman * Redistribution and use in source and binary forms, with or without
102742Swollman * modification, are permitted provided that the following conditions
112742Swollman * are met:
12158421Swollman * 1. Redistributions of source code must retain the above copyright
13158421Swollman *    notice, this list of conditions and the following disclaimer.
142742Swollman * 2. Redistributions in binary form must reproduce the above copyright
1586222Swollman *    notice, this list of conditions and the following disclaimer in the
1620094Swollman *    documentation and/or other materials provided with the distribution.
1720094Swollman *
1820094Swollman * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
1920094Swollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2020094Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21158421Swollman * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22158421Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2320094Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
242742Swollman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
252742Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
262742Swollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
272742Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
282742Swollman * SUCH DAMAGE.
2958787Sru */
302742Swollman
312742Swollman#include <sys/cdefs.h>
322742Swollman__FBSDID("$FreeBSD: head/sbin/hastd/hastd.c 218375 2011-02-06 14:09:19Z pjd $");
332742Swollman
34114173Swollman#include <sys/param.h>
35114173Swollman#include <sys/linker.h>
36114173Swollman#include <sys/module.h>
37114173Swollman#include <sys/stat.h>
38114173Swollman#include <sys/wait.h>
39114173Swollman
40114173Swollman#include <err.h>
41114173Swollman#include <errno.h>
42114173Swollman#include <libutil.h>
43114173Swollman#include <signal.h>
44114173Swollman#include <stdbool.h>
45114173Swollman#include <stdio.h>
46114173Swollman#include <stdlib.h>
47114173Swollman#include <string.h>
48149590Swollman#include <sysexits.h>
49149590Swollman#include <unistd.h>
50114173Swollman
512742Swollman#include <activemap.h>
529908Swollman#include <pjdlog.h>
532742Swollman
542742Swollman#include "control.h"
552742Swollman#include "event.h"
562742Swollman#include "hast.h"
572742Swollman#include "hast_proto.h"
582742Swollman#include "hastd.h"
592742Swollman#include "hooks.h"
602742Swollman#include "subr.h"
612742Swollman
6220094Swollman/* Path to configuration file. */
632742Swollmanconst char *cfgpath = HAST_CONFIG;
6420094Swollman/* Hastd configuration. */
65158421Swollmanstatic struct hastd_config *cfg;
6620094Swollman/* Was SIGINT or SIGTERM signal received? */
6720094Swollmanbool sigexit_received = false;
6820094Swollman/* PID file handle. */
6920094Swollmanstruct pidfh *pfh;
7020094Swollman
7120094Swollman/* How often check for hooks running for too long. */
7220094Swollman#define	REPORT_INTERVAL	5
7320094Swollman
7420094Swollmanstatic void
7520094Swollmanusage(void)
7620094Swollman{
7720094Swollman
7820094Swollman	errx(EX_USAGE, "[-dFh] [-c config] [-P pidfile]");
792742Swollman}
802742Swollman
812742Swollmanstatic void
822742Swollmang_gate_load(void)
8319878Swollman{
842742Swollman
852742Swollman	if (modfind("g_gate") == -1) {
862742Swollman		/* Not present in kernel, try loading it. */
87158421Swollman		if (kldload("geom_gate") == -1 || modfind("g_gate") == -1) {
88158421Swollman			if (errno != EEXIST) {
89158421Swollman				pjdlog_exit(EX_OSERR,
90158421Swollman				    "Unable to load geom_gate module");
91158421Swollman			}
92153670Swollman		}
9343014Swollman	}
9443014Swollman}
9543014Swollman
96233447Sedwinvoid
97233447Sedwindescriptors_cleanup(struct hast_resource *res)
98233447Sedwin{
99233447Sedwin	struct hast_resource *tres;
100233447Sedwin
101233447Sedwin	TAILQ_FOREACH(tres, &cfg->hc_resources, hr_next) {
102233447Sedwin		if (tres == res) {
103233447Sedwin			PJDLOG_VERIFY(res->hr_role == HAST_ROLE_SECONDARY ||
104233447Sedwin			    (res->hr_remotein == NULL &&
105233447Sedwin			     res->hr_remoteout == NULL));
106233447Sedwin			continue;
107233447Sedwin		}
108233447Sedwin		if (tres->hr_remotein != NULL)
109233447Sedwin			proto_close(tres->hr_remotein);
110233447Sedwin		if (tres->hr_remoteout != NULL)
1112742Swollman			proto_close(tres->hr_remoteout);
1122742Swollman		if (tres->hr_ctrl != NULL)
11319878Swollman			proto_close(tres->hr_ctrl);
11419878Swollman		if (tres->hr_event != NULL)
11519878Swollman			proto_close(tres->hr_event);
11658787Sru		if (tres->hr_conn != NULL)
11743014Swollman			proto_close(tres->hr_conn);
118233447Sedwin	}
119233447Sedwin	if (cfg->hc_controlin != NULL)
1202742Swollman		proto_close(cfg->hc_controlin);
1212742Swollman	proto_close(cfg->hc_controlconn);
122153670Swollman	proto_close(cfg->hc_listenconn);
123153670Swollman	(void)pidfile_close(pfh);
124153670Swollman	hook_fini();
12543014Swollman	pjdlog_fini();
126153670Swollman}
127153670Swollman
1282742Swollmanstatic const char *
1292742Swollmandtype2str(mode_t mode)
13019878Swollman{
13119878Swollman
13219878Swollman	if (S_ISBLK(mode))
133149514Swollman		return ("block device");
13420094Swollman	else if (S_ISCHR(mode))
13543014Swollman		return ("character device");
13643014Swollman	else if (S_ISDIR(mode))
1372742Swollman		return ("directory");
1382742Swollman	else if (S_ISFIFO(mode))
1392742Swollman		return ("pipe or FIFO");
14067578Swollman	else if (S_ISLNK(mode))
1412742Swollman		return ("symbolic link");
1422742Swollman	else if (S_ISREG(mode))
1432742Swollman		return ("regular file");
1442742Swollman	else if (S_ISSOCK(mode))
145193785Sedwin		return ("socket");
146193785Sedwin	else if (S_ISWHT(mode))
147193785Sedwin		return ("whiteout");
148193785Sedwin	else
149193785Sedwin		return ("unknown");
150193785Sedwin}
151193785Sedwin
152193785Sedwinvoid
153193785Sedwindescriptors_assert(const struct hast_resource *res, int pjdlogmode)
154193785Sedwin{
155193785Sedwin	char msg[256];
156193785Sedwin	struct stat sb;
157193785Sedwin	long maxfd;
158193785Sedwin	bool isopen;
159193785Sedwin	mode_t mode;
160193785Sedwin	int fd;
161193785Sedwin
162193785Sedwin	/*
163193785Sedwin	 * At this point descriptor to syslog socket is closed, so if we want
164193785Sedwin	 * to log assertion message, we have to first store it in 'msg' local
165193785Sedwin	 * buffer and then open syslog socket and log it.
166193785Sedwin	 */
167193785Sedwin	msg[0] = '\0';
168193785Sedwin
169193785Sedwin	maxfd = sysconf(_SC_OPEN_MAX);
170193785Sedwin	if (maxfd < 0) {
171193785Sedwin		pjdlog_init(pjdlogmode);
172193785Sedwin		pjdlog_prefix_set("[%s] (%s) ", res->hr_name,
173193785Sedwin		    role2str(res->hr_role));
174193785Sedwin		pjdlog_errno(LOG_WARNING, "sysconf(_SC_OPEN_MAX) failed");
175193785Sedwin		pjdlog_fini();
176193785Sedwin		maxfd = 16384;
177193785Sedwin	}
178193785Sedwin	for (fd = 0; fd <= maxfd; fd++) {
179193785Sedwin		if (fstat(fd, &sb) == 0) {
180193785Sedwin			isopen = true;
181193785Sedwin			mode = sb.st_mode;
182193785Sedwin		} else if (errno == EBADF) {
183194485Sedwin			isopen = false;
184194485Sedwin			mode = 0;
185194485Sedwin		} else {
186194485Sedwin			isopen = true;	/* silence gcc */
187194485Sedwin			mode = 0;	/* silence gcc */
188194485Sedwin			(void)snprintf(msg, sizeof(msg),
189193785Sedwin			    "Unable to fstat descriptor %d: %s", fd,
190198270Sedwin			    strerror(errno));
191198270Sedwin			break;
192198270Sedwin		}
193198270Sedwin		if (fd == STDIN_FILENO || fd == STDOUT_FILENO ||
194198270Sedwin		    fd == STDERR_FILENO) {
195198270Sedwin			if (!isopen) {
196198270Sedwin				(void)snprintf(msg, sizeof(msg),
197198270Sedwin				    "Descriptor %d (%s) is closed, but should be open.",
198198270Sedwin				    fd, (fd == STDIN_FILENO ? "stdin" :
199198270Sedwin				    (fd == STDOUT_FILENO ? "stdout" : "stderr")));
200198270Sedwin				break;
201198270Sedwin			}
202198270Sedwin		} else if (fd == proto_descriptor(res->hr_event)) {
203196581Sedwin			if (!isopen) {
204198270Sedwin				(void)snprintf(msg, sizeof(msg),
205198270Sedwin				    "Descriptor %d (event) is closed, but should be open.",
206198270Sedwin				    fd);
207198270Sedwin				break;
208198270Sedwin			}
209198270Sedwin			if (!S_ISSOCK(mode)) {
210198270Sedwin				(void)snprintf(msg, sizeof(msg),
211198270Sedwin				    "Descriptor %d (event) is %s, but should be %s.",
212198270Sedwin				    fd, dtype2str(mode), dtype2str(S_IFSOCK));
213198270Sedwin				break;
214198270Sedwin			}
215201189Sedwin		} else if (fd == proto_descriptor(res->hr_ctrl)) {
216201189Sedwin			if (!isopen) {
217201189Sedwin				(void)snprintf(msg, sizeof(msg),
218201189Sedwin				    "Descriptor %d (ctrl) is closed, but should be open.",
219201189Sedwin				    fd);
220201189Sedwin				break;
221201189Sedwin			}
222201189Sedwin			if (!S_ISSOCK(mode)) {
223201189Sedwin				(void)snprintf(msg, sizeof(msg),
224201189Sedwin				    "Descriptor %d (ctrl) is %s, but should be %s.",
225201189Sedwin				    fd, dtype2str(mode), dtype2str(S_IFSOCK));
226201189Sedwin				break;
227201189Sedwin			}
228201189Sedwin		} else if (fd == proto_descriptor(res->hr_conn)) {
229201189Sedwin			if (!isopen) {
230201189Sedwin				(void)snprintf(msg, sizeof(msg),
231201189Sedwin				    "Descriptor %d (conn) is closed, but should be open.",
232201189Sedwin				    fd);
233206219Sedwin				break;
234206219Sedwin			}
235206219Sedwin			if (!S_ISSOCK(mode)) {
236206219Sedwin				(void)snprintf(msg, sizeof(msg),
237206219Sedwin				    "Descriptor %d (conn) is %s, but should be %s.",
238204887Sedwin				    fd, dtype2str(mode), dtype2str(S_IFSOCK));
239206219Sedwin				break;
240206219Sedwin			}
241206219Sedwin		} else if (res->hr_role == HAST_ROLE_SECONDARY &&
242206219Sedwin		    fd == proto_descriptor(res->hr_remotein)) {
243204887Sedwin			if (!isopen) {
244201189Sedwin				(void)snprintf(msg, sizeof(msg),
245202606Sedwin				    "Descriptor %d (remote in) is closed, but should be open.",
246204887Sedwin				    fd);
247201189Sedwin				break;
2482742Swollman			}
24975267Swollman			if (!S_ISSOCK(mode)) {
25019878Swollman				(void)snprintf(msg, sizeof(msg),
25119878Swollman				    "Descriptor %d (remote in) is %s, but should be %s.",
2522742Swollman				    fd, dtype2str(mode), dtype2str(S_IFSOCK));
25319878Swollman				break;
25419878Swollman			}
255201189Sedwin		} else if (res->hr_role == HAST_ROLE_SECONDARY &&
256201189Sedwin		    fd == proto_descriptor(res->hr_remoteout)) {
2572742Swollman			if (!isopen) {
2582742Swollman				(void)snprintf(msg, sizeof(msg),
2592742Swollman				    "Descriptor %d (remote out) is closed, but should be open.",
26067578Swollman				    fd);
2612742Swollman				break;
26219878Swollman			}
2632742Swollman			if (!S_ISSOCK(mode)) {
2642742Swollman				(void)snprintf(msg, sizeof(msg),
26586222Swollman				    "Descriptor %d (remote out) is %s, but should be %s.",
26686222Swollman				    fd, dtype2str(mode), dtype2str(S_IFSOCK));
267149514Swollman				break;
268149514Swollman			}
269149514Swollman		} else {
2702742Swollman			if (isopen) {
271149514Swollman				(void)snprintf(msg, sizeof(msg),
272149514Swollman				    "Descriptor %d is open (%s), but should be closed.",
27386222Swollman				    fd, dtype2str(mode));
2742742Swollman				break;
2752742Swollman			}
2762742Swollman		}
2772742Swollman	}
2782742Swollman	if (msg[0] != '\0') {
2792742Swollman		pjdlog_init(pjdlogmode);
2802742Swollman		pjdlog_prefix_set("[%s] (%s) ", res->hr_name,
28114343Swollman		    role2str(res->hr_role));
2822742Swollman		PJDLOG_ABORT("%s", msg);
28317200Swollman	}
28419878Swollman}
28519878Swollman
2862742Swollmanstatic void
28719878Swollmanchild_exit_log(unsigned int pid, int status)
2882742Swollman{
2892742Swollman
2902742Swollman	if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
2912742Swollman		pjdlog_debug(1, "Worker process exited gracefully (pid=%u).",
29219878Swollman		    pid);
2932742Swollman	} else if (WIFSIGNALED(status)) {
2942742Swollman		pjdlog_error("Worker process killed (pid=%u, signal=%d).",
2952742Swollman		    pid, WTERMSIG(status));
2962742Swollman	} else {
29743014Swollman		pjdlog_error("Worker process exited ungracefully (pid=%u, exitcode=%d).",
2982742Swollman		    pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1);
2992742Swollman	}
3002742Swollman}
3012742Swollman
30219878Swollmanstatic void
30319878Swollmanchild_exit(void)
3042742Swollman{
3052742Swollman	struct hast_resource *res;
3062742Swollman	int status;
30793799Swollman	pid_t pid;
3082742Swollman
3092742Swollman	while ((pid = wait3(&status, WNOHANG, NULL)) > 0) {
3102742Swollman		/* Find resource related to the process that just exited. */
3112742Swollman		TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
3122742Swollman			if (pid == res->hr_workerpid)
3132742Swollman				break;
3142742Swollman		}
3152742Swollman		if (res == NULL) {
31619878Swollman			/*
3172742Swollman			 * This can happen when new connection arrives and we
3182742Swollman			 * cancel child responsible for the old one or if this
3192742Swollman			 * was hook which we executed.
320158421Swollman			 */
321158421Swollman			hook_check_one(pid, status);
322158421Swollman			continue;
323158421Swollman		}
3242742Swollman		pjdlog_prefix_set("[%s] (%s) ", res->hr_name,
325158421Swollman		    role2str(res->hr_role));
326158421Swollman		child_exit_log(pid, status);
3272742Swollman		child_cleanup(res);
328158421Swollman		if (res->hr_role == HAST_ROLE_PRIMARY) {
3292742Swollman			/*
3302742Swollman			 * Restart child process if it was killed by signal
3312742Swollman			 * or exited because of temporary problem.
3322742Swollman			 */
3332742Swollman			if (WIFSIGNALED(status) ||
33414343Swollman			    (WIFEXITED(status) &&
33514343Swollman			     WEXITSTATUS(status) == EX_TEMPFAIL)) {
336163302Sru				sleep(1);
33793799Swollman				pjdlog_info("Restarting worker process.");
33893799Swollman				hastd_primary(res);
33993799Swollman			} else {
340163302Sru				res->hr_role = HAST_ROLE_INIT;
341169811Swollman				pjdlog_info("Changing resource role back to %s.",
342163302Sru				    role2str(res->hr_role));
343163302Sru			}
344163302Sru		}
345163302Sru		pjdlog_prefix_set("%s", "");
346163302Sru	}
347163302Sru}
348163302Sru
349163302Srustatic bool
350163302Sruresource_needs_restart(const struct hast_resource *res0,
351163302Sru    const struct hast_resource *res1)
352163302Sru{
353181421Sedwin
354181421Sedwin	PJDLOG_ASSERT(strcmp(res0->hr_name, res1->hr_name) == 0);
355181421Sedwin
356181421Sedwin	if (strcmp(res0->hr_provname, res1->hr_provname) != 0)
357181421Sedwin		return (true);
358181421Sedwin	if (strcmp(res0->hr_localpath, res1->hr_localpath) != 0)
359181421Sedwin		return (true);
360181421Sedwin	if (res0->hr_role == HAST_ROLE_INIT ||
361181421Sedwin	    res0->hr_role == HAST_ROLE_SECONDARY) {
362181421Sedwin		if (strcmp(res0->hr_remoteaddr, res1->hr_remoteaddr) != 0)
363181421Sedwin			return (true);
364181421Sedwin		if (res0->hr_replication != res1->hr_replication)
365181421Sedwin			return (true);
366181421Sedwin		if (res0->hr_timeout != res1->hr_timeout)
367181421Sedwin			return (true);
368181421Sedwin		if (strcmp(res0->hr_exec, res1->hr_exec) != 0)
369181421Sedwin			return (true);
370181421Sedwin	}
371181421Sedwin	return (false);
372181421Sedwin}
373181421Sedwin
374181421Sedwinstatic bool
375163302Sruresource_needs_reload(const struct hast_resource *res0,
376163302Sru    const struct hast_resource *res1)
37793799Swollman{
378163302Sru
37967578Swollman	PJDLOG_ASSERT(strcmp(res0->hr_name, res1->hr_name) == 0);
38093799Swollman	PJDLOG_ASSERT(strcmp(res0->hr_provname, res1->hr_provname) == 0);
38114343Swollman	PJDLOG_ASSERT(strcmp(res0->hr_localpath, res1->hr_localpath) == 0);
38293799Swollman
38393799Swollman	if (res0->hr_role != HAST_ROLE_PRIMARY)
38414343Swollman		return (false);
38593799Swollman
386163302Sru	if (strcmp(res0->hr_remoteaddr, res1->hr_remoteaddr) != 0)
3872742Swollman		return (true);
3882742Swollman	if (res0->hr_replication != res1->hr_replication)
3892742Swollman		return (true);
39093799Swollman	if (res0->hr_timeout != res1->hr_timeout)
391163302Sru		return (true);
392163302Sru	if (strcmp(res0->hr_exec, res1->hr_exec) != 0)
393163302Sru		return (true);
394163302Sru	return (false);
39586222Swollman}
39693799Swollman
39714343Swollmanstatic void
39893799Swollmanresource_reload(const struct hast_resource *res)
399163302Sru{
400163302Sru	struct nv *nvin, *nvout;
401163302Sru	int error;
402163302Sru
403163302Sru	PJDLOG_ASSERT(res->hr_role == HAST_ROLE_PRIMARY);
404163302Sru
405163302Sru	nvout = nv_alloc();
406163302Sru	nv_add_uint8(nvout, HASTCTL_RELOAD, "cmd");
40767578Swollman	nv_add_string(nvout, res->hr_remoteaddr, "remoteaddr");
40819878Swollman	nv_add_int32(nvout, (int32_t)res->hr_replication, "replication");
40914343Swollman	nv_add_int32(nvout, (int32_t)res->hr_timeout, "timeout");
41093799Swollman	nv_add_string(nvout, res->hr_exec, "exec");
411163302Sru	if (nv_error(nvout) != 0) {
412163302Sru		nv_free(nvout);
413163302Sru		pjdlog_error("Unable to allocate header for reload message.");
414163302Sru		return;
415200835Sedwin	}
416200835Sedwin	if (hast_proto_send(res, res->hr_ctrl, nvout, NULL, 0) < 0) {
417200835Sedwin		pjdlog_errno(LOG_ERR, "Unable to send reload message");
418200835Sedwin		nv_free(nvout);
419200835Sedwin		return;
420200835Sedwin	}
421200835Sedwin	nv_free(nvout);
422200835Sedwin
423200835Sedwin	/* Receive response. */
424200835Sedwin	if (hast_proto_recv_hdr(res->hr_ctrl, &nvin) < 0) {
425200835Sedwin		pjdlog_errno(LOG_ERR, "Unable to receive reload reply");
426200835Sedwin		return;
427200835Sedwin	}
428200835Sedwin	error = nv_get_int16(nvin, "error");
429200835Sedwin	nv_free(nvin);
430200835Sedwin	if (error != 0) {
431200835Sedwin		pjdlog_common(LOG_ERR, 0, error, "Reload failed");
432200835Sedwin		return;
433200835Sedwin	}
434200835Sedwin}
435200835Sedwin
436200835Sedwinstatic void
437200835Sedwinhastd_reload(void)
438200835Sedwin{
439200835Sedwin	struct hastd_config *newcfg;
440200835Sedwin	struct hast_resource *nres, *cres, *tres;
441200835Sedwin	uint8_t role;
442200835Sedwin
443200835Sedwin	pjdlog_info("Reloading configuration...");
444200835Sedwin
445200835Sedwin	newcfg = yy_config_parse(cfgpath, false);
446200835Sedwin	if (newcfg == NULL)
447200835Sedwin		goto failed;
448200835Sedwin
449200835Sedwin	/*
450200835Sedwin	 * Check if control address has changed.
451200835Sedwin	 */
452200835Sedwin	if (strcmp(cfg->hc_controladdr, newcfg->hc_controladdr) != 0) {
453200835Sedwin		if (proto_server(newcfg->hc_controladdr,
454200835Sedwin		    &newcfg->hc_controlconn) < 0) {
455200835Sedwin			pjdlog_errno(LOG_ERR,
456200835Sedwin			    "Unable to listen on control address %s",
457200835Sedwin			    newcfg->hc_controladdr);
458200835Sedwin			goto failed;
459200835Sedwin		}
460200835Sedwin	}
461200835Sedwin	/*
462200835Sedwin	 * Check if listen address has changed.
463200835Sedwin	 */
464200835Sedwin	if (strcmp(cfg->hc_listenaddr, newcfg->hc_listenaddr) != 0) {
465200835Sedwin		if (proto_server(newcfg->hc_listenaddr,
466200835Sedwin		    &newcfg->hc_listenconn) < 0) {
467200835Sedwin			pjdlog_errno(LOG_ERR, "Unable to listen on address %s",
468200835Sedwin			    newcfg->hc_listenaddr);
469200835Sedwin			goto failed;
470200835Sedwin		}
471200835Sedwin	}
472200835Sedwin	/*
473200835Sedwin	 * Only when both control and listen sockets are successfully
474200835Sedwin	 * initialized switch them to new configuration.
475200835Sedwin	 */
476200835Sedwin	if (newcfg->hc_controlconn != NULL) {
477200835Sedwin		pjdlog_info("Control socket changed from %s to %s.",
47867578Swollman		    cfg->hc_controladdr, newcfg->hc_controladdr);
47919878Swollman		proto_close(cfg->hc_controlconn);
48019878Swollman		cfg->hc_controlconn = newcfg->hc_controlconn;
48114343Swollman		newcfg->hc_controlconn = NULL;
48293799Swollman		strlcpy(cfg->hc_controladdr, newcfg->hc_controladdr,
483198825Sedwin		    sizeof(cfg->hc_controladdr));
484198825Sedwin	}
485200835Sedwin	if (newcfg->hc_listenconn != NULL) {
486200835Sedwin		pjdlog_info("Listen socket changed from %s to %s.",
487198825Sedwin		    cfg->hc_listenaddr, newcfg->hc_listenaddr);
488198825Sedwin		proto_close(cfg->hc_listenconn);
489198825Sedwin		cfg->hc_listenconn = newcfg->hc_listenconn;
490198825Sedwin		newcfg->hc_listenconn = NULL;
491198825Sedwin		strlcpy(cfg->hc_listenaddr, newcfg->hc_listenaddr,
492198825Sedwin		    sizeof(cfg->hc_listenaddr));
493198825Sedwin	}
494198825Sedwin
495198825Sedwin	/*
496198825Sedwin	 * Stop and remove resources that were removed from the configuration.
497198825Sedwin	 */
498198825Sedwin	TAILQ_FOREACH_SAFE(cres, &cfg->hc_resources, hr_next, tres) {
499198825Sedwin		TAILQ_FOREACH(nres, &newcfg->hc_resources, hr_next) {
500198825Sedwin			if (strcmp(cres->hr_name, nres->hr_name) == 0)
501198825Sedwin				break;
502198825Sedwin		}
503198825Sedwin		if (nres == NULL) {
504198825Sedwin			control_set_role(cres, HAST_ROLE_INIT);
505198825Sedwin			TAILQ_REMOVE(&cfg->hc_resources, cres, hr_next);
506198825Sedwin			pjdlog_info("Resource %s removed.", cres->hr_name);
507198825Sedwin			free(cres);
508198825Sedwin		}
509198825Sedwin	}
510198825Sedwin	/*
511198825Sedwin	 * Move new resources to the current configuration.
512198825Sedwin	 */
513198825Sedwin	TAILQ_FOREACH_SAFE(nres, &newcfg->hc_resources, hr_next, tres) {
514198825Sedwin		TAILQ_FOREACH(cres, &cfg->hc_resources, hr_next) {
515198825Sedwin			if (strcmp(cres->hr_name, nres->hr_name) == 0)
516198825Sedwin				break;
517198825Sedwin		}
518198825Sedwin		if (cres == NULL) {
519198825Sedwin			TAILQ_REMOVE(&newcfg->hc_resources, nres, hr_next);
520198825Sedwin			TAILQ_INSERT_TAIL(&cfg->hc_resources, nres, hr_next);
521198825Sedwin			pjdlog_info("Resource %s added.", nres->hr_name);
522198825Sedwin		}
523198825Sedwin	}
524198825Sedwin	/*
525198825Sedwin	 * Deal with modified resources.
526198825Sedwin	 * Depending on what has changed exactly we might want to perform
527198825Sedwin	 * different actions.
528198825Sedwin	 *
529198825Sedwin	 * We do full resource restart in the following situations:
530198825Sedwin	 * Resource role is INIT or SECONDARY.
531198825Sedwin	 * Resource role is PRIMARY and path to local component or provider
532198825Sedwin	 * name has changed.
533198825Sedwin	 * In case of PRIMARY, the worker process will be killed and restarted,
534198825Sedwin	 * which also means removing /dev/hast/<name> provider and
535198825Sedwin	 * recreating it.
536198825Sedwin	 *
537198825Sedwin	 * We do just reload (send SIGHUP to worker process) if we act as
538198825Sedwin	 * PRIMARY, but only if remote address, replication mode, timeout or
539198825Sedwin	 * execution path has changed. For those, there is no need to restart
540198825Sedwin	 * worker process.
541198825Sedwin	 * If PRIMARY receives SIGHUP, it will reconnect if remote address or
542198825Sedwin	 * replication mode has changed or simply set new timeout if only
543198825Sedwin	 * timeout has changed.
544198825Sedwin	 */
545198825Sedwin	TAILQ_FOREACH_SAFE(nres, &newcfg->hc_resources, hr_next, tres) {
546198825Sedwin		TAILQ_FOREACH(cres, &cfg->hc_resources, hr_next) {
547198825Sedwin			if (strcmp(cres->hr_name, nres->hr_name) == 0)
548198825Sedwin				break;
549198825Sedwin		}
55067578Swollman		PJDLOG_ASSERT(cres != NULL);
5512742Swollman		if (resource_needs_restart(cres, nres)) {
552198825Sedwin			pjdlog_info("Resource %s configuration was modified, restarting it.",
553198825Sedwin			    cres->hr_name);
5542742Swollman			role = cres->hr_role;
5552742Swollman			control_set_role(cres, HAST_ROLE_INIT);
5562742Swollman			TAILQ_REMOVE(&cfg->hc_resources, cres, hr_next);
5572742Swollman			free(cres);
5582742Swollman			TAILQ_REMOVE(&newcfg->hc_resources, nres, hr_next);
559198825Sedwin			TAILQ_INSERT_TAIL(&cfg->hc_resources, nres, hr_next);
560198825Sedwin			control_set_role(nres, role);
5612742Swollman		} else if (resource_needs_reload(cres, nres)) {
5622742Swollman			pjdlog_info("Resource %s configuration was modified, reloading it.",
5632742Swollman			    cres->hr_name);
5642742Swollman			strlcpy(cres->hr_remoteaddr, nres->hr_remoteaddr,
5652742Swollman			    sizeof(cres->hr_remoteaddr));
566213312Sedwin			cres->hr_replication = nres->hr_replication;
567213312Sedwin			cres->hr_timeout = nres->hr_timeout;
568198825Sedwin			strlcpy(cres->hr_exec, nres->hr_exec,
569198825Sedwin			    sizeof(cres->hr_exec));
570198825Sedwin			if (cres->hr_workerpid != 0)
5712742Swollman				resource_reload(cres);
5722742Swollman		}
573198825Sedwin	}
574198825Sedwin
57558787Sru	yy_config_free(newcfg);
5762742Swollman	pjdlog_info("Configuration reloaded successfully.");
57730711Swollman	return;
57830711Swollmanfailed:
57943014Swollman	if (newcfg != NULL) {
58030711Swollman		if (newcfg->hc_controlconn != NULL)
581158421Swollman			proto_close(newcfg->hc_controlconn);
58243543Swollman		if (newcfg->hc_listenconn != NULL)
58343543Swollman			proto_close(newcfg->hc_listenconn);
58443543Swollman		yy_config_free(newcfg);
585206868Sedwin	}
586206868Sedwin	pjdlog_warning("Configuration not reloaded.");
587206868Sedwin}
588206868Sedwin
589206868Sedwinstatic void
590206868Sedwinterminate_workers(void)
591206868Sedwin{
592206868Sedwin	struct hast_resource *res;
593206868Sedwin
594206868Sedwin	pjdlog_info("Termination signal received, exiting.");
595206868Sedwin	TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
596206868Sedwin		if (res->hr_workerpid == 0)
597206868Sedwin			continue;
598206868Sedwin		pjdlog_info("Terminating worker process (resource=%s, role=%s, pid=%u).",
599206868Sedwin		    res->hr_name, role2str(res->hr_role), res->hr_workerpid);
600206868Sedwin		if (kill(res->hr_workerpid, SIGTERM) == 0)
601206868Sedwin			continue;
602206868Sedwin		pjdlog_errno(LOG_WARNING,
603206868Sedwin		    "Unable to send signal to worker process (resource=%s, role=%s, pid=%u).",
604206868Sedwin		    res->hr_name, role2str(res->hr_role), res->hr_workerpid);
605206868Sedwin	}
606206868Sedwin}
60730711Swollman
60830711Swollmanstatic void
60930711Swollmanlisten_accept(void)
61030711Swollman{
61130711Swollman	struct hast_resource *res;
61230711Swollman	struct proto_conn *conn;
61330711Swollman	struct nv *nvin, *nvout, *nverr;
61430711Swollman	const char *resname;
61530711Swollman	const unsigned char *token;
61630711Swollman	char laddr[256], raddr[256];
617206868Sedwin	size_t size;
618206868Sedwin	pid_t pid;
619206868Sedwin	int status;
62030711Swollman
62186222Swollman	proto_local_address(cfg->hc_listenconn, laddr, sizeof(laddr));
62230711Swollman	pjdlog_debug(1, "Accepting connection to %s.", laddr);
62330711Swollman
62493799Swollman	if (proto_accept(cfg->hc_listenconn, &conn) < 0) {
6252742Swollman		pjdlog_errno(LOG_ERR, "Unable to accept connection %s", laddr);
62693799Swollman		return;
62793799Swollman	}
62893799Swollman
62993799Swollman	proto_local_address(conn, laddr, sizeof(laddr));
63093799Swollman	proto_remote_address(conn, raddr, sizeof(raddr));
63193799Swollman	pjdlog_info("Connection from %s to %s.", raddr, laddr);
63293799Swollman
63393799Swollman	/* Error in setting timeout is not critical, but why should it fail? */
63493799Swollman	if (proto_timeout(conn, HAST_TIMEOUT) < 0)
63593799Swollman		pjdlog_errno(LOG_WARNING, "Unable to set connection timeout");
63693799Swollman
63793799Swollman	nvin = nvout = nverr = NULL;
63893799Swollman
63993799Swollman	/*
6402742Swollman	 * Before receiving any data see if remote host have access to any
64193799Swollman	 * resource.
64293799Swollman	 */
64319878Swollman	TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
6442742Swollman		if (proto_address_match(conn, res->hr_remoteaddr))
6452742Swollman			break;
6462742Swollman	}
6472742Swollman	if (res == NULL) {
6482742Swollman		pjdlog_error("Client %s isn't known.", raddr);
6492742Swollman		goto close;
65019878Swollman	}
6512742Swollman	/* Ok, remote host can access at least one resource. */
65219878Swollman
6532742Swollman	if (hast_proto_recv_hdr(conn, &nvin) < 0) {
65419878Swollman		pjdlog_errno(LOG_ERR, "Unable to receive header from %s",
6552742Swollman		    raddr);
6562742Swollman		goto close;
65743543Swollman	}
65843543Swollman
6592742Swollman	resname = nv_get_string(nvin, "resource");
6602742Swollman	if (resname == NULL) {
66143543Swollman		pjdlog_error("No 'resource' field in the header received from %s.",
66258787Sru		    raddr);
66343543Swollman		goto close;
6642742Swollman	}
66567578Swollman	pjdlog_debug(2, "%s: resource=%s", raddr, resname);
66667578Swollman	token = nv_get_uint8_array(nvin, &size, "token");
66767578Swollman	/*
66867578Swollman	 * NULL token means that this is first conection.
6692742Swollman	 */
670149514Swollman	if (token != NULL && size != sizeof(res->hr_token)) {
6719908Swollman		pjdlog_error("Received token of invalid size from %s (expected %zu, got %zu).",
6729908Swollman		    raddr, sizeof(res->hr_token), size);
6739908Swollman		goto close;
67414343Swollman	}
67514343Swollman
676149514Swollman	/*
67720094Swollman	 * From now on we want to send errors to the remote node.
67820094Swollman	 */
67920094Swollman	nverr = nv_alloc();
680136638Swollman
681136638Swollman	/* Find resource related to this connection. */
682149514Swollman	TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
683136638Swollman		if (strcmp(resname, res->hr_name) == 0)
684136638Swollman			break;
685136638Swollman	}
686136638Swollman	/* Have we found the resource? */
687136638Swollman	if (res == NULL) {
688136638Swollman		pjdlog_error("No resource '%s' as requested by %s.",
689136638Swollman		    resname, raddr);
690153670Swollman		nv_add_stringf(nverr, "errmsg", "Resource not configured.");
691153670Swollman		goto fail;
692153670Swollman	}
693153670Swollman
694153670Swollman	/* Now that we know resource name setup log prefix. */
695153670Swollman	pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role));
696153670Swollman
697153670Swollman	/* Does the remote host have access to this resource? */
698153670Swollman	if (!proto_address_match(conn, res->hr_remoteaddr)) {
699153670Swollman		pjdlog_error("Client %s has no access to the resource.", raddr);
700153670Swollman		nv_add_stringf(nverr, "errmsg", "No access to the resource.");
7012742Swollman		goto fail;
7022742Swollman	}
70319878Swollman	/* Is the resource marked as secondary? */
70419878Swollman	if (res->hr_role != HAST_ROLE_SECONDARY) {
70519878Swollman		pjdlog_error("We act as %s for the resource and not as %s as requested by %s.",
70619878Swollman		    role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY),
70720094Swollman		    raddr);
70820094Swollman		nv_add_stringf(nverr, "errmsg",
70920094Swollman		    "Remote node acts as %s for the resource and not as %s.",
71043543Swollman		    role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY));
711136638Swollman		goto fail;
712153670Swollman	}
713153670Swollman	/* Does token (if exists) match? */
7142742Swollman	if (token != NULL && memcmp(token, res->hr_token,
71558787Sru	    sizeof(res->hr_token)) != 0) {
71675267Swollman		pjdlog_error("Token received from %s doesn't match.", raddr);
717169811Swollman		nv_add_stringf(nverr, "errmsg", "Token doesn't match.");
718169811Swollman		goto fail;
71975267Swollman	}
72075267Swollman	/*
72175267Swollman	 * If there is no token, but we have half-open connection
72275267Swollman	 * (only remotein) or full connection (worker process is running)
72375267Swollman	 * we have to cancel those and accept the new connection.
72475267Swollman	 */
72575267Swollman	if (token == NULL) {
72675267Swollman		PJDLOG_ASSERT(res->hr_remoteout == NULL);
72775267Swollman		pjdlog_debug(1, "Initial connection from %s.", raddr);
72875267Swollman		if (res->hr_workerpid != 0) {
72975267Swollman			PJDLOG_ASSERT(res->hr_remotein == NULL);
73075267Swollman			pjdlog_debug(1,
73175267Swollman			    "Worker process exists (pid=%u), stopping it.",
73275267Swollman			    (unsigned int)res->hr_workerpid);
73375267Swollman			/* Stop child process. */
73475267Swollman			if (kill(res->hr_workerpid, SIGINT) < 0) {
73575267Swollman				pjdlog_errno(LOG_ERR,
73675267Swollman				    "Unable to stop worker process (pid=%u)",
73775267Swollman				    (unsigned int)res->hr_workerpid);
73875267Swollman				/*
73975267Swollman				 * Other than logging the problem we
74058787Sru				 * ignore it - nothing smart to do.
74158787Sru				 */
742149514Swollman			}
743169811Swollman			/* Wait for it to exit. */
744149514Swollman			else if ((pid = waitpid(res->hr_workerpid,
74586222Swollman			    &status, 0)) != res->hr_workerpid) {
746149514Swollman				/* We can only log the problem. */
74758787Sru				pjdlog_errno(LOG_ERR,
7482742Swollman				    "Waiting for worker process (pid=%u) failed",
7492742Swollman				    (unsigned int)res->hr_workerpid);
750177591Sedwin			} else {
75119878Swollman				child_exit_log(res->hr_workerpid, status);
75219878Swollman			}
7532742Swollman			child_cleanup(res);
7542742Swollman		} else if (res->hr_remotein != NULL) {
7552742Swollman			char oaddr[256];
756177591Sedwin
7572742Swollman			proto_remote_address(res->hr_remotein, oaddr,
7582742Swollman			    sizeof(oaddr));
7592742Swollman			pjdlog_debug(1,
7602742Swollman			    "Canceling half-open connection from %s on connection from %s.",
7612742Swollman			    oaddr, raddr);
76286222Swollman			proto_close(res->hr_remotein);
763158421Swollman			res->hr_remotein = NULL;
76486222Swollman		}
76586222Swollman	}
76686222Swollman
76786222Swollman	/*
76886222Swollman	 * Checks and cleanups are done.
769169811Swollman	 */
770169811Swollman
771169811Swollman	if (token == NULL) {
772169811Swollman		arc4random_buf(res->hr_token, sizeof(res->hr_token));
773169811Swollman		nvout = nv_alloc();
774169811Swollman		nv_add_uint8_array(nvout, res->hr_token,
775169811Swollman		    sizeof(res->hr_token), "token");
776169811Swollman		if (nv_error(nvout) != 0) {
777169811Swollman			pjdlog_common(LOG_ERR, 0, nv_error(nvout),
778169811Swollman			    "Unable to prepare return header for %s", raddr);
779169811Swollman			nv_add_stringf(nverr, "errmsg",
780169811Swollman			    "Remote node was unable to prepare return header: %s.",
781169811Swollman			    strerror(nv_error(nvout)));
7822742Swollman			goto fail;
7832742Swollman		}
784158421Swollman		if (hast_proto_send(NULL, conn, nvout, NULL, 0) < 0) {
78558787Sru			int error = errno;
78658787Sru
78719878Swollman			pjdlog_errno(LOG_ERR, "Unable to send response to %s",
78886222Swollman			    raddr);
789169811Swollman			nv_add_stringf(nverr, "errmsg",
79086222Swollman			    "Remote node was unable to send response: %s.",
79186222Swollman			    strerror(error));
79286222Swollman			goto fail;
79386222Swollman		}
79486222Swollman		res->hr_remotein = conn;
79586222Swollman		pjdlog_debug(1, "Incoming connection from %s configured.",
79686222Swollman		    raddr);
797169811Swollman	} else {
79886222Swollman		res->hr_remoteout = conn;
79986222Swollman		pjdlog_debug(1, "Outgoing connection to %s configured.", raddr);
80086222Swollman		hastd_secondary(res, nvin);
80186222Swollman	}
80286222Swollman	nv_free(nvin);
80393799Swollman	nv_free(nvout);
80419878Swollman	nv_free(nverr);
80586222Swollman	pjdlog_prefix_set("%s", "");
806169811Swollman	return;
80786222Swollmanfail:
8082742Swollman	if (nv_error(nverr) != 0) {
809169811Swollman		pjdlog_common(LOG_ERR, 0, nv_error(nverr),
8102742Swollman		    "Unable to prepare error header for %s", raddr);
81186222Swollman		goto close;
8122742Swollman	}
8132742Swollman	if (hast_proto_send(NULL, conn, nverr, NULL, 0) < 0) {
814114173Swollman		pjdlog_errno(LOG_ERR, "Unable to send error to %s", raddr);
815114173Swollman		goto close;
816114173Swollman	}
817114173Swollmanclose:
81821217Swollman	if (nvin != NULL)
819114173Swollman		nv_free(nvin);
820114173Swollman	if (nvout != NULL)
82121217Swollman		nv_free(nvout);
822114173Swollman	if (nverr != NULL)
823114173Swollman		nv_free(nverr);
824114173Swollman	proto_close(conn);
825114173Swollman	pjdlog_prefix_set("%s", "");
826114173Swollman}
827114173Swollman
828114173Swollmanstatic void
829114173Swollmanconnection_migrate(struct hast_resource *res)
830114173Swollman{
831114173Swollman	struct proto_conn *conn;
832114173Swollman	int16_t val = 0;
833114173Swollman
834114173Swollman	if (proto_recv(res->hr_conn, &val, sizeof(val)) < 0) {
835114173Swollman		pjdlog_errno(LOG_WARNING,
836114173Swollman		    "Unable to receive connection command");
837114173Swollman		return;
838114173Swollman	}
839114173Swollman	if (proto_client(res->hr_remoteaddr, &conn) < 0) {
840114173Swollman		val = errno;
841114173Swollman		pjdlog_errno(LOG_WARNING,
842114173Swollman		    "Unable to create outgoing connection to %s",
843114173Swollman		    res->hr_remoteaddr);
844149514Swollman		goto out;
845149514Swollman	}
846149514Swollman	if (proto_connect(conn, -1) < 0) {
847149514Swollman		val = errno;
848149514Swollman		pjdlog_errno(LOG_WARNING, "Unable to connect to %s",
849149514Swollman		    res->hr_remoteaddr);
850149514Swollman		proto_close(conn);
851158421Swollman		goto out;
852158421Swollman	}
853149514Swollman	val = 0;
854149514Swollmanout:
855149514Swollman	if (proto_send(res->hr_conn, &val, sizeof(val)) < 0) {
856149514Swollman		pjdlog_errno(LOG_WARNING,
85721217Swollman		    "Unable to send reply to connection request");
858149514Swollman	}
859149514Swollman	if (val == 0 && proto_connection_send(res->hr_conn, conn) < 0)
860149514Swollman		pjdlog_errno(LOG_WARNING, "Unable to send connection");
861149514Swollman}
862149514Swollman
863149514Swollmanstatic void
864149514Swollmanmain_loop(void)
865149514Swollman{
866149514Swollman	struct hast_resource *res;
867149514Swollman	struct timeval seltimeout;
868149514Swollman	struct timespec sigtimeout;
869149514Swollman	int fd, maxfd, ret, signo;
870149514Swollman	sigset_t mask;
871149514Swollman	fd_set rfds;
872158421Swollman
873158421Swollman	seltimeout.tv_sec = REPORT_INTERVAL;
874158421Swollman	seltimeout.tv_usec = 0;
875158421Swollman	sigtimeout.tv_sec = 0;
876172479Sedwin	sigtimeout.tv_nsec = 0;
877172479Sedwin
878172479Sedwin	PJDLOG_VERIFY(sigemptyset(&mask) == 0);
879172479Sedwin	PJDLOG_VERIFY(sigaddset(&mask, SIGHUP) == 0);
880172479Sedwin	PJDLOG_VERIFY(sigaddset(&mask, SIGINT) == 0);
881174242Sedwin	PJDLOG_VERIFY(sigaddset(&mask, SIGTERM) == 0);
882174242Sedwin	PJDLOG_VERIFY(sigaddset(&mask, SIGCHLD) == 0);
883174242Sedwin
884174242Sedwin	pjdlog_info("Started successfully, running protocol version %d.",
885174242Sedwin	    HAST_PROTO_VERSION);
886174242Sedwin
887174242Sedwin	for (;;) {
888174242Sedwin		while ((signo = sigtimedwait(&mask, NULL, &sigtimeout)) != -1) {
889174242Sedwin			switch (signo) {
8902742Swollman			case SIGINT:
891114173Swollman			case SIGTERM:
892114173Swollman				sigexit_received = true;
893114173Swollman				terminate_workers();
894114173Swollman				proto_close(cfg->hc_controlconn);
895114173Swollman				exit(EX_OK);
896114173Swollman				break;
897114173Swollman			case SIGCHLD:
898114173Swollman				child_exit();
899114173Swollman				break;
900114173Swollman			case SIGHUP:
901114173Swollman				hastd_reload();
902114173Swollman				break;
903114173Swollman			default:
904114173Swollman				PJDLOG_ABORT("Unexpected signal (%d).", signo);
905114173Swollman			}
906114173Swollman		}
907114173Swollman
908158421Swollman		/* Setup descriptors for select(2). */
909158421Swollman		FD_ZERO(&rfds);
910172479Sedwin		maxfd = fd = proto_descriptor(cfg->hc_controlconn);
911172479Sedwin		PJDLOG_ASSERT(fd >= 0);
912172479Sedwin		FD_SET(fd, &rfds);
913172479Sedwin		fd = proto_descriptor(cfg->hc_listenconn);
914172479Sedwin		PJDLOG_ASSERT(fd >= 0);
915172479Sedwin		FD_SET(fd, &rfds);
916172479Sedwin		maxfd = fd > maxfd ? fd : maxfd;
917172479Sedwin		TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
918172479Sedwin			if (res->hr_event == NULL)
919172479Sedwin				continue;
920172479Sedwin			PJDLOG_ASSERT(res->hr_conn != NULL);
921172479Sedwin			fd = proto_descriptor(res->hr_event);
922172479Sedwin			PJDLOG_ASSERT(fd >= 0);
923172479Sedwin			FD_SET(fd, &rfds);
924172479Sedwin			maxfd = fd > maxfd ? fd : maxfd;
925172479Sedwin			if (res->hr_role == HAST_ROLE_PRIMARY) {
926172479Sedwin				/* Only primary workers asks for connections. */
927172479Sedwin				fd = proto_descriptor(res->hr_conn);
928172479Sedwin				PJDLOG_ASSERT(fd >= 0);
929172479Sedwin				FD_SET(fd, &rfds);
930172479Sedwin				maxfd = fd > maxfd ? fd : maxfd;
931172479Sedwin			}
932172479Sedwin		}
933172479Sedwin
934172479Sedwin		PJDLOG_ASSERT(maxfd + 1 <= (int)FD_SETSIZE);
935172479Sedwin		ret = select(maxfd + 1, &rfds, NULL, NULL, &seltimeout);
936172479Sedwin		if (ret == 0)
937172479Sedwin			hook_check();
938172479Sedwin		else if (ret == -1) {
939172479Sedwin			if (errno == EINTR)
9402742Swollman				continue;
9412742Swollman			KEEP_ERRNO((void)pidfile_remove(pfh));
94219878Swollman			pjdlog_exit(EX_OSERR, "select() failed");
943114173Swollman		}
94419878Swollman
94519878Swollman		if (FD_ISSET(proto_descriptor(cfg->hc_controlconn), &rfds))
9462742Swollman			control_handle(cfg);
94764499Swollman		if (FD_ISSET(proto_descriptor(cfg->hc_listenconn), &rfds))
9482742Swollman			listen_accept();
94964499Swollman		TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
950149514Swollman			if (res->hr_event == NULL)
95164499Swollman				continue;
95275267Swollman			PJDLOG_ASSERT(res->hr_conn != NULL);
95364499Swollman			if (FD_ISSET(proto_descriptor(res->hr_event), &rfds)) {
95464499Swollman				if (event_recv(res) == 0)
95564499Swollman					continue;
95664499Swollman				/* The worker process exited? */
95764499Swollman				proto_close(res->hr_event);
95864499Swollman				res->hr_event = NULL;
95964499Swollman				proto_close(res->hr_conn);
96064499Swollman				res->hr_conn = NULL;
96164499Swollman				continue;
96264499Swollman			}
96364499Swollman			if (res->hr_role == HAST_ROLE_PRIMARY &&
964177591Sedwin			    FD_ISSET(proto_descriptor(res->hr_conn), &rfds)) {
965177591Sedwin				connection_migrate(res);
966177591Sedwin			}
967177591Sedwin		}
968177591Sedwin	}
969177591Sedwin}
970177591Sedwin
971177591Sedwinstatic void
972177591Sedwindummy_sighandler(int sig __unused)
973177591Sedwin{
974177591Sedwin	/* Nothing to do. */
975177591Sedwin}
976177591Sedwin
977177591Sedwinint
978177591Sedwinmain(int argc, char *argv[])
9792742Swollman{
9802742Swollman	const char *pidfile;
9812742Swollman	pid_t otherpid;
9822742Swollman	bool foreground;
9832742Swollman	int debuglevel;
98420094Swollman	sigset_t mask;
98520094Swollman
98620094Swollman	foreground = false;
987158421Swollman	debuglevel = 0;
988169811Swollman	pidfile = HASTD_PIDFILE;
989177591Sedwin
990177591Sedwin	for (;;) {
9912742Swollman		int ch;
9922742Swollman
99319878Swollman		ch = getopt(argc, argv, "c:dFhP:");
9942742Swollman		if (ch == -1)
9952742Swollman			break;
9962742Swollman		switch (ch) {
9972742Swollman		case 'c':
9982742Swollman			cfgpath = optarg;
9992742Swollman			break;
10002742Swollman		case 'd':
10012742Swollman			debuglevel++;
100275267Swollman			break;
100375267Swollman		case 'F':
100475267Swollman			foreground = true;
100575267Swollman			break;
100675267Swollman		case 'P':
100775267Swollman			pidfile = optarg;
100875267Swollman			break;
100975267Swollman		case 'h':
101075267Swollman		default:
101175267Swollman			usage();
101275267Swollman		}
101375267Swollman	}
101475267Swollman	argc -= optind;
101575267Swollman	argv += optind;
101675267Swollman
101775267Swollman	pjdlog_init(PJDLOG_MODE_STD);
101875267Swollman	pjdlog_debug_set(debuglevel);
101975267Swollman
102075267Swollman	g_gate_load();
10212742Swollman
1022158421Swollman	pfh = pidfile_open(pidfile, 0600, &otherpid);
10232742Swollman	if (pfh == NULL) {
10242742Swollman		if (errno == EEXIST) {
10252742Swollman			pjdlog_exitx(EX_TEMPFAIL,
10262742Swollman			    "Another hastd is already running, pid: %jd.",
10272742Swollman			    (intmax_t)otherpid);
10282742Swollman		}
10292742Swollman		/* If we cannot create pidfile from other reasons, only warn. */
10302742Swollman		pjdlog_errno(LOG_WARNING, "Unable to open or create pidfile");
10312742Swollman	}
10322742Swollman
10332742Swollman	cfg = yy_config_parse(cfgpath, true);
10342742Swollman	PJDLOG_ASSERT(cfg != NULL);
10352742Swollman
10362742Swollman	/*
10372742Swollman	 * Restore default actions for interesting signals in case parent
10382742Swollman	 * process (like init(8)) decided to ignore some of them (like SIGHUP).
10392742Swollman	 */
10402742Swollman	PJDLOG_VERIFY(signal(SIGHUP, SIG_DFL) != SIG_ERR);
10412742Swollman	PJDLOG_VERIFY(signal(SIGINT, SIG_DFL) != SIG_ERR);
10422742Swollman	PJDLOG_VERIFY(signal(SIGTERM, SIG_DFL) != SIG_ERR);
10432742Swollman	/*
10442742Swollman	 * Because SIGCHLD is ignored by default, setup dummy handler for it,
10452742Swollman	 * so we can mask it.
10462742Swollman	 */
10472742Swollman	PJDLOG_VERIFY(signal(SIGCHLD, dummy_sighandler) != SIG_ERR);
10482742Swollman
10492742Swollman	PJDLOG_VERIFY(sigemptyset(&mask) == 0);
10502742Swollman	PJDLOG_VERIFY(sigaddset(&mask, SIGHUP) == 0);
10512742Swollman	PJDLOG_VERIFY(sigaddset(&mask, SIGINT) == 0);
10522742Swollman	PJDLOG_VERIFY(sigaddset(&mask, SIGTERM) == 0);
10532742Swollman	PJDLOG_VERIFY(sigaddset(&mask, SIGCHLD) == 0);
10542742Swollman	PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
10552742Swollman
10562742Swollman	/* Listen on control address. */
10572742Swollman	if (proto_server(cfg->hc_controladdr, &cfg->hc_controlconn) < 0) {
10582742Swollman		KEEP_ERRNO((void)pidfile_remove(pfh));
10592742Swollman		pjdlog_exit(EX_OSERR, "Unable to listen on control address %s",
10602742Swollman		    cfg->hc_controladdr);
10612742Swollman	}
10622742Swollman	/* Listen for remote connections. */
10632742Swollman	if (proto_server(cfg->hc_listenaddr, &cfg->hc_listenconn) < 0) {
10642742Swollman		KEEP_ERRNO((void)pidfile_remove(pfh));
1065149514Swollman		pjdlog_exit(EX_OSERR, "Unable to listen on address %s",
1066149514Swollman		    cfg->hc_listenaddr);
1067149514Swollman	}
106830711Swollman
10692742Swollman	if (!foreground) {
10702742Swollman		if (daemon(0, 0) < 0) {
107143543Swollman			KEEP_ERRNO((void)pidfile_remove(pfh));
107243543Swollman			pjdlog_exit(EX_OSERR, "Unable to daemonize");
107343543Swollman		}
107443543Swollman
107543543Swollman		/* Start logging to syslog. */
107643543Swollman		pjdlog_mode_set(PJDLOG_MODE_SYSLOG);
107743543Swollman
107864499Swollman		/* Write PID to a file. */
107964499Swollman		if (pidfile_write(pfh) < 0) {
108043543Swollman			pjdlog_errno(LOG_WARNING,
108164499Swollman			    "Unable to write PID to a file");
108264499Swollman		}
108364499Swollman	}
108464499Swollman
108564499Swollman	hook_init();
108664499Swollman
108764499Swollman	main_loop();
108864499Swollman
108964499Swollman	exit(0);
109064499Swollman}
10912742Swollman