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