xref: /freebsd/contrib/openbsm/bin/auditdistd/auditdistd.c (revision c7aa572cacdeca83c35c12a378909d0b659300a9)
1aa772005SRobert Watson /*-
2aa772005SRobert Watson  * Copyright (c) 2012 The FreeBSD Foundation
3aa772005SRobert Watson  * All rights reserved.
4aa772005SRobert Watson  *
5aa772005SRobert Watson  * This software was developed by Pawel Jakub Dawidek under sponsorship from
6aa772005SRobert Watson  * the FreeBSD Foundation.
7aa772005SRobert Watson  *
8aa772005SRobert Watson  * Redistribution and use in source and binary forms, with or without
9aa772005SRobert Watson  * modification, are permitted provided that the following conditions
10aa772005SRobert Watson  * are met:
11aa772005SRobert Watson  * 1. Redistributions of source code must retain the above copyright
12aa772005SRobert Watson  *    notice, this list of conditions and the following disclaimer.
13aa772005SRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
14aa772005SRobert Watson  *    notice, this list of conditions and the following disclaimer in the
15aa772005SRobert Watson  *    documentation and/or other materials provided with the distribution.
16aa772005SRobert Watson  *
17aa772005SRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18aa772005SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19aa772005SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20aa772005SRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21aa772005SRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22aa772005SRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23aa772005SRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24aa772005SRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25aa772005SRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26aa772005SRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27aa772005SRobert Watson  * SUCH DAMAGE.
28aa772005SRobert Watson  */
29aa772005SRobert Watson 
30aa772005SRobert Watson #include <config/config.h>
31aa772005SRobert Watson 
32aa772005SRobert Watson #include <sys/param.h>
33aa772005SRobert Watson #if defined(HAVE_SYS_ENDIAN_H) && defined(HAVE_BSWAP)
34aa772005SRobert Watson #include <sys/endian.h>
35aa772005SRobert Watson #else /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
36aa772005SRobert Watson #ifdef HAVE_MACHINE_ENDIAN_H
37aa772005SRobert Watson #include <machine/endian.h>
38aa772005SRobert Watson #else /* !HAVE_MACHINE_ENDIAN_H */
39aa772005SRobert Watson #ifdef HAVE_ENDIAN_H
40aa772005SRobert Watson #include <endian.h>
41aa772005SRobert Watson #else /* !HAVE_ENDIAN_H */
42aa772005SRobert Watson #error "No supported endian.h"
43aa772005SRobert Watson #endif /* !HAVE_ENDIAN_H */
44aa772005SRobert Watson #endif /* !HAVE_MACHINE_ENDIAN_H */
45aa772005SRobert Watson #include <compat/endian.h>
46aa772005SRobert Watson #endif /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
47aa772005SRobert Watson #include <sys/queue.h>
48aa772005SRobert Watson #include <sys/wait.h>
49aa772005SRobert Watson 
50aa772005SRobert Watson #include <ctype.h>
51aa772005SRobert Watson #include <err.h>
52aa772005SRobert Watson #include <errno.h>
53aa772005SRobert Watson #include <fcntl.h>
54aa772005SRobert Watson #ifdef HAVE_LIBUTIL_H
55aa772005SRobert Watson #include <libutil.h>
56aa772005SRobert Watson #endif
57aa772005SRobert Watson #include <signal.h>
58aa772005SRobert Watson #include <stdio.h>
59aa772005SRobert Watson #include <stdlib.h>
60aa772005SRobert Watson #include <string.h>
61aa772005SRobert Watson #include <strings.h>
62aa772005SRobert Watson #include <unistd.h>
63aa772005SRobert Watson 
64aa772005SRobert Watson #include <openssl/hmac.h>
65aa772005SRobert Watson 
66aa772005SRobert Watson #ifndef HAVE_PIDFILE_OPEN
67aa772005SRobert Watson #include <compat/pidfile.h>
68aa772005SRobert Watson #endif
69aa772005SRobert Watson #ifndef HAVE_STRLCPY
70aa772005SRobert Watson #include <compat/strlcpy.h>
71aa772005SRobert Watson #endif
72aa772005SRobert Watson #ifndef HAVE_SIGTIMEDWAIT
73aa772005SRobert Watson #include "sigtimedwait.h"
74aa772005SRobert Watson #endif
75aa772005SRobert Watson 
76aa772005SRobert Watson #include "auditdistd.h"
77aa772005SRobert Watson #include "pjdlog.h"
78aa772005SRobert Watson #include "proto.h"
79aa772005SRobert Watson #include "subr.h"
80aa772005SRobert Watson #include "synch.h"
81aa772005SRobert Watson 
82aa772005SRobert Watson /* Path to configuration file. */
83aa772005SRobert Watson const char *cfgpath = ADIST_CONFIG;
84aa772005SRobert Watson /* Auditdistd configuration. */
85aa772005SRobert Watson static struct adist_config *adcfg;
86aa772005SRobert Watson /* Was SIGINT or SIGTERM signal received? */
87aa772005SRobert Watson bool sigexit_received = false;
88aa772005SRobert Watson /* PID file handle. */
89aa772005SRobert Watson struct pidfh *pfh;
90aa772005SRobert Watson 
91aa772005SRobert Watson /* How often check for hooks running for too long. */
92aa772005SRobert Watson #define	SIGNALS_CHECK_INTERVAL	5
93aa772005SRobert Watson 
94aa772005SRobert Watson static void
usage(void)95aa772005SRobert Watson usage(void)
96aa772005SRobert Watson {
97aa772005SRobert Watson 
98aa772005SRobert Watson 	errx(EX_USAGE, "[-dFhl] [-c config] [-P pidfile]");
99aa772005SRobert Watson }
100aa772005SRobert Watson 
101aa772005SRobert Watson void
descriptors_cleanup(struct adist_host * adhost)102aa772005SRobert Watson descriptors_cleanup(struct adist_host *adhost)
103aa772005SRobert Watson {
104aa772005SRobert Watson 	struct adist_host *adh;
105aa772005SRobert Watson 	struct adist_listen *lst;
106aa772005SRobert Watson 
107aa772005SRobert Watson 	TAILQ_FOREACH(adh, &adcfg->adc_hosts, adh_next) {
108aa772005SRobert Watson 		if (adh == adhost)
109aa772005SRobert Watson 			continue;
110aa772005SRobert Watson 		if (adh->adh_remote != NULL) {
111aa772005SRobert Watson 			proto_close(adh->adh_remote);
112aa772005SRobert Watson 			adh->adh_remote = NULL;
113aa772005SRobert Watson 		}
114aa772005SRobert Watson 	}
115aa772005SRobert Watson 	TAILQ_FOREACH(lst, &adcfg->adc_listen, adl_next) {
116aa772005SRobert Watson 		if (lst->adl_conn != NULL)
117aa772005SRobert Watson 			proto_close(lst->adl_conn);
118aa772005SRobert Watson 	}
119aa772005SRobert Watson 	(void)pidfile_close(pfh);
120aa772005SRobert Watson 	pjdlog_fini();
121aa772005SRobert Watson }
122aa772005SRobert Watson 
123aa772005SRobert Watson static void
child_cleanup(struct adist_host * adhost)124aa772005SRobert Watson child_cleanup(struct adist_host *adhost)
125aa772005SRobert Watson {
126aa772005SRobert Watson 
127aa772005SRobert Watson 	if (adhost->adh_conn != NULL) {
128aa772005SRobert Watson 		PJDLOG_ASSERT(adhost->adh_role == ADIST_ROLE_SENDER);
129aa772005SRobert Watson 		proto_close(adhost->adh_conn);
130aa772005SRobert Watson 		adhost->adh_conn = NULL;
131aa772005SRobert Watson 	}
132aa772005SRobert Watson 	adhost->adh_worker_pid = 0;
133aa772005SRobert Watson }
134aa772005SRobert Watson 
135aa772005SRobert Watson static void
child_exit_log(const char * type,unsigned int pid,int status)136aa772005SRobert Watson child_exit_log(const char *type, unsigned int pid, int status)
137aa772005SRobert Watson {
138aa772005SRobert Watson 
139aa772005SRobert Watson 	if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
140aa772005SRobert Watson 		pjdlog_debug(1, "%s process exited gracefully (pid=%u).",
141aa772005SRobert Watson 		    type, pid);
142aa772005SRobert Watson 	} else if (WIFSIGNALED(status)) {
143aa772005SRobert Watson 		pjdlog_error("%s process killed (pid=%u, signal=%d).",
144aa772005SRobert Watson 		    type, pid, WTERMSIG(status));
145aa772005SRobert Watson 	} else {
146aa772005SRobert Watson 		pjdlog_error("%s process exited ungracefully (pid=%u, exitcode=%d).",
147aa772005SRobert Watson 		    type, pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1);
148aa772005SRobert Watson 	}
149aa772005SRobert Watson }
150aa772005SRobert Watson 
151aa772005SRobert Watson static void
child_exit(void)152aa772005SRobert Watson child_exit(void)
153aa772005SRobert Watson {
154aa772005SRobert Watson 	struct adist_host *adhost;
155aa772005SRobert Watson 	bool restart;
156aa772005SRobert Watson 	int status;
157aa772005SRobert Watson 	pid_t pid;
158aa772005SRobert Watson 
159aa772005SRobert Watson 	restart = false;
160aa772005SRobert Watson 	while ((pid = wait3(&status, WNOHANG, NULL)) > 0) {
161aa772005SRobert Watson 		/* Find host related to the process that just exited. */
162aa772005SRobert Watson 		TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
163aa772005SRobert Watson 			if (pid == adhost->adh_worker_pid)
164aa772005SRobert Watson 				break;
165aa772005SRobert Watson 		}
166aa772005SRobert Watson 		if (adhost == NULL) {
167aa772005SRobert Watson 			child_exit_log("Sandbox", pid, status);
168aa772005SRobert Watson 		} else {
169aa772005SRobert Watson 			if (adhost->adh_role == ADIST_ROLE_SENDER)
170aa772005SRobert Watson 				restart = true;
171aa772005SRobert Watson 			pjdlog_prefix_set("[%s] (%s) ", adhost->adh_name,
172aa772005SRobert Watson 			    role2str(adhost->adh_role));
173aa772005SRobert Watson 			child_exit_log("Worker", pid, status);
174aa772005SRobert Watson 			child_cleanup(adhost);
175aa772005SRobert Watson 			pjdlog_prefix_set("%s", "");
176aa772005SRobert Watson 		}
177aa772005SRobert Watson 	}
178aa772005SRobert Watson 	if (!restart)
179aa772005SRobert Watson 		return;
180aa772005SRobert Watson 	/* We have some sender processes to restart. */
181aa772005SRobert Watson 	sleep(1);
182aa772005SRobert Watson 	TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
183aa772005SRobert Watson 		if (adhost->adh_role != ADIST_ROLE_SENDER)
184aa772005SRobert Watson 			continue;
185aa772005SRobert Watson 		if (adhost->adh_worker_pid != 0)
186aa772005SRobert Watson 			continue;
187aa772005SRobert Watson 		pjdlog_prefix_set("[%s] (%s) ", adhost->adh_name,
188aa772005SRobert Watson 		    role2str(adhost->adh_role));
189aa772005SRobert Watson 		pjdlog_info("Restarting sender process.");
190aa772005SRobert Watson 		adist_sender(adcfg, adhost);
191aa772005SRobert Watson 		pjdlog_prefix_set("%s", "");
192aa772005SRobert Watson 	}
193aa772005SRobert Watson }
194aa772005SRobert Watson 
195aa772005SRobert Watson /* TODO */
196aa772005SRobert Watson static void
adist_reload(void)197aa772005SRobert Watson adist_reload(void)
198aa772005SRobert Watson {
199aa772005SRobert Watson 
200aa772005SRobert Watson 	pjdlog_info("Reloading configuration is not yet implemented.");
201aa772005SRobert Watson }
202aa772005SRobert Watson 
203aa772005SRobert Watson static void
terminate_workers(void)204aa772005SRobert Watson terminate_workers(void)
205aa772005SRobert Watson {
206aa772005SRobert Watson 	struct adist_host *adhost;
207aa772005SRobert Watson 
208aa772005SRobert Watson 	pjdlog_info("Termination signal received, exiting.");
209aa772005SRobert Watson 	TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
210aa772005SRobert Watson 		if (adhost->adh_worker_pid == 0)
211aa772005SRobert Watson 			continue;
212aa772005SRobert Watson 		pjdlog_info("Terminating worker process (adhost=%s, role=%s, pid=%u).",
213aa772005SRobert Watson 		    adhost->adh_name, role2str(adhost->adh_role),
214aa772005SRobert Watson 		    adhost->adh_worker_pid);
215aa772005SRobert Watson 		if (kill(adhost->adh_worker_pid, SIGTERM) == 0)
216aa772005SRobert Watson 			continue;
217aa772005SRobert Watson 		pjdlog_errno(LOG_WARNING,
218aa772005SRobert Watson 		    "Unable to send signal to worker process (adhost=%s, role=%s, pid=%u).",
219aa772005SRobert Watson 		    adhost->adh_name, role2str(adhost->adh_role),
220aa772005SRobert Watson 		    adhost->adh_worker_pid);
221aa772005SRobert Watson 	}
222aa772005SRobert Watson }
223aa772005SRobert Watson 
224aa772005SRobert Watson static void
listen_accept(struct adist_listen * lst)225aa772005SRobert Watson listen_accept(struct adist_listen *lst)
226aa772005SRobert Watson {
227aa772005SRobert Watson 	unsigned char rnd[32], hash[32], resp[32];
228aa772005SRobert Watson 	struct adist_host *adhost;
229aa772005SRobert Watson 	struct proto_conn *conn;
230aa772005SRobert Watson 	char adname[ADIST_HOSTSIZE];
231aa772005SRobert Watson 	char laddr[256], raddr[256];
232aa772005SRobert Watson 	char welcome[8];
233aa772005SRobert Watson 	int status, version;
234aa772005SRobert Watson 	pid_t pid;
235aa772005SRobert Watson 
236aa772005SRobert Watson 	proto_local_address(lst->adl_conn, laddr, sizeof(laddr));
237aa772005SRobert Watson 	pjdlog_debug(1, "Accepting connection to %s.", laddr);
238aa772005SRobert Watson 
239aa772005SRobert Watson 	if (proto_accept(lst->adl_conn, &conn) == -1) {
240aa772005SRobert Watson 		pjdlog_errno(LOG_ERR, "Unable to accept connection to %s",
241aa772005SRobert Watson 		    laddr);
242aa772005SRobert Watson 		return;
243aa772005SRobert Watson 	}
244aa772005SRobert Watson 
245aa772005SRobert Watson 	proto_local_address(conn, laddr, sizeof(laddr));
246aa772005SRobert Watson 	proto_remote_address(conn, raddr, sizeof(raddr));
247aa772005SRobert Watson 	pjdlog_info("Connection from %s to %s.", raddr, laddr);
248aa772005SRobert Watson 
249aa772005SRobert Watson 	/* Error in setting timeout is not critical, but why should it fail? */
250aa772005SRobert Watson 	if (proto_timeout(conn, ADIST_TIMEOUT) < 0)
251aa772005SRobert Watson 		pjdlog_errno(LOG_WARNING, "Unable to set connection timeout");
252aa772005SRobert Watson 
253aa772005SRobert Watson 	/*
254aa772005SRobert Watson 	 * Before receiving any data see if remote host is known.
255aa772005SRobert Watson 	 */
256aa772005SRobert Watson 	TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
257aa772005SRobert Watson 		if (adhost->adh_role != ADIST_ROLE_RECEIVER)
258aa772005SRobert Watson 			continue;
259aa772005SRobert Watson 		if (!proto_address_match(conn, adhost->adh_remoteaddr))
260aa772005SRobert Watson 			continue;
261aa772005SRobert Watson 		break;
262aa772005SRobert Watson 	}
263aa772005SRobert Watson 	if (adhost == NULL) {
264aa772005SRobert Watson 		pjdlog_error("Client %s is not known.", raddr);
265aa772005SRobert Watson 		goto close;
266aa772005SRobert Watson 	}
267aa772005SRobert Watson 	/* Ok, remote host is known. */
268aa772005SRobert Watson 
269aa772005SRobert Watson 	/* Exchange welcome message, which include version number. */
270aa772005SRobert Watson 	bzero(welcome, sizeof(welcome));
271aa772005SRobert Watson 	if (proto_recv(conn, welcome, sizeof(welcome)) == -1) {
272aa772005SRobert Watson 		pjdlog_errno(LOG_WARNING,
273aa772005SRobert Watson 		    "Unable to receive welcome message from %s",
274aa772005SRobert Watson 		    adhost->adh_remoteaddr);
275aa772005SRobert Watson 		goto close;
276aa772005SRobert Watson 	}
277aa772005SRobert Watson 	if (strncmp(welcome, "ADIST", 5) != 0 || !isdigit(welcome[5]) ||
278aa772005SRobert Watson 	    !isdigit(welcome[6]) || welcome[7] != '\0') {
279aa772005SRobert Watson 		pjdlog_warning("Invalid welcome message from %s.",
280aa772005SRobert Watson 		    adhost->adh_remoteaddr);
281aa772005SRobert Watson 		goto close;
282aa772005SRobert Watson 	}
283aa772005SRobert Watson 
284aa772005SRobert Watson 	version = MIN(ADIST_VERSION, atoi(welcome + 5));
285aa772005SRobert Watson 
286aa772005SRobert Watson 	(void)snprintf(welcome, sizeof(welcome), "ADIST%02d", version);
287aa772005SRobert Watson 	if (proto_send(conn, welcome, sizeof(welcome)) == -1) {
288aa772005SRobert Watson 		pjdlog_errno(LOG_WARNING,
289aa772005SRobert Watson 		    "Unable to send welcome message to %s",
290aa772005SRobert Watson 		    adhost->adh_remoteaddr);
291aa772005SRobert Watson 		goto close;
292aa772005SRobert Watson 	}
293aa772005SRobert Watson 
294aa772005SRobert Watson 	if (proto_recv(conn, adname, sizeof(adhost->adh_name)) < 0) {
295aa772005SRobert Watson 		pjdlog_errno(LOG_ERR, "Unable to receive hostname from %s",
296aa772005SRobert Watson 		    raddr);
297aa772005SRobert Watson 		goto close;
298aa772005SRobert Watson 	}
299aa772005SRobert Watson 
300aa772005SRobert Watson 	/* Find host now that we have hostname. */
301aa772005SRobert Watson 	TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
302aa772005SRobert Watson 		if (adhost->adh_role != ADIST_ROLE_RECEIVER)
303aa772005SRobert Watson 			continue;
304aa772005SRobert Watson 		if (!proto_address_match(conn, adhost->adh_remoteaddr))
305aa772005SRobert Watson 			continue;
306aa772005SRobert Watson 		if (strcmp(adhost->adh_name, adname) != 0)
307aa772005SRobert Watson 			continue;
308aa772005SRobert Watson 		break;
309aa772005SRobert Watson 	}
310aa772005SRobert Watson 	if (adhost == NULL) {
311aa772005SRobert Watson 		pjdlog_error("No configuration for host %s from address %s.",
312aa772005SRobert Watson 		    adname, raddr);
313aa772005SRobert Watson 		goto close;
314aa772005SRobert Watson 	}
315aa772005SRobert Watson 
316aa772005SRobert Watson 	adhost->adh_version = version;
317aa772005SRobert Watson 	pjdlog_debug(1, "Version %d negotiated with %s.", adhost->adh_version,
318aa772005SRobert Watson 	    adhost->adh_remoteaddr);
319aa772005SRobert Watson 
320aa772005SRobert Watson 	/* Now that we know host name setup log prefix. */
321aa772005SRobert Watson 	pjdlog_prefix_set("[%s] (%s) ", adhost->adh_name,
322aa772005SRobert Watson 	    role2str(adhost->adh_role));
323aa772005SRobert Watson 
324aa772005SRobert Watson 	if (adist_random(rnd, sizeof(rnd)) == -1) {
325aa772005SRobert Watson 		pjdlog_error("Unable to generate challenge.");
326aa772005SRobert Watson 		goto close;
327aa772005SRobert Watson 	}
328aa772005SRobert Watson 	pjdlog_debug(1, "Challenge generated.");
329aa772005SRobert Watson 
330aa772005SRobert Watson 	if (proto_send(conn, rnd, sizeof(rnd)) == -1) {
331aa772005SRobert Watson 		pjdlog_errno(LOG_ERR, "Unable to send challenge to %s",
332aa772005SRobert Watson 		    adhost->adh_remoteaddr);
333aa772005SRobert Watson 		goto close;
334aa772005SRobert Watson 	}
335aa772005SRobert Watson 	pjdlog_debug(1, "Challenge sent.");
336aa772005SRobert Watson 
337aa772005SRobert Watson 	if (proto_recv(conn, resp, sizeof(resp)) == -1) {
338aa772005SRobert Watson 		pjdlog_errno(LOG_ERR, "Unable to receive response from %s",
339aa772005SRobert Watson 		    adhost->adh_remoteaddr);
340aa772005SRobert Watson 		goto close;
341aa772005SRobert Watson 	}
342aa772005SRobert Watson 	pjdlog_debug(1, "Response received.");
343aa772005SRobert Watson 
344aa772005SRobert Watson 	if (HMAC(EVP_sha256(), adhost->adh_password,
345aa772005SRobert Watson 	    (int)strlen(adhost->adh_password), rnd, (int)sizeof(rnd), hash,
346aa772005SRobert Watson 	    NULL) == NULL) {
347aa772005SRobert Watson 		pjdlog_error("Unable to generate hash.");
348aa772005SRobert Watson 		goto close;
349aa772005SRobert Watson 	}
350aa772005SRobert Watson 	pjdlog_debug(1, "Hash generated.");
351aa772005SRobert Watson 
352aa772005SRobert Watson 	if (memcmp(resp, hash, sizeof(hash)) != 0) {
353aa772005SRobert Watson 		pjdlog_error("Invalid response from %s (wrong password?).",
354aa772005SRobert Watson 		    adhost->adh_remoteaddr);
355aa772005SRobert Watson 		goto close;
356aa772005SRobert Watson 	}
357aa772005SRobert Watson 	pjdlog_info("Sender authenticated.");
358aa772005SRobert Watson 
359aa772005SRobert Watson 	if (proto_recv(conn, rnd, sizeof(rnd)) == -1) {
360aa772005SRobert Watson 		pjdlog_errno(LOG_ERR, "Unable to receive challenge from %s",
361aa772005SRobert Watson 		    adhost->adh_remoteaddr);
362aa772005SRobert Watson 		goto close;
363aa772005SRobert Watson 	}
364aa772005SRobert Watson 	pjdlog_debug(1, "Challenge received.");
365aa772005SRobert Watson 
366aa772005SRobert Watson 	if (HMAC(EVP_sha256(), adhost->adh_password,
367aa772005SRobert Watson 	    (int)strlen(adhost->adh_password), rnd, (int)sizeof(rnd), hash,
368aa772005SRobert Watson 	    NULL) == NULL) {
369aa772005SRobert Watson 		pjdlog_error("Unable to generate response.");
370aa772005SRobert Watson 		goto close;
371aa772005SRobert Watson 	}
372aa772005SRobert Watson 	pjdlog_debug(1, "Response generated.");
373aa772005SRobert Watson 
374aa772005SRobert Watson 	if (proto_send(conn, hash, sizeof(hash)) == -1) {
375aa772005SRobert Watson 		pjdlog_errno(LOG_ERR, "Unable to send response to %s",
376aa772005SRobert Watson 		    adhost->adh_remoteaddr);
377aa772005SRobert Watson 		goto close;
378aa772005SRobert Watson 	}
379aa772005SRobert Watson 	pjdlog_debug(1, "Response sent.");
380aa772005SRobert Watson 
381aa772005SRobert Watson 	if (adhost->adh_worker_pid != 0) {
382aa772005SRobert Watson 		pjdlog_debug(1,
383aa772005SRobert Watson 		    "Receiver process exists (pid=%u), stopping it.",
384aa772005SRobert Watson 		    (unsigned int)adhost->adh_worker_pid);
385aa772005SRobert Watson 		/* Stop child process. */
386aa772005SRobert Watson 		if (kill(adhost->adh_worker_pid, SIGINT) == -1) {
387aa772005SRobert Watson 			pjdlog_errno(LOG_ERR,
388aa772005SRobert Watson 			    "Unable to stop worker process (pid=%u)",
389aa772005SRobert Watson 			    (unsigned int)adhost->adh_worker_pid);
390aa772005SRobert Watson 			/*
391aa772005SRobert Watson 			 * Other than logging the problem we
392aa772005SRobert Watson 			 * ignore it - nothing smart to do.
393aa772005SRobert Watson 			 */
394aa772005SRobert Watson 		}
395aa772005SRobert Watson 		/* Wait for it to exit. */
396aa772005SRobert Watson 		else if ((pid = waitpid(adhost->adh_worker_pid,
397aa772005SRobert Watson 		    &status, 0)) != adhost->adh_worker_pid) {
398aa772005SRobert Watson 			/* We can only log the problem. */
399aa772005SRobert Watson 			pjdlog_errno(LOG_ERR,
400aa772005SRobert Watson 			    "Waiting for worker process (pid=%u) failed",
401aa772005SRobert Watson 			    (unsigned int)adhost->adh_worker_pid);
402aa772005SRobert Watson 		} else {
403aa772005SRobert Watson 			child_exit_log("Worker", adhost->adh_worker_pid,
404aa772005SRobert Watson 			    status);
405aa772005SRobert Watson 		}
406aa772005SRobert Watson 		child_cleanup(adhost);
407aa772005SRobert Watson 	}
408aa772005SRobert Watson 
409aa772005SRobert Watson 	adhost->adh_remote = conn;
410aa772005SRobert Watson 	adist_receiver(adcfg, adhost);
411aa772005SRobert Watson 
412aa772005SRobert Watson 	pjdlog_prefix_set("%s", "");
413aa772005SRobert Watson 	return;
414aa772005SRobert Watson close:
415aa772005SRobert Watson 	proto_close(conn);
416aa772005SRobert Watson 	pjdlog_prefix_set("%s", "");
417aa772005SRobert Watson }
418aa772005SRobert Watson 
419aa772005SRobert Watson static void
connection_migrate(struct adist_host * adhost)420aa772005SRobert Watson connection_migrate(struct adist_host *adhost)
421aa772005SRobert Watson {
422aa772005SRobert Watson 	struct proto_conn *conn;
423aa772005SRobert Watson 	int16_t val = 0;
424aa772005SRobert Watson 
425aa772005SRobert Watson 	pjdlog_prefix_set("[%s] (%s) ", adhost->adh_name,
426aa772005SRobert Watson 	    role2str(adhost->adh_role));
427aa772005SRobert Watson 
428aa772005SRobert Watson 	PJDLOG_ASSERT(adhost->adh_role == ADIST_ROLE_SENDER);
429aa772005SRobert Watson 
430aa772005SRobert Watson 	if (proto_recv(adhost->adh_conn, &val, sizeof(val)) < 0) {
431aa772005SRobert Watson 		pjdlog_errno(LOG_WARNING,
432aa772005SRobert Watson 		    "Unable to receive connection command");
433aa772005SRobert Watson 		return;
434aa772005SRobert Watson 	}
435aa772005SRobert Watson 	if (proto_set("tls:fingerprint", adhost->adh_fingerprint) == -1) {
436aa772005SRobert Watson 		val = errno;
437aa772005SRobert Watson 		pjdlog_errno(LOG_WARNING, "Unable to set fingerprint");
438aa772005SRobert Watson 		goto out;
439aa772005SRobert Watson 	}
440aa772005SRobert Watson 	if (proto_connect(adhost->adh_localaddr[0] != '\0' ?
441aa772005SRobert Watson 	    adhost->adh_localaddr : NULL,
442aa772005SRobert Watson 	    adhost->adh_remoteaddr, -1, &conn) < 0) {
443aa772005SRobert Watson 		val = errno;
444aa772005SRobert Watson 		pjdlog_errno(LOG_WARNING, "Unable to connect to %s",
445aa772005SRobert Watson 		    adhost->adh_remoteaddr);
446aa772005SRobert Watson 		goto out;
447aa772005SRobert Watson 	}
448aa772005SRobert Watson 	val = 0;
449aa772005SRobert Watson out:
450aa772005SRobert Watson 	if (proto_send(adhost->adh_conn, &val, sizeof(val)) < 0) {
451aa772005SRobert Watson 		pjdlog_errno(LOG_WARNING,
452aa772005SRobert Watson 		    "Unable to send reply to connection request");
453aa772005SRobert Watson 	}
454aa772005SRobert Watson 	if (val == 0 && proto_connection_send(adhost->adh_conn, conn) < 0)
455aa772005SRobert Watson 		pjdlog_errno(LOG_WARNING, "Unable to send connection");
456aa772005SRobert Watson 
457aa772005SRobert Watson 	pjdlog_prefix_set("%s", "");
458aa772005SRobert Watson }
459aa772005SRobert Watson 
460aa772005SRobert Watson static void
check_signals(void)461aa772005SRobert Watson check_signals(void)
462aa772005SRobert Watson {
463aa772005SRobert Watson 	struct timespec sigtimeout;
464aa772005SRobert Watson 	sigset_t mask;
465aa772005SRobert Watson 	int signo;
466aa772005SRobert Watson 
467aa772005SRobert Watson 	sigtimeout.tv_sec = 0;
468aa772005SRobert Watson 	sigtimeout.tv_nsec = 0;
469aa772005SRobert Watson 
470aa772005SRobert Watson 	PJDLOG_VERIFY(sigemptyset(&mask) == 0);
471aa772005SRobert Watson 	PJDLOG_VERIFY(sigaddset(&mask, SIGHUP) == 0);
472aa772005SRobert Watson 	PJDLOG_VERIFY(sigaddset(&mask, SIGINT) == 0);
473aa772005SRobert Watson 	PJDLOG_VERIFY(sigaddset(&mask, SIGTERM) == 0);
474aa772005SRobert Watson 	PJDLOG_VERIFY(sigaddset(&mask, SIGCHLD) == 0);
475aa772005SRobert Watson 
476aa772005SRobert Watson 	while ((signo = sigtimedwait(&mask, NULL, &sigtimeout)) != -1) {
477aa772005SRobert Watson 		switch (signo) {
478aa772005SRobert Watson 		case SIGINT:
479aa772005SRobert Watson 		case SIGTERM:
480aa772005SRobert Watson 			sigexit_received = true;
481aa772005SRobert Watson 			terminate_workers();
482aa772005SRobert Watson 			exit(EX_OK);
483aa772005SRobert Watson 			break;
484aa772005SRobert Watson 		case SIGCHLD:
485aa772005SRobert Watson 			child_exit();
486aa772005SRobert Watson 			break;
487aa772005SRobert Watson 		case SIGHUP:
488aa772005SRobert Watson 			adist_reload();
489aa772005SRobert Watson 			break;
490aa772005SRobert Watson 		default:
491aa772005SRobert Watson 			PJDLOG_ABORT("Unexpected signal (%d).", signo);
492aa772005SRobert Watson 		}
493aa772005SRobert Watson 	}
494aa772005SRobert Watson }
495aa772005SRobert Watson 
496aa772005SRobert Watson static void
main_loop(void)497aa772005SRobert Watson main_loop(void)
498aa772005SRobert Watson {
499aa772005SRobert Watson 	struct adist_host *adhost;
500aa772005SRobert Watson 	struct adist_listen *lst;
501aa772005SRobert Watson 	struct timeval seltimeout;
502aa772005SRobert Watson 	int fd, maxfd, ret;
503aa772005SRobert Watson 	fd_set rfds;
504aa772005SRobert Watson 
505aa772005SRobert Watson 	seltimeout.tv_sec = SIGNALS_CHECK_INTERVAL;
506aa772005SRobert Watson 	seltimeout.tv_usec = 0;
507aa772005SRobert Watson 
508aa772005SRobert Watson 	pjdlog_info("Started successfully.");
509aa772005SRobert Watson 
510aa772005SRobert Watson 	for (;;) {
511aa772005SRobert Watson 		check_signals();
512aa772005SRobert Watson 
513aa772005SRobert Watson 		/* Setup descriptors for select(2). */
514aa772005SRobert Watson 		FD_ZERO(&rfds);
515aa772005SRobert Watson 		maxfd = -1;
516aa772005SRobert Watson 		TAILQ_FOREACH(lst, &adcfg->adc_listen, adl_next) {
517aa772005SRobert Watson 			if (lst->adl_conn == NULL)
518aa772005SRobert Watson 				continue;
519aa772005SRobert Watson 			fd = proto_descriptor(lst->adl_conn);
520aa772005SRobert Watson 			PJDLOG_ASSERT(fd >= 0);
521aa772005SRobert Watson 			FD_SET(fd, &rfds);
522aa772005SRobert Watson 			maxfd = fd > maxfd ? fd : maxfd;
523aa772005SRobert Watson 		}
524aa772005SRobert Watson 		TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
525aa772005SRobert Watson 			if (adhost->adh_role == ADIST_ROLE_SENDER) {
526*3008333dSChristian S.J. Peron 				/* Only sender workers ask for connections. */
527aa772005SRobert Watson 				PJDLOG_ASSERT(adhost->adh_conn != NULL);
528aa772005SRobert Watson 				fd = proto_descriptor(adhost->adh_conn);
529aa772005SRobert Watson 				PJDLOG_ASSERT(fd >= 0);
530aa772005SRobert Watson 				FD_SET(fd, &rfds);
531aa772005SRobert Watson 				maxfd = fd > maxfd ? fd : maxfd;
532aa772005SRobert Watson 			} else {
533aa772005SRobert Watson 				PJDLOG_ASSERT(adhost->adh_conn == NULL);
534aa772005SRobert Watson 			}
535aa772005SRobert Watson 		}
536aa772005SRobert Watson 
537aa772005SRobert Watson 		PJDLOG_ASSERT(maxfd + 1 <= (int)FD_SETSIZE);
538aa772005SRobert Watson 		ret = select(maxfd + 1, &rfds, NULL, NULL, &seltimeout);
539aa772005SRobert Watson 		if (ret == 0) {
540aa772005SRobert Watson 			/*
541aa772005SRobert Watson 			 * select(2) timed out, so there should be no
542aa772005SRobert Watson 			 * descriptors to check.
543aa772005SRobert Watson 			 */
544aa772005SRobert Watson 			continue;
545aa772005SRobert Watson 		} else if (ret == -1) {
546aa772005SRobert Watson 			if (errno == EINTR)
547aa772005SRobert Watson 				continue;
548aa772005SRobert Watson 			KEEP_ERRNO((void)pidfile_remove(pfh));
549aa772005SRobert Watson 			pjdlog_exit(EX_OSERR, "select() failed");
550aa772005SRobert Watson 		}
551aa772005SRobert Watson 		PJDLOG_ASSERT(ret > 0);
552aa772005SRobert Watson 
553aa772005SRobert Watson 		/*
554aa772005SRobert Watson 		 * Check for signals before we do anything to update our
555aa772005SRobert Watson 		 * info about terminated workers in the meantime.
556aa772005SRobert Watson 		 */
557aa772005SRobert Watson 		check_signals();
558aa772005SRobert Watson 
559aa772005SRobert Watson 		TAILQ_FOREACH(lst, &adcfg->adc_listen, adl_next) {
560aa772005SRobert Watson 			if (lst->adl_conn == NULL)
561aa772005SRobert Watson 				continue;
562aa772005SRobert Watson 			if (FD_ISSET(proto_descriptor(lst->adl_conn), &rfds))
563aa772005SRobert Watson 				listen_accept(lst);
564aa772005SRobert Watson 		}
565aa772005SRobert Watson 		TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
566aa772005SRobert Watson 			if (adhost->adh_role == ADIST_ROLE_SENDER) {
567aa772005SRobert Watson 				PJDLOG_ASSERT(adhost->adh_conn != NULL);
568aa772005SRobert Watson 				if (FD_ISSET(proto_descriptor(adhost->adh_conn),
569aa772005SRobert Watson 				    &rfds)) {
570aa772005SRobert Watson 					connection_migrate(adhost);
571aa772005SRobert Watson 				}
572aa772005SRobert Watson 			} else {
573aa772005SRobert Watson 				PJDLOG_ASSERT(adhost->adh_conn == NULL);
574aa772005SRobert Watson 			}
575aa772005SRobert Watson 		}
576aa772005SRobert Watson 	}
577aa772005SRobert Watson }
578aa772005SRobert Watson 
579aa772005SRobert Watson static void
adist_config_dump(struct adist_config * cfg)580aa772005SRobert Watson adist_config_dump(struct adist_config *cfg)
581aa772005SRobert Watson {
582aa772005SRobert Watson 	struct adist_host *adhost;
583aa772005SRobert Watson 	struct adist_listen *lst;
584aa772005SRobert Watson 
585aa772005SRobert Watson 	pjdlog_debug(2, "Configuration:");
586aa772005SRobert Watson 	pjdlog_debug(2, "  Global:");
587aa772005SRobert Watson 	pjdlog_debug(2, "    pidfile: %s", cfg->adc_pidfile);
588aa772005SRobert Watson 	pjdlog_debug(2, "    timeout: %d", cfg->adc_timeout);
589aa772005SRobert Watson 	if (TAILQ_EMPTY(&cfg->adc_listen)) {
590aa772005SRobert Watson 		pjdlog_debug(2, "  Sender only, not listening.");
591aa772005SRobert Watson 	} else {
592aa772005SRobert Watson 		pjdlog_debug(2, "  Listening on:");
593aa772005SRobert Watson 		TAILQ_FOREACH(lst, &cfg->adc_listen, adl_next) {
594aa772005SRobert Watson 			pjdlog_debug(2, "    listen: %s", lst->adl_addr);
595aa772005SRobert Watson 			pjdlog_debug(2, "    conn: %p", lst->adl_conn);
596aa772005SRobert Watson 		}
597aa772005SRobert Watson 	}
598aa772005SRobert Watson 	pjdlog_debug(2, "  Hosts:");
599aa772005SRobert Watson 	TAILQ_FOREACH(adhost, &cfg->adc_hosts, adh_next) {
600aa772005SRobert Watson 		pjdlog_debug(2, "    name: %s", adhost->adh_name);
601aa772005SRobert Watson 		pjdlog_debug(2, "      role: %s", role2str(adhost->adh_role));
602aa772005SRobert Watson 		pjdlog_debug(2, "      version: %d", adhost->adh_version);
603aa772005SRobert Watson 		pjdlog_debug(2, "      localaddr: %s", adhost->adh_localaddr);
604aa772005SRobert Watson 		pjdlog_debug(2, "      remoteaddr: %s", adhost->adh_remoteaddr);
605aa772005SRobert Watson 		pjdlog_debug(2, "      remote: %p", adhost->adh_remote);
606aa772005SRobert Watson 		pjdlog_debug(2, "      directory: %s", adhost->adh_directory);
607aa772005SRobert Watson 		pjdlog_debug(2, "      compression: %d", adhost->adh_compression);
608aa772005SRobert Watson 		pjdlog_debug(2, "      checksum: %d", adhost->adh_checksum);
609aa772005SRobert Watson 		pjdlog_debug(2, "      pid: %ld", (long)adhost->adh_worker_pid);
610aa772005SRobert Watson 		pjdlog_debug(2, "      conn: %p", adhost->adh_conn);
611aa772005SRobert Watson 	}
612aa772005SRobert Watson }
613aa772005SRobert Watson 
614aa772005SRobert Watson static void
dummy_sighandler(int sig __unused)615aa772005SRobert Watson dummy_sighandler(int sig __unused)
616aa772005SRobert Watson {
617aa772005SRobert Watson 	/* Nothing to do. */
618aa772005SRobert Watson }
619aa772005SRobert Watson 
620aa772005SRobert Watson int
main(int argc,char * argv[])621aa772005SRobert Watson main(int argc, char *argv[])
622aa772005SRobert Watson {
623aa772005SRobert Watson 	struct adist_host *adhost;
624aa772005SRobert Watson 	struct adist_listen *lst;
625aa772005SRobert Watson 	const char *execpath, *pidfile;
626aa772005SRobert Watson 	bool foreground, launchd;
627aa772005SRobert Watson 	pid_t otherpid;
628aa772005SRobert Watson 	int debuglevel;
629aa772005SRobert Watson 	sigset_t mask;
630aa772005SRobert Watson 
631aa772005SRobert Watson 	execpath = argv[0];
632aa772005SRobert Watson 	if (execpath[0] != '/') {
633aa772005SRobert Watson 		errx(EX_USAGE,
634aa772005SRobert Watson 		    "auditdistd requires execution with an absolute path.");
635aa772005SRobert Watson 	}
636aa772005SRobert Watson 
637aa772005SRobert Watson 	/*
638aa772005SRobert Watson 	 * We are executed from proto to create sandbox.
639aa772005SRobert Watson 	 */
640aa772005SRobert Watson 	if (argc > 1 && strcmp(argv[1], "proto") == 0) {
641aa772005SRobert Watson 		argc -= 2;
642aa772005SRobert Watson 		argv += 2;
643aa772005SRobert Watson 		if (proto_exec(argc, argv) == -1)
644aa772005SRobert Watson 			err(EX_USAGE, "Unable to execute proto");
645aa772005SRobert Watson 	}
646aa772005SRobert Watson 
647aa772005SRobert Watson 	foreground = false;
648aa772005SRobert Watson 	debuglevel = 0;
649aa772005SRobert Watson 	launchd = false;
650aa772005SRobert Watson 	pidfile = NULL;
651aa772005SRobert Watson 
652aa772005SRobert Watson 	for (;;) {
653aa772005SRobert Watson 		int ch;
654aa772005SRobert Watson 
655aa772005SRobert Watson 		ch = getopt(argc, argv, "c:dFhlP:");
656aa772005SRobert Watson 		if (ch == -1)
657aa772005SRobert Watson 			break;
658aa772005SRobert Watson 		switch (ch) {
659aa772005SRobert Watson 		case 'c':
660aa772005SRobert Watson 			cfgpath = optarg;
661aa772005SRobert Watson 			break;
662aa772005SRobert Watson 		case 'd':
663aa772005SRobert Watson 			debuglevel++;
664aa772005SRobert Watson 			break;
665aa772005SRobert Watson 		case 'F':
666aa772005SRobert Watson 			foreground = true;
667aa772005SRobert Watson 			break;
668aa772005SRobert Watson 		case 'l':
669aa772005SRobert Watson 			launchd = true;
670aa772005SRobert Watson 			break;
671aa772005SRobert Watson 		case 'P':
672aa772005SRobert Watson 			pidfile = optarg;
673aa772005SRobert Watson 			break;
674aa772005SRobert Watson 		case 'h':
675aa772005SRobert Watson 		default:
676aa772005SRobert Watson 			usage();
677aa772005SRobert Watson 		}
678aa772005SRobert Watson 	}
679aa772005SRobert Watson 	argc -= optind;
680aa772005SRobert Watson 	argv += optind;
681aa772005SRobert Watson 
682aa772005SRobert Watson 	pjdlog_init(PJDLOG_MODE_STD);
683aa772005SRobert Watson 	pjdlog_debug_set(debuglevel);
684aa772005SRobert Watson 
685aa772005SRobert Watson 	if (proto_set("execpath", execpath) == -1)
686aa772005SRobert Watson 		pjdlog_exit(EX_TEMPFAIL, "Unable to set executable name");
687aa772005SRobert Watson 	if (proto_set("user", ADIST_USER) == -1)
688aa772005SRobert Watson 		pjdlog_exit(EX_TEMPFAIL, "Unable to set proto user");
689aa772005SRobert Watson 	if (proto_set("tcp:port", ADIST_TCP_PORT) == -1)
690aa772005SRobert Watson 		pjdlog_exit(EX_TEMPFAIL, "Unable to set default TCP port");
691aa772005SRobert Watson 
692aa772005SRobert Watson 	/*
693aa772005SRobert Watson 	 * When path to the configuration file is relative, obtain full path,
694aa772005SRobert Watson 	 * so we can always find the file, even after daemonizing and changing
695aa772005SRobert Watson 	 * working directory to /.
696aa772005SRobert Watson 	 */
697aa772005SRobert Watson 	if (cfgpath[0] != '/') {
698aa772005SRobert Watson 		const char *newcfgpath;
699aa772005SRobert Watson 
700aa772005SRobert Watson 		newcfgpath = realpath(cfgpath, NULL);
701aa772005SRobert Watson 		if (newcfgpath == NULL) {
702aa772005SRobert Watson 			pjdlog_exit(EX_CONFIG,
703aa772005SRobert Watson 			    "Unable to obtain full path of %s", cfgpath);
704aa772005SRobert Watson 		}
705aa772005SRobert Watson 		cfgpath = newcfgpath;
706aa772005SRobert Watson 	}
707aa772005SRobert Watson 
708aa772005SRobert Watson 	adcfg = yy_config_parse(cfgpath, true);
709aa772005SRobert Watson 	PJDLOG_ASSERT(adcfg != NULL);
710aa772005SRobert Watson 	adist_config_dump(adcfg);
711aa772005SRobert Watson 
712aa772005SRobert Watson 	if (proto_set("tls:certfile", adcfg->adc_certfile) == -1)
713aa772005SRobert Watson 		pjdlog_exit(EX_TEMPFAIL, "Unable to set certfile path");
714aa772005SRobert Watson 	if (proto_set("tls:keyfile", adcfg->adc_keyfile) == -1)
715aa772005SRobert Watson 		pjdlog_exit(EX_TEMPFAIL, "Unable to set keyfile path");
716aa772005SRobert Watson 
717aa772005SRobert Watson 	if (pidfile != NULL) {
718aa772005SRobert Watson 		if (strlcpy(adcfg->adc_pidfile, pidfile,
719aa772005SRobert Watson 		    sizeof(adcfg->adc_pidfile)) >=
720aa772005SRobert Watson 		    sizeof(adcfg->adc_pidfile)) {
721aa772005SRobert Watson 			pjdlog_exitx(EX_CONFIG, "Pidfile path is too long.");
722aa772005SRobert Watson 		}
723aa772005SRobert Watson 	}
724aa772005SRobert Watson 	if (foreground && pidfile == NULL) {
725aa772005SRobert Watson 		pfh = NULL;
726aa772005SRobert Watson 	} else {
727aa772005SRobert Watson 		pfh = pidfile_open(adcfg->adc_pidfile, 0600, &otherpid);
728aa772005SRobert Watson 		if (pfh == NULL) {
729aa772005SRobert Watson 			if (errno == EEXIST) {
730aa772005SRobert Watson 				pjdlog_exitx(EX_TEMPFAIL,
731aa772005SRobert Watson 				    "Another auditdistd is already running, pid: %jd.",
732aa772005SRobert Watson 				    (intmax_t)otherpid);
733aa772005SRobert Watson 			}
734aa772005SRobert Watson 			/*
735aa772005SRobert Watson 			 * If we cannot create pidfile from other reasons,
736aa772005SRobert Watson 			 * only warn.
737aa772005SRobert Watson 			 */
738aa772005SRobert Watson 			pjdlog_errno(LOG_WARNING,
739aa772005SRobert Watson 			    "Unable to open or create pidfile %s",
740aa772005SRobert Watson 			    adcfg->adc_pidfile);
741aa772005SRobert Watson 		}
742aa772005SRobert Watson 	}
743aa772005SRobert Watson 
744aa772005SRobert Watson 	/*
745aa772005SRobert Watson 	 * Restore default actions for interesting signals in case parent
746aa772005SRobert Watson 	 * process (like init(8)) decided to ignore some of them (like SIGHUP).
747aa772005SRobert Watson 	 */
748aa772005SRobert Watson 	PJDLOG_VERIFY(signal(SIGHUP, SIG_DFL) != SIG_ERR);
749aa772005SRobert Watson 	PJDLOG_VERIFY(signal(SIGINT, SIG_DFL) != SIG_ERR);
750aa772005SRobert Watson 	PJDLOG_VERIFY(signal(SIGTERM, SIG_DFL) != SIG_ERR);
751aa772005SRobert Watson 	/*
752aa772005SRobert Watson 	 * Because SIGCHLD is ignored by default, setup dummy handler for it,
753aa772005SRobert Watson 	 * so we can mask it.
754aa772005SRobert Watson 	 */
755aa772005SRobert Watson 	PJDLOG_VERIFY(signal(SIGCHLD, dummy_sighandler) != SIG_ERR);
756aa772005SRobert Watson 
757aa772005SRobert Watson 	PJDLOG_VERIFY(sigemptyset(&mask) == 0);
758aa772005SRobert Watson 	PJDLOG_VERIFY(sigaddset(&mask, SIGHUP) == 0);
759aa772005SRobert Watson 	PJDLOG_VERIFY(sigaddset(&mask, SIGINT) == 0);
760aa772005SRobert Watson 	PJDLOG_VERIFY(sigaddset(&mask, SIGTERM) == 0);
761aa772005SRobert Watson 	PJDLOG_VERIFY(sigaddset(&mask, SIGCHLD) == 0);
762aa772005SRobert Watson 	PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
763aa772005SRobert Watson 
764aa772005SRobert Watson 	/* Listen for remote connections. */
765aa772005SRobert Watson 	TAILQ_FOREACH(lst, &adcfg->adc_listen, adl_next) {
766aa772005SRobert Watson 		if (proto_server(lst->adl_addr, &lst->adl_conn) == -1) {
767aa772005SRobert Watson 			KEEP_ERRNO((void)pidfile_remove(pfh));
768aa772005SRobert Watson 			pjdlog_exit(EX_OSERR, "Unable to listen on address %s",
769aa772005SRobert Watson 			    lst->adl_addr);
770aa772005SRobert Watson 		}
771aa772005SRobert Watson 	}
772aa772005SRobert Watson 
773aa772005SRobert Watson 	if (!foreground) {
774aa772005SRobert Watson 		if (!launchd && daemon(0, 0) == -1) {
775aa772005SRobert Watson 			KEEP_ERRNO((void)pidfile_remove(pfh));
776aa772005SRobert Watson 			pjdlog_exit(EX_OSERR, "Unable to daemonize");
777aa772005SRobert Watson 		}
778aa772005SRobert Watson 
779aa772005SRobert Watson 		/* Start logging to syslog. */
780aa772005SRobert Watson 		pjdlog_mode_set(PJDLOG_MODE_SYSLOG);
781aa772005SRobert Watson 	}
782aa772005SRobert Watson 	if (pfh != NULL) {
783aa772005SRobert Watson 		/* Write PID to a file. */
784aa772005SRobert Watson 		if (pidfile_write(pfh) < 0) {
785aa772005SRobert Watson 			pjdlog_errno(LOG_WARNING,
786aa772005SRobert Watson 			    "Unable to write PID to a file");
787aa772005SRobert Watson 		}
788aa772005SRobert Watson 	}
789aa772005SRobert Watson 
790aa772005SRobert Watson 	TAILQ_FOREACH(adhost, &adcfg->adc_hosts, adh_next) {
791aa772005SRobert Watson 		if (adhost->adh_role == ADIST_ROLE_SENDER)
792aa772005SRobert Watson 			adist_sender(adcfg, adhost);
793aa772005SRobert Watson 	}
794aa772005SRobert Watson 
795aa772005SRobert Watson 	main_loop();
796aa772005SRobert Watson 
797aa772005SRobert Watson 	exit(0);
798aa772005SRobert Watson }
799