hastd.c revision 207372
1204076Spjd/*-
2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation
3204076Spjd * All rights reserved.
4204076Spjd *
5204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from
6204076Spjd * the FreeBSD Foundation.
7204076Spjd *
8204076Spjd * Redistribution and use in source and binary forms, with or without
9204076Spjd * modification, are permitted provided that the following conditions
10204076Spjd * are met:
11204076Spjd * 1. Redistributions of source code must retain the above copyright
12204076Spjd *    notice, this list of conditions and the following disclaimer.
13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
14204076Spjd *    notice, this list of conditions and the following disclaimer in the
15204076Spjd *    documentation and/or other materials provided with the distribution.
16204076Spjd *
17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27204076Spjd * SUCH DAMAGE.
28204076Spjd */
29204076Spjd
30204076Spjd#include <sys/cdefs.h>
31204076Spjd__FBSDID("$FreeBSD: head/sbin/hastd/hastd.c 207372 2010-04-29 15:42:24Z pjd $");
32204076Spjd
33204076Spjd#include <sys/param.h>
34204076Spjd#include <sys/linker.h>
35204076Spjd#include <sys/module.h>
36204076Spjd#include <sys/wait.h>
37204076Spjd
38204076Spjd#include <assert.h>
39204076Spjd#include <err.h>
40204076Spjd#include <errno.h>
41204076Spjd#include <libutil.h>
42204076Spjd#include <signal.h>
43204076Spjd#include <stdbool.h>
44204076Spjd#include <stdio.h>
45204076Spjd#include <stdlib.h>
46204076Spjd#include <string.h>
47204076Spjd#include <sysexits.h>
48204076Spjd#include <unistd.h>
49204076Spjd
50204076Spjd#include <activemap.h>
51204076Spjd#include <pjdlog.h>
52204076Spjd
53204076Spjd#include "control.h"
54204076Spjd#include "hast.h"
55204076Spjd#include "hast_proto.h"
56204076Spjd#include "hastd.h"
57204076Spjd#include "subr.h"
58204076Spjd
59204076Spjd/* Path to configuration file. */
60204076Spjdstatic const char *cfgpath = HAST_CONFIG;
61204076Spjd/* Hastd configuration. */
62204076Spjdstatic struct hastd_config *cfg;
63204076Spjd/* Was SIGCHLD signal received? */
64204076Spjdstatic bool sigchld_received = false;
65204076Spjd/* Was SIGHUP signal received? */
66204076Spjdstatic bool sighup_received = false;
67204076Spjd/* Was SIGINT or SIGTERM signal received? */
68204076Spjdbool sigexit_received = false;
69204076Spjd/* PID file handle. */
70204076Spjdstruct pidfh *pfh;
71204076Spjd
72204076Spjdstatic void
73204076Spjdusage(void)
74204076Spjd{
75204076Spjd
76204076Spjd	errx(EX_USAGE, "[-dFh] [-c config] [-P pidfile]");
77204076Spjd}
78204076Spjd
79204076Spjdstatic void
80204076Spjdsighandler(int sig)
81204076Spjd{
82204076Spjd
83204076Spjd	switch (sig) {
84204076Spjd	case SIGCHLD:
85204076Spjd		sigchld_received = true;
86204076Spjd		break;
87204076Spjd	case SIGHUP:
88204076Spjd		sighup_received = true;
89204076Spjd		break;
90204076Spjd	default:
91204076Spjd		assert(!"invalid condition");
92204076Spjd	}
93204076Spjd}
94204076Spjd
95204076Spjdstatic void
96204076Spjdg_gate_load(void)
97204076Spjd{
98204076Spjd
99204076Spjd	if (modfind("g_gate") == -1) {
100204076Spjd		/* Not present in kernel, try loading it. */
101204076Spjd		if (kldload("geom_gate") == -1 || modfind("g_gate") == -1) {
102204076Spjd			if (errno != EEXIST) {
103204076Spjd				pjdlog_exit(EX_OSERR,
104204076Spjd				    "Unable to load geom_gate module");
105204076Spjd			}
106204076Spjd		}
107204076Spjd	}
108204076Spjd}
109204076Spjd
110204076Spjdstatic void
111207372Spjdchild_exit_log(unsigned int pid, int status)
112207372Spjd{
113207372Spjd
114207372Spjd	if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
115207372Spjd		pjdlog_debug(1, "Worker process exited gracefully (pid=%u).",
116207372Spjd		    pid);
117207372Spjd	} else if (WIFSIGNALED(status)) {
118207372Spjd		pjdlog_error("Worker process killed (pid=%u, signal=%d).",
119207372Spjd		    pid, WTERMSIG(status));
120207372Spjd	} else {
121207372Spjd		pjdlog_error("Worker process exited ungracefully (pid=%u, exitcode=%d).",
122207372Spjd		    pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1);
123207372Spjd	}
124207372Spjd}
125207372Spjd
126207372Spjdstatic void
127204076Spjdchild_exit(void)
128204076Spjd{
129204076Spjd	struct hast_resource *res;
130204076Spjd	int status;
131204076Spjd	pid_t pid;
132204076Spjd
133204076Spjd	while ((pid = wait3(&status, WNOHANG, NULL)) > 0) {
134204076Spjd		/* Find resource related to the process that just exited. */
135204076Spjd		TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
136204076Spjd			if (pid == res->hr_workerpid)
137204076Spjd				break;
138204076Spjd		}
139204076Spjd		if (res == NULL) {
140204076Spjd			/*
141204076Spjd			 * This can happen when new connection arrives and we
142204076Spjd			 * cancel child responsible for the old one.
143204076Spjd			 */
144204076Spjd			continue;
145204076Spjd		}
146204076Spjd		pjdlog_prefix_set("[%s] (%s) ", res->hr_name,
147204076Spjd		    role2str(res->hr_role));
148207372Spjd		child_exit_log(pid, status);
149206696Spjd		proto_close(res->hr_ctrl);
150204076Spjd		res->hr_workerpid = 0;
151204076Spjd		if (res->hr_role == HAST_ROLE_PRIMARY) {
152207372Spjd			/*
153207372Spjd			 * Restart child process if it was killed by signal
154207372Spjd			 * or exited because of temporary problem.
155207372Spjd			 */
156207372Spjd			if (WIFSIGNALED(status) ||
157207372Spjd			    (WIFEXITED(status) &&
158207372Spjd			     WEXITSTATUS(status) == EX_TEMPFAIL)) {
159207348Spjd				sleep(1);
160207348Spjd				pjdlog_info("Restarting worker process.");
161207348Spjd				hastd_primary(res);
162207348Spjd			} else {
163207348Spjd				res->hr_role = HAST_ROLE_INIT;
164207348Spjd				pjdlog_info("Changing resource role back to %s.",
165207348Spjd				    role2str(res->hr_role));
166207348Spjd			}
167204076Spjd		}
168204076Spjd		pjdlog_prefix_set("%s", "");
169204076Spjd	}
170204076Spjd}
171204076Spjd
172204076Spjdstatic void
173204076Spjdhastd_reload(void)
174204076Spjd{
175204076Spjd
176204076Spjd	/* TODO */
177204076Spjd	pjdlog_warning("Configuration reload is not implemented.");
178204076Spjd}
179204076Spjd
180204076Spjdstatic void
181204076Spjdlisten_accept(void)
182204076Spjd{
183204076Spjd	struct hast_resource *res;
184204076Spjd	struct proto_conn *conn;
185204076Spjd	struct nv *nvin, *nvout, *nverr;
186204076Spjd	const char *resname;
187204076Spjd	const unsigned char *token;
188204076Spjd	char laddr[256], raddr[256];
189204076Spjd	size_t size;
190204076Spjd	pid_t pid;
191204076Spjd	int status;
192204076Spjd
193204076Spjd	proto_local_address(cfg->hc_listenconn, laddr, sizeof(laddr));
194204076Spjd	pjdlog_debug(1, "Accepting connection to %s.", laddr);
195204076Spjd
196204076Spjd	if (proto_accept(cfg->hc_listenconn, &conn) < 0) {
197204076Spjd		pjdlog_errno(LOG_ERR, "Unable to accept connection %s", laddr);
198204076Spjd		return;
199204076Spjd	}
200204076Spjd
201204076Spjd	proto_local_address(conn, laddr, sizeof(laddr));
202204076Spjd	proto_remote_address(conn, raddr, sizeof(raddr));
203204076Spjd	pjdlog_info("Connection from %s to %s.", laddr, raddr);
204204076Spjd
205207371Spjd	/* Error in setting timeout is not critical, but why should it fail? */
206207371Spjd	if (proto_timeout(conn, HAST_TIMEOUT) < 0)
207207371Spjd		pjdlog_errno(LOG_WARNING, "Unable to set connection timeout");
208207371Spjd
209204076Spjd	nvin = nvout = nverr = NULL;
210204076Spjd
211204076Spjd	/*
212204076Spjd	 * Before receiving any data see if remote host have access to any
213204076Spjd	 * resource.
214204076Spjd	 */
215204076Spjd	TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
216204076Spjd		if (proto_address_match(conn, res->hr_remoteaddr))
217204076Spjd			break;
218204076Spjd	}
219204076Spjd	if (res == NULL) {
220204076Spjd		pjdlog_error("Client %s isn't known.", raddr);
221204076Spjd		goto close;
222204076Spjd	}
223204076Spjd	/* Ok, remote host can access at least one resource. */
224204076Spjd
225204076Spjd	if (hast_proto_recv_hdr(conn, &nvin) < 0) {
226204076Spjd		pjdlog_errno(LOG_ERR, "Unable to receive header from %s",
227204076Spjd		    raddr);
228204076Spjd		goto close;
229204076Spjd	}
230204076Spjd
231204076Spjd	resname = nv_get_string(nvin, "resource");
232204076Spjd	if (resname == NULL) {
233204076Spjd		pjdlog_error("No 'resource' field in the header received from %s.",
234204076Spjd		    raddr);
235204076Spjd		goto close;
236204076Spjd	}
237204076Spjd	pjdlog_debug(2, "%s: resource=%s", raddr, resname);
238204076Spjd	token = nv_get_uint8_array(nvin, &size, "token");
239204076Spjd	/*
240204076Spjd	 * NULL token means that this is first conection.
241204076Spjd	 */
242204076Spjd	if (token != NULL && size != sizeof(res->hr_token)) {
243204076Spjd		pjdlog_error("Received token of invalid size from %s (expected %zu, got %zu).",
244204076Spjd		    raddr, sizeof(res->hr_token), size);
245204076Spjd		goto close;
246204076Spjd	}
247204076Spjd
248204076Spjd	/*
249204076Spjd	 * From now on we want to send errors to the remote node.
250204076Spjd	 */
251204076Spjd	nverr = nv_alloc();
252204076Spjd
253204076Spjd	/* Find resource related to this connection. */
254204076Spjd	TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
255204076Spjd		if (strcmp(resname, res->hr_name) == 0)
256204076Spjd			break;
257204076Spjd	}
258204076Spjd	/* Have we found the resource? */
259204076Spjd	if (res == NULL) {
260204076Spjd		pjdlog_error("No resource '%s' as requested by %s.",
261204076Spjd		    resname, raddr);
262204076Spjd		nv_add_stringf(nverr, "errmsg", "Resource not configured.");
263204076Spjd		goto fail;
264204076Spjd	}
265204076Spjd
266204076Spjd	/* Now that we know resource name setup log prefix. */
267204076Spjd	pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role));
268204076Spjd
269204076Spjd	/* Does the remote host have access to this resource? */
270204076Spjd	if (!proto_address_match(conn, res->hr_remoteaddr)) {
271204076Spjd		pjdlog_error("Client %s has no access to the resource.", raddr);
272204076Spjd		nv_add_stringf(nverr, "errmsg", "No access to the resource.");
273204076Spjd		goto fail;
274204076Spjd	}
275204076Spjd	/* Is the resource marked as secondary? */
276204076Spjd	if (res->hr_role != HAST_ROLE_SECONDARY) {
277204076Spjd		pjdlog_error("We act as %s for the resource and not as %s as requested by %s.",
278204076Spjd		    role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY),
279204076Spjd		    raddr);
280204076Spjd		nv_add_stringf(nverr, "errmsg",
281204076Spjd		    "Remote node acts as %s for the resource and not as %s.",
282204076Spjd		    role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY));
283204076Spjd		goto fail;
284204076Spjd	}
285204076Spjd	/* Does token (if exists) match? */
286204076Spjd	if (token != NULL && memcmp(token, res->hr_token,
287204076Spjd	    sizeof(res->hr_token)) != 0) {
288204076Spjd		pjdlog_error("Token received from %s doesn't match.", raddr);
289204076Spjd		nv_add_stringf(nverr, "errmsg", "Toke doesn't match.");
290204076Spjd		goto fail;
291204076Spjd	}
292204076Spjd	/*
293204076Spjd	 * If there is no token, but we have half-open connection
294204076Spjd	 * (only remotein) or full connection (worker process is running)
295204076Spjd	 * we have to cancel those and accept the new connection.
296204076Spjd	 */
297204076Spjd	if (token == NULL) {
298204076Spjd		assert(res->hr_remoteout == NULL);
299204076Spjd		pjdlog_debug(1, "Initial connection from %s.", raddr);
300204076Spjd		if (res->hr_workerpid != 0) {
301204076Spjd			assert(res->hr_remotein == NULL);
302204076Spjd			pjdlog_debug(1,
303204076Spjd			    "Worker process exists (pid=%u), stopping it.",
304204076Spjd			    (unsigned int)res->hr_workerpid);
305204076Spjd			/* Stop child process. */
306204076Spjd			if (kill(res->hr_workerpid, SIGINT) < 0) {
307204076Spjd				pjdlog_errno(LOG_ERR,
308204076Spjd				    "Unable to stop worker process (pid=%u)",
309204076Spjd				    (unsigned int)res->hr_workerpid);
310204076Spjd				/*
311204076Spjd				 * Other than logging the problem we
312204076Spjd				 * ignore it - nothing smart to do.
313204076Spjd				 */
314204076Spjd			}
315204076Spjd			/* Wait for it to exit. */
316204076Spjd			else if ((pid = waitpid(res->hr_workerpid,
317204076Spjd			    &status, 0)) != res->hr_workerpid) {
318207372Spjd				/* We can only log the problem. */
319204076Spjd				pjdlog_errno(LOG_ERR,
320204076Spjd				    "Waiting for worker process (pid=%u) failed",
321204076Spjd				    (unsigned int)res->hr_workerpid);
322204076Spjd			} else {
323207372Spjd				child_exit_log(res->hr_workerpid, status);
324204076Spjd			}
325204076Spjd			res->hr_workerpid = 0;
326204076Spjd		} else if (res->hr_remotein != NULL) {
327204076Spjd			char oaddr[256];
328204076Spjd
329204076Spjd			proto_remote_address(conn, oaddr, sizeof(oaddr));
330204076Spjd			pjdlog_debug(1,
331204076Spjd			    "Canceling half-open connection from %s on connection from %s.",
332204076Spjd			    oaddr, raddr);
333204076Spjd			proto_close(res->hr_remotein);
334204076Spjd			res->hr_remotein = NULL;
335204076Spjd		}
336204076Spjd	}
337204076Spjd
338204076Spjd	/*
339204076Spjd	 * Checks and cleanups are done.
340204076Spjd	 */
341204076Spjd
342204076Spjd	if (token == NULL) {
343204076Spjd		arc4random_buf(res->hr_token, sizeof(res->hr_token));
344204076Spjd		nvout = nv_alloc();
345204076Spjd		nv_add_uint8_array(nvout, res->hr_token,
346204076Spjd		    sizeof(res->hr_token), "token");
347204076Spjd		if (nv_error(nvout) != 0) {
348204076Spjd			pjdlog_common(LOG_ERR, 0, nv_error(nvout),
349204076Spjd			    "Unable to prepare return header for %s", raddr);
350204076Spjd			nv_add_stringf(nverr, "errmsg",
351204076Spjd			    "Remote node was unable to prepare return header: %s.",
352204076Spjd			    strerror(nv_error(nvout)));
353204076Spjd			goto fail;
354204076Spjd		}
355204076Spjd		if (hast_proto_send(NULL, conn, nvout, NULL, 0) < 0) {
356204076Spjd			int error = errno;
357204076Spjd
358204076Spjd			pjdlog_errno(LOG_ERR, "Unable to send response to %s",
359204076Spjd			    raddr);
360204076Spjd			nv_add_stringf(nverr, "errmsg",
361204076Spjd			    "Remote node was unable to send response: %s.",
362204076Spjd			    strerror(error));
363204076Spjd			goto fail;
364204076Spjd		}
365204076Spjd		res->hr_remotein = conn;
366204076Spjd		pjdlog_debug(1, "Incoming connection from %s configured.",
367204076Spjd		    raddr);
368204076Spjd	} else {
369204076Spjd		res->hr_remoteout = conn;
370204076Spjd		pjdlog_debug(1, "Outgoing connection to %s configured.", raddr);
371204076Spjd		hastd_secondary(res, nvin);
372204076Spjd	}
373204076Spjd	nv_free(nvin);
374204076Spjd	nv_free(nvout);
375204076Spjd	nv_free(nverr);
376204076Spjd	pjdlog_prefix_set("%s", "");
377204076Spjd	return;
378204076Spjdfail:
379204076Spjd	if (nv_error(nverr) != 0) {
380204076Spjd		pjdlog_common(LOG_ERR, 0, nv_error(nverr),
381204076Spjd		    "Unable to prepare error header for %s", raddr);
382204076Spjd		goto close;
383204076Spjd	}
384204076Spjd	if (hast_proto_send(NULL, conn, nverr, NULL, 0) < 0) {
385204076Spjd		pjdlog_errno(LOG_ERR, "Unable to send error to %s", raddr);
386204076Spjd		goto close;
387204076Spjd	}
388204076Spjdclose:
389204076Spjd	if (nvin != NULL)
390204076Spjd		nv_free(nvin);
391204076Spjd	if (nvout != NULL)
392204076Spjd		nv_free(nvout);
393204076Spjd	if (nverr != NULL)
394204076Spjd		nv_free(nverr);
395204076Spjd	proto_close(conn);
396204076Spjd	pjdlog_prefix_set("%s", "");
397204076Spjd}
398204076Spjd
399204076Spjdstatic void
400204076Spjdmain_loop(void)
401204076Spjd{
402204076Spjd	fd_set rfds, wfds;
403204076Spjd	int fd, maxfd, ret;
404204076Spjd
405204076Spjd	for (;;) {
406204076Spjd		if (sigchld_received) {
407204076Spjd			sigchld_received = false;
408204076Spjd			child_exit();
409204076Spjd		}
410204076Spjd		if (sighup_received) {
411204076Spjd			sighup_received = false;
412204076Spjd			hastd_reload();
413204076Spjd		}
414204076Spjd
415204076Spjd		maxfd = 0;
416204076Spjd		FD_ZERO(&rfds);
417204076Spjd		FD_ZERO(&wfds);
418204076Spjd
419204076Spjd		/* Setup descriptors for select(2). */
420204076Spjd#define	SETUP_FD(conn)	do {						\
421204076Spjd	fd = proto_descriptor(conn);					\
422204076Spjd	if (fd >= 0) {							\
423204076Spjd		maxfd = fd > maxfd ? fd : maxfd;			\
424204076Spjd		FD_SET(fd, &rfds);					\
425204076Spjd		FD_SET(fd, &wfds);					\
426204076Spjd	}								\
427204076Spjd} while (0)
428204076Spjd		SETUP_FD(cfg->hc_controlconn);
429204076Spjd		SETUP_FD(cfg->hc_listenconn);
430204076Spjd#undef	SETUP_FD
431204076Spjd
432204076Spjd		ret = select(maxfd + 1, &rfds, &wfds, NULL, NULL);
433204076Spjd		if (ret == -1) {
434204076Spjd			if (errno == EINTR)
435204076Spjd				continue;
436204076Spjd			KEEP_ERRNO((void)pidfile_remove(pfh));
437204076Spjd			pjdlog_exit(EX_OSERR, "select() failed");
438204076Spjd		}
439204076Spjd
440204076Spjd#define	ISSET_FD(conn)	\
441204076Spjd	(FD_ISSET((fd = proto_descriptor(conn)), &rfds) || FD_ISSET(fd, &wfds))
442204076Spjd		if (ISSET_FD(cfg->hc_controlconn))
443204076Spjd			control_handle(cfg);
444204076Spjd		if (ISSET_FD(cfg->hc_listenconn))
445204076Spjd			listen_accept();
446204076Spjd#undef	ISSET_FD
447204076Spjd	}
448204076Spjd}
449204076Spjd
450204076Spjdint
451204076Spjdmain(int argc, char *argv[])
452204076Spjd{
453204076Spjd	const char *pidfile;
454204076Spjd	pid_t otherpid;
455204076Spjd	bool foreground;
456204076Spjd	int debuglevel;
457204076Spjd
458204076Spjd	g_gate_load();
459204076Spjd
460204076Spjd	foreground = false;
461204076Spjd	debuglevel = 0;
462204076Spjd	pidfile = HASTD_PIDFILE;
463204076Spjd
464204076Spjd	for (;;) {
465204076Spjd		int ch;
466204076Spjd
467204076Spjd		ch = getopt(argc, argv, "c:dFhP:");
468204076Spjd		if (ch == -1)
469204076Spjd			break;
470204076Spjd		switch (ch) {
471204076Spjd		case 'c':
472204076Spjd			cfgpath = optarg;
473204076Spjd			break;
474204076Spjd		case 'd':
475204076Spjd			debuglevel++;
476204076Spjd			break;
477204076Spjd		case 'F':
478204076Spjd			foreground = true;
479204076Spjd			break;
480204076Spjd		case 'P':
481204076Spjd			pidfile = optarg;
482204076Spjd			break;
483204076Spjd		case 'h':
484204076Spjd		default:
485204076Spjd			usage();
486204076Spjd		}
487204076Spjd	}
488204076Spjd	argc -= optind;
489204076Spjd	argv += optind;
490204076Spjd
491204076Spjd	pjdlog_debug_set(debuglevel);
492204076Spjd
493204076Spjd	pfh = pidfile_open(pidfile, 0600, &otherpid);
494204076Spjd	if (pfh == NULL) {
495204076Spjd		if (errno == EEXIST) {
496204076Spjd			pjdlog_exitx(EX_TEMPFAIL,
497204076Spjd			    "Another hastd is already running, pid: %jd.",
498204076Spjd			    (intmax_t)otherpid);
499204076Spjd		}
500204076Spjd		/* If we cannot create pidfile from other reasons, only warn. */
501204076Spjd		pjdlog_errno(LOG_WARNING, "Cannot open or create pidfile");
502204076Spjd	}
503204076Spjd
504204076Spjd	cfg = yy_config_parse(cfgpath);
505204076Spjd	assert(cfg != NULL);
506204076Spjd
507204076Spjd	signal(SIGHUP, sighandler);
508204076Spjd	signal(SIGCHLD, sighandler);
509204076Spjd
510204076Spjd	/* Listen on control address. */
511204076Spjd	if (proto_server(cfg->hc_controladdr, &cfg->hc_controlconn) < 0) {
512204076Spjd		KEEP_ERRNO((void)pidfile_remove(pfh));
513204076Spjd		pjdlog_exit(EX_OSERR, "Unable to listen on control address %s",
514204076Spjd		    cfg->hc_controladdr);
515204076Spjd	}
516204076Spjd	/* Listen for remote connections. */
517204076Spjd	if (proto_server(cfg->hc_listenaddr, &cfg->hc_listenconn) < 0) {
518204076Spjd		KEEP_ERRNO((void)pidfile_remove(pfh));
519204076Spjd		pjdlog_exit(EX_OSERR, "Unable to listen on address %s",
520204076Spjd		    cfg->hc_listenaddr);
521204076Spjd	}
522204076Spjd
523204076Spjd	if (!foreground) {
524204076Spjd		if (daemon(0, 0) < 0) {
525204076Spjd			KEEP_ERRNO((void)pidfile_remove(pfh));
526204076Spjd			pjdlog_exit(EX_OSERR, "Unable to daemonize");
527204076Spjd		}
528204076Spjd
529204076Spjd		/* Start logging to syslog. */
530204076Spjd		pjdlog_mode_set(PJDLOG_MODE_SYSLOG);
531204076Spjd
532204076Spjd		/* Write PID to a file. */
533204076Spjd		if (pidfile_write(pfh) < 0) {
534204076Spjd			pjdlog_errno(LOG_WARNING,
535204076Spjd			    "Unable to write PID to a file");
536204076Spjd		}
537204076Spjd	}
538204076Spjd
539204076Spjd	main_loop();
540204076Spjd
541204076Spjd	exit(0);
542204076Spjd}
543