hastd.c revision 207372
1204792Srdivacky/*-
2204792Srdivacky * Copyright (c) 2009-2010 The FreeBSD Foundation
3204792Srdivacky * All rights reserved.
4204792Srdivacky *
5204792Srdivacky * This software was developed by Pawel Jakub Dawidek under sponsorship from
6204792Srdivacky * the FreeBSD Foundation.
7204792Srdivacky *
8204792Srdivacky * Redistribution and use in source and binary forms, with or without
9204792Srdivacky * modification, are permitted provided that the following conditions
10204792Srdivacky * are met:
11204792Srdivacky * 1. Redistributions of source code must retain the above copyright
12204792Srdivacky *    notice, this list of conditions and the following disclaimer.
13204792Srdivacky * 2. Redistributions in binary form must reproduce the above copyright
14204792Srdivacky *    notice, this list of conditions and the following disclaimer in the
15204792Srdivacky *    documentation and/or other materials provided with the distribution.
16204792Srdivacky *
17204792Srdivacky * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18204792Srdivacky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19204792Srdivacky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20204792Srdivacky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21204792Srdivacky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22204792Srdivacky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23204792Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24204792Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25204792Srdivacky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26204792Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27204792Srdivacky * SUCH DAMAGE.
28204792Srdivacky */
29204792Srdivacky
30204792Srdivacky#include <sys/cdefs.h>
31204792Srdivacky__FBSDID("$FreeBSD: head/sbin/hastd/hastd.c 207372 2010-04-29 15:42:24Z pjd $");
32204792Srdivacky
33204792Srdivacky#include <sys/param.h>
34204792Srdivacky#include <sys/linker.h>
35204792Srdivacky#include <sys/module.h>
36204792Srdivacky#include <sys/wait.h>
37204792Srdivacky
38204792Srdivacky#include <assert.h>
39204792Srdivacky#include <err.h>
40204792Srdivacky#include <errno.h>
41204792Srdivacky#include <libutil.h>
42204792Srdivacky#include <signal.h>
43204792Srdivacky#include <stdbool.h>
44204792Srdivacky#include <stdio.h>
45204792Srdivacky#include <stdlib.h>
46204792Srdivacky#include <string.h>
47204792Srdivacky#include <sysexits.h>
48204792Srdivacky#include <unistd.h>
49204792Srdivacky
50204792Srdivacky#include <activemap.h>
51204792Srdivacky#include <pjdlog.h>
52204792Srdivacky
53204792Srdivacky#include "control.h"
54204792Srdivacky#include "hast.h"
55204792Srdivacky#include "hast_proto.h"
56204792Srdivacky#include "hastd.h"
57204792Srdivacky#include "subr.h"
58204792Srdivacky
59204792Srdivacky/* Path to configuration file. */
60204792Srdivackystatic const char *cfgpath = HAST_CONFIG;
61226633Sdim/* Hastd configuration. */
62226633Sdimstatic struct hastd_config *cfg;
63204792Srdivacky/* Was SIGCHLD signal received? */
64204792Srdivackystatic bool sigchld_received = false;
65204792Srdivacky/* Was SIGHUP signal received? */
66204792Srdivackystatic bool sighup_received = false;
67204792Srdivacky/* Was SIGINT or SIGTERM signal received? */
68204792Srdivackybool sigexit_received = false;
69204792Srdivacky/* PID file handle. */
70204792Srdivackystruct pidfh *pfh;
71204792Srdivacky
72210299Sedstatic void
73210299Sedusage(void)
74210299Sed{
75210299Sed
76210299Sed	errx(EX_USAGE, "[-dFh] [-c config] [-P pidfile]");
77210299Sed}
78210299Sed
79210299Sedstatic void
80210299Sedsighandler(int sig)
81210299Sed{
82210299Sed
83210299Sed	switch (sig) {
84210299Sed	case SIGCHLD:
85210299Sed		sigchld_received = true;
86210299Sed		break;
87210299Sed	case SIGHUP:
88210299Sed		sighup_received = true;
89210299Sed		break;
90210299Sed	default:
91210299Sed		assert(!"invalid condition");
92210299Sed	}
93210299Sed}
94210299Sed
95210299Sedstatic void
96210299Sedg_gate_load(void)
97204792Srdivacky{
98204792Srdivacky
99204792Srdivacky	if (modfind("g_gate") == -1) {
100205218Srdivacky		/* Not present in kernel, try loading it. */
101204792Srdivacky		if (kldload("geom_gate") == -1 || modfind("g_gate") == -1) {
102204792Srdivacky			if (errno != EEXIST) {
103204792Srdivacky				pjdlog_exit(EX_OSERR,
104204792Srdivacky				    "Unable to load geom_gate module");
105226633Sdim			}
106205218Srdivacky		}
107204792Srdivacky	}
108204792Srdivacky}
109205218Srdivacky
110204792Srdivackystatic void
111204792Srdivackychild_exit_log(unsigned int pid, int status)
112204792Srdivacky{
113204792Srdivacky
114204792Srdivacky	if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
115205218Srdivacky		pjdlog_debug(1, "Worker process exited gracefully (pid=%u).",
116205218Srdivacky		    pid);
117205218Srdivacky	} else if (WIFSIGNALED(status)) {
118207618Srdivacky		pjdlog_error("Worker process killed (pid=%u, signal=%d).",
119205218Srdivacky		    pid, WTERMSIG(status));
120205218Srdivacky	} else {
121205218Srdivacky		pjdlog_error("Worker process exited ungracefully (pid=%u, exitcode=%d).",
122205218Srdivacky		    pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1);
123226633Sdim	}
124207618Srdivacky}
125207618Srdivacky
126207618Srdivackystatic void
127205218Srdivackychild_exit(void)
128205218Srdivacky{
129205218Srdivacky	struct hast_resource *res;
130205218Srdivacky	int status;
131205218Srdivacky	pid_t pid;
132205218Srdivacky
133205218Srdivacky	while ((pid = wait3(&status, WNOHANG, NULL)) > 0) {
134206083Srdivacky		/* Find resource related to the process that just exited. */
135206083Srdivacky		TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
136206083Srdivacky			if (pid == res->hr_workerpid)
137206083Srdivacky				break;
138206083Srdivacky		}
139206083Srdivacky		if (res == NULL) {
140206083Srdivacky			/*
141206083Srdivacky			 * This can happen when new connection arrives and we
142206083Srdivacky			 * cancel child responsible for the old one.
143206083Srdivacky			 */
144206083Srdivacky			continue;
145206083Srdivacky		}
146206083Srdivacky		pjdlog_prefix_set("[%s] (%s) ", res->hr_name,
147206083Srdivacky		    role2str(res->hr_role));
148206083Srdivacky		child_exit_log(pid, status);
149206083Srdivacky		proto_close(res->hr_ctrl);
150206083Srdivacky		res->hr_workerpid = 0;
151206083Srdivacky		if (res->hr_role == HAST_ROLE_PRIMARY) {
152206083Srdivacky			/*
153206083Srdivacky			 * Restart child process if it was killed by signal
154206083Srdivacky			 * or exited because of temporary problem.
155206083Srdivacky			 */
156206083Srdivacky			if (WIFSIGNALED(status) ||
157206083Srdivacky			    (WIFEXITED(status) &&
158204792Srdivacky			     WEXITSTATUS(status) == EX_TEMPFAIL)) {
159204792Srdivacky				sleep(1);
160204792Srdivacky				pjdlog_info("Restarting worker process.");
161204792Srdivacky				hastd_primary(res);
162204792Srdivacky			} else {
163204792Srdivacky				res->hr_role = HAST_ROLE_INIT;
164204792Srdivacky				pjdlog_info("Changing resource role back to %s.",
165204792Srdivacky				    role2str(res->hr_role));
166204792Srdivacky			}
167204792Srdivacky		}
168204792Srdivacky		pjdlog_prefix_set("%s", "");
169204792Srdivacky	}
170204792Srdivacky}
171204792Srdivacky
172204792Srdivackystatic void
173204792Srdivackyhastd_reload(void)
174204792Srdivacky{
175204792Srdivacky
176204792Srdivacky	/* TODO */
177204792Srdivacky	pjdlog_warning("Configuration reload is not implemented.");
178204792Srdivacky}
179204792Srdivacky
180204792Srdivackystatic void
181204792Srdivackylisten_accept(void)
182204792Srdivacky{
183204792Srdivacky	struct hast_resource *res;
184204792Srdivacky	struct proto_conn *conn;
185204792Srdivacky	struct nv *nvin, *nvout, *nverr;
186204792Srdivacky	const char *resname;
187204792Srdivacky	const unsigned char *token;
188204792Srdivacky	char laddr[256], raddr[256];
189204792Srdivacky	size_t size;
190204792Srdivacky	pid_t pid;
191204792Srdivacky	int status;
192204792Srdivacky
193204792Srdivacky	proto_local_address(cfg->hc_listenconn, laddr, sizeof(laddr));
194204792Srdivacky	pjdlog_debug(1, "Accepting connection to %s.", laddr);
195204792Srdivacky
196204792Srdivacky	if (proto_accept(cfg->hc_listenconn, &conn) < 0) {
197204792Srdivacky		pjdlog_errno(LOG_ERR, "Unable to accept connection %s", laddr);
198204792Srdivacky		return;
199204792Srdivacky	}
200204792Srdivacky
201204792Srdivacky	proto_local_address(conn, laddr, sizeof(laddr));
202204792Srdivacky	proto_remote_address(conn, raddr, sizeof(raddr));
203204792Srdivacky	pjdlog_info("Connection from %s to %s.", laddr, raddr);
204204792Srdivacky
205204792Srdivacky	/* Error in setting timeout is not critical, but why should it fail? */
206204792Srdivacky	if (proto_timeout(conn, HAST_TIMEOUT) < 0)
207204792Srdivacky		pjdlog_errno(LOG_WARNING, "Unable to set connection timeout");
208204792Srdivacky
209204792Srdivacky	nvin = nvout = nverr = NULL;
210204792Srdivacky
211204792Srdivacky	/*
212204792Srdivacky	 * Before receiving any data see if remote host have access to any
213204792Srdivacky	 * resource.
214204792Srdivacky	 */
215204792Srdivacky	TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
216204792Srdivacky		if (proto_address_match(conn, res->hr_remoteaddr))
217204792Srdivacky			break;
218204792Srdivacky	}
219204792Srdivacky	if (res == NULL) {
220204792Srdivacky		pjdlog_error("Client %s isn't known.", raddr);
221204792Srdivacky		goto close;
222204792Srdivacky	}
223204792Srdivacky	/* Ok, remote host can access at least one resource. */
224204792Srdivacky
225204792Srdivacky	if (hast_proto_recv_hdr(conn, &nvin) < 0) {
226204792Srdivacky		pjdlog_errno(LOG_ERR, "Unable to receive header from %s",
227204792Srdivacky		    raddr);
228204792Srdivacky		goto close;
229204792Srdivacky	}
230204792Srdivacky
231204792Srdivacky	resname = nv_get_string(nvin, "resource");
232204792Srdivacky	if (resname == NULL) {
233204792Srdivacky		pjdlog_error("No 'resource' field in the header received from %s.",
234204792Srdivacky		    raddr);
235204792Srdivacky		goto close;
236204792Srdivacky	}
237204792Srdivacky	pjdlog_debug(2, "%s: resource=%s", raddr, resname);
238204792Srdivacky	token = nv_get_uint8_array(nvin, &size, "token");
239204792Srdivacky	/*
240204792Srdivacky	 * NULL token means that this is first conection.
241204792Srdivacky	 */
242204792Srdivacky	if (token != NULL && size != sizeof(res->hr_token)) {
243204792Srdivacky		pjdlog_error("Received token of invalid size from %s (expected %zu, got %zu).",
244204792Srdivacky		    raddr, sizeof(res->hr_token), size);
245204792Srdivacky		goto close;
246204792Srdivacky	}
247204792Srdivacky
248204792Srdivacky	/*
249204792Srdivacky	 * From now on we want to send errors to the remote node.
250204792Srdivacky	 */
251204792Srdivacky	nverr = nv_alloc();
252204792Srdivacky
253204792Srdivacky	/* Find resource related to this connection. */
254204792Srdivacky	TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
255204792Srdivacky		if (strcmp(resname, res->hr_name) == 0)
256204792Srdivacky			break;
257204792Srdivacky	}
258204792Srdivacky	/* Have we found the resource? */
259204792Srdivacky	if (res == NULL) {
260204792Srdivacky		pjdlog_error("No resource '%s' as requested by %s.",
261204792Srdivacky		    resname, raddr);
262204792Srdivacky		nv_add_stringf(nverr, "errmsg", "Resource not configured.");
263204792Srdivacky		goto fail;
264204792Srdivacky	}
265204792Srdivacky
266204792Srdivacky	/* Now that we know resource name setup log prefix. */
267204792Srdivacky	pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role));
268204792Srdivacky
269204792Srdivacky	/* Does the remote host have access to this resource? */
270204792Srdivacky	if (!proto_address_match(conn, res->hr_remoteaddr)) {
271204792Srdivacky		pjdlog_error("Client %s has no access to the resource.", raddr);
272204792Srdivacky		nv_add_stringf(nverr, "errmsg", "No access to the resource.");
273204792Srdivacky		goto fail;
274204792Srdivacky	}
275204792Srdivacky	/* Is the resource marked as secondary? */
276204792Srdivacky	if (res->hr_role != HAST_ROLE_SECONDARY) {
277204792Srdivacky		pjdlog_error("We act as %s for the resource and not as %s as requested by %s.",
278204792Srdivacky		    role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY),
279204792Srdivacky		    raddr);
280204792Srdivacky		nv_add_stringf(nverr, "errmsg",
281204792Srdivacky		    "Remote node acts as %s for the resource and not as %s.",
282204792Srdivacky		    role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY));
283204792Srdivacky		goto fail;
284204792Srdivacky	}
285204792Srdivacky	/* Does token (if exists) match? */
286204792Srdivacky	if (token != NULL && memcmp(token, res->hr_token,
287204792Srdivacky	    sizeof(res->hr_token)) != 0) {
288204792Srdivacky		pjdlog_error("Token received from %s doesn't match.", raddr);
289204792Srdivacky		nv_add_stringf(nverr, "errmsg", "Toke doesn't match.");
290204792Srdivacky		goto fail;
291204792Srdivacky	}
292204792Srdivacky	/*
293204792Srdivacky	 * If there is no token, but we have half-open connection
294204792Srdivacky	 * (only remotein) or full connection (worker process is running)
295204792Srdivacky	 * we have to cancel those and accept the new connection.
296204792Srdivacky	 */
297204792Srdivacky	if (token == NULL) {
298204792Srdivacky		assert(res->hr_remoteout == NULL);
299204792Srdivacky		pjdlog_debug(1, "Initial connection from %s.", raddr);
300204792Srdivacky		if (res->hr_workerpid != 0) {
301204792Srdivacky			assert(res->hr_remotein == NULL);
302204792Srdivacky			pjdlog_debug(1,
303204792Srdivacky			    "Worker process exists (pid=%u), stopping it.",
304204792Srdivacky			    (unsigned int)res->hr_workerpid);
305204792Srdivacky			/* Stop child process. */
306204792Srdivacky			if (kill(res->hr_workerpid, SIGINT) < 0) {
307204792Srdivacky				pjdlog_errno(LOG_ERR,
308204792Srdivacky				    "Unable to stop worker process (pid=%u)",
309204792Srdivacky				    (unsigned int)res->hr_workerpid);
310204792Srdivacky				/*
311204792Srdivacky				 * Other than logging the problem we
312204792Srdivacky				 * ignore it - nothing smart to do.
313204792Srdivacky				 */
314204792Srdivacky			}
315204792Srdivacky			/* Wait for it to exit. */
316204792Srdivacky			else if ((pid = waitpid(res->hr_workerpid,
317204792Srdivacky			    &status, 0)) != res->hr_workerpid) {
318204792Srdivacky				/* We can only log the problem. */
319204792Srdivacky				pjdlog_errno(LOG_ERR,
320204792Srdivacky				    "Waiting for worker process (pid=%u) failed",
321204792Srdivacky				    (unsigned int)res->hr_workerpid);
322204792Srdivacky			} else {
323204792Srdivacky				child_exit_log(res->hr_workerpid, status);
324204792Srdivacky			}
325204792Srdivacky			res->hr_workerpid = 0;
326204792Srdivacky		} else if (res->hr_remotein != NULL) {
327204792Srdivacky			char oaddr[256];
328204792Srdivacky
329204792Srdivacky			proto_remote_address(conn, oaddr, sizeof(oaddr));
330204792Srdivacky			pjdlog_debug(1,
331204792Srdivacky			    "Canceling half-open connection from %s on connection from %s.",
332204792Srdivacky			    oaddr, raddr);
333204792Srdivacky			proto_close(res->hr_remotein);
334204792Srdivacky			res->hr_remotein = NULL;
335204792Srdivacky		}
336204792Srdivacky	}
337204792Srdivacky
338204792Srdivacky	/*
339204792Srdivacky	 * Checks and cleanups are done.
340204792Srdivacky	 */
341204792Srdivacky
342204792Srdivacky	if (token == NULL) {
343204792Srdivacky		arc4random_buf(res->hr_token, sizeof(res->hr_token));
344204792Srdivacky		nvout = nv_alloc();
345204792Srdivacky		nv_add_uint8_array(nvout, res->hr_token,
346204792Srdivacky		    sizeof(res->hr_token), "token");
347204792Srdivacky		if (nv_error(nvout) != 0) {
348204792Srdivacky			pjdlog_common(LOG_ERR, 0, nv_error(nvout),
349204792Srdivacky			    "Unable to prepare return header for %s", raddr);
350204792Srdivacky			nv_add_stringf(nverr, "errmsg",
351204792Srdivacky			    "Remote node was unable to prepare return header: %s.",
352204792Srdivacky			    strerror(nv_error(nvout)));
353204792Srdivacky			goto fail;
354205218Srdivacky		}
355205218Srdivacky		if (hast_proto_send(NULL, conn, nvout, NULL, 0) < 0) {
356205218Srdivacky			int error = errno;
357205218Srdivacky
358207618Srdivacky			pjdlog_errno(LOG_ERR, "Unable to send response to %s",
359207618Srdivacky			    raddr);
360207618Srdivacky			nv_add_stringf(nverr, "errmsg",
361205218Srdivacky			    "Remote node was unable to send response: %s.",
362207618Srdivacky			    strerror(error));
363207618Srdivacky			goto fail;
364226633Sdim		}
365207618Srdivacky		res->hr_remotein = conn;
366223017Sdim		pjdlog_debug(1, "Incoming connection from %s configured.",
367205218Srdivacky		    raddr);
368205218Srdivacky	} else {
369207618Srdivacky		res->hr_remoteout = conn;
370207618Srdivacky		pjdlog_debug(1, "Outgoing connection to %s configured.", raddr);
371207618Srdivacky		hastd_secondary(res, nvin);
372207618Srdivacky	}
373207618Srdivacky	nv_free(nvin);
374207618Srdivacky	nv_free(nvout);
375207618Srdivacky	nv_free(nverr);
376210299Sed	pjdlog_prefix_set("%s", "");
377212904Sdim	return;
378218893Sdimfail:
379218893Sdim	if (nv_error(nverr) != 0) {
380210299Sed		pjdlog_common(LOG_ERR, 0, nv_error(nverr),
381205218Srdivacky		    "Unable to prepare error header for %s", raddr);
382205218Srdivacky		goto close;
383205218Srdivacky	}
384205218Srdivacky	if (hast_proto_send(NULL, conn, nverr, NULL, 0) < 0) {
385205218Srdivacky		pjdlog_errno(LOG_ERR, "Unable to send error to %s", raddr);
386205218Srdivacky		goto close;
387205218Srdivacky	}
388205218Srdivackyclose:
389205218Srdivacky	if (nvin != NULL)
390205218Srdivacky		nv_free(nvin);
391205218Srdivacky	if (nvout != NULL)
392207618Srdivacky		nv_free(nvout);
393207618Srdivacky	if (nverr != NULL)
394207618Srdivacky		nv_free(nverr);
395207618Srdivacky	proto_close(conn);
396207618Srdivacky	pjdlog_prefix_set("%s", "");
397207618Srdivacky}
398207618Srdivacky
399210299Sedstatic void
400212904Sdimmain_loop(void)
401218893Sdim{
402218893Sdim	fd_set rfds, wfds;
403210299Sed	int fd, maxfd, ret;
404205218Srdivacky
405205218Srdivacky	for (;;) {
406205218Srdivacky		if (sigchld_received) {
407205218Srdivacky			sigchld_received = false;
408205218Srdivacky			child_exit();
409205218Srdivacky		}
410207618Srdivacky		if (sighup_received) {
411207618Srdivacky			sighup_received = false;
412207618Srdivacky			hastd_reload();
413207618Srdivacky		}
414207618Srdivacky
415207618Srdivacky		maxfd = 0;
416207618Srdivacky		FD_ZERO(&rfds);
417210299Sed		FD_ZERO(&wfds);
418212904Sdim
419210299Sed		/* Setup descriptors for select(2). */
420205218Srdivacky#define	SETUP_FD(conn)	do {						\
421218893Sdim	fd = proto_descriptor(conn);					\
422210299Sed	if (fd >= 0) {							\
423205218Srdivacky		maxfd = fd > maxfd ? fd : maxfd;			\
424205218Srdivacky		FD_SET(fd, &rfds);					\
425205218Srdivacky		FD_SET(fd, &wfds);					\
426205218Srdivacky	}								\
427205218Srdivacky} while (0)
428205218Srdivacky		SETUP_FD(cfg->hc_controlconn);
429207618Srdivacky		SETUP_FD(cfg->hc_listenconn);
430207618Srdivacky#undef	SETUP_FD
431207618Srdivacky
432207618Srdivacky		ret = select(maxfd + 1, &rfds, &wfds, NULL, NULL);
433207618Srdivacky		if (ret == -1) {
434207618Srdivacky			if (errno == EINTR)
435207618Srdivacky				continue;
436207618Srdivacky			KEEP_ERRNO((void)pidfile_remove(pfh));
437207618Srdivacky			pjdlog_exit(EX_OSERR, "select() failed");
438205218Srdivacky		}
439205218Srdivacky
440205218Srdivacky#define	ISSET_FD(conn)	\
441205218Srdivacky	(FD_ISSET((fd = proto_descriptor(conn)), &rfds) || FD_ISSET(fd, &wfds))
442205218Srdivacky		if (ISSET_FD(cfg->hc_controlconn))
443212904Sdim			control_handle(cfg);
444210299Sed		if (ISSET_FD(cfg->hc_listenconn))
445205218Srdivacky			listen_accept();
446205218Srdivacky#undef	ISSET_FD
447205218Srdivacky	}
448205218Srdivacky}
449205218Srdivacky
450205218Srdivackyint
451205218Srdivackymain(int argc, char *argv[])
452207618Srdivacky{
453207618Srdivacky	const char *pidfile;
454207618Srdivacky	pid_t otherpid;
455207618Srdivacky	bool foreground;
456207618Srdivacky	int debuglevel;
457207618Srdivacky
458207618Srdivacky	g_gate_load();
459207618Srdivacky
460210299Sed	foreground = false;
461212904Sdim	debuglevel = 0;
462210299Sed	pidfile = HASTD_PIDFILE;
463210299Sed
464205218Srdivacky	for (;;) {
465205218Srdivacky		int ch;
466205218Srdivacky
467205218Srdivacky		ch = getopt(argc, argv, "c:dFhP:");
468205218Srdivacky		if (ch == -1)
469205218Srdivacky			break;
470205218Srdivacky		switch (ch) {
471205218Srdivacky		case 'c':
472205218Srdivacky			cfgpath = optarg;
473205218Srdivacky			break;
474205218Srdivacky		case 'd':
475205218Srdivacky			debuglevel++;
476205218Srdivacky			break;
477205218Srdivacky		case 'F':
478205218Srdivacky			foreground = true;
479205218Srdivacky			break;
480		case 'P':
481			pidfile = optarg;
482			break;
483		case 'h':
484		default:
485			usage();
486		}
487	}
488	argc -= optind;
489	argv += optind;
490
491	pjdlog_debug_set(debuglevel);
492
493	pfh = pidfile_open(pidfile, 0600, &otherpid);
494	if (pfh == NULL) {
495		if (errno == EEXIST) {
496			pjdlog_exitx(EX_TEMPFAIL,
497			    "Another hastd is already running, pid: %jd.",
498			    (intmax_t)otherpid);
499		}
500		/* If we cannot create pidfile from other reasons, only warn. */
501		pjdlog_errno(LOG_WARNING, "Cannot open or create pidfile");
502	}
503
504	cfg = yy_config_parse(cfgpath);
505	assert(cfg != NULL);
506
507	signal(SIGHUP, sighandler);
508	signal(SIGCHLD, sighandler);
509
510	/* Listen on control address. */
511	if (proto_server(cfg->hc_controladdr, &cfg->hc_controlconn) < 0) {
512		KEEP_ERRNO((void)pidfile_remove(pfh));
513		pjdlog_exit(EX_OSERR, "Unable to listen on control address %s",
514		    cfg->hc_controladdr);
515	}
516	/* Listen for remote connections. */
517	if (proto_server(cfg->hc_listenaddr, &cfg->hc_listenconn) < 0) {
518		KEEP_ERRNO((void)pidfile_remove(pfh));
519		pjdlog_exit(EX_OSERR, "Unable to listen on address %s",
520		    cfg->hc_listenaddr);
521	}
522
523	if (!foreground) {
524		if (daemon(0, 0) < 0) {
525			KEEP_ERRNO((void)pidfile_remove(pfh));
526			pjdlog_exit(EX_OSERR, "Unable to daemonize");
527		}
528
529		/* Start logging to syslog. */
530		pjdlog_mode_set(PJDLOG_MODE_SYSLOG);
531
532		/* Write PID to a file. */
533		if (pidfile_write(pfh) < 0) {
534			pjdlog_errno(LOG_WARNING,
535			    "Unable to write PID to a file");
536		}
537	}
538
539	main_loop();
540
541	exit(0);
542}
543