xref: /freebsd/contrib/openbsm/bin/auditdistd/receiver.c (revision 63d1fd5970ec814904aa0f4580b10a0d302d08b2)
1 /*-
2  * Copyright (c) 2012 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Pawel Jakub Dawidek under sponsorship from
6  * the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <config/config.h>
31 
32 #include <sys/param.h>
33 #if defined(HAVE_SYS_ENDIAN_H) && defined(HAVE_BSWAP)
34 #include <sys/endian.h>
35 #else /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
36 #ifdef HAVE_MACHINE_ENDIAN_H
37 #include <machine/endian.h>
38 #else /* !HAVE_MACHINE_ENDIAN_H */
39 #ifdef HAVE_ENDIAN_H
40 #include <endian.h>
41 #else /* !HAVE_ENDIAN_H */
42 #error "No supported endian.h"
43 #endif /* !HAVE_ENDIAN_H */
44 #endif /* !HAVE_MACHINE_ENDIAN_H */
45 #include <compat/endian.h>
46 #endif /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
47 #include <sys/queue.h>
48 #include <sys/stat.h>
49 #include <sys/time.h>
50 
51 #include <err.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #ifdef HAVE_LIBUTIL_H
55 #include <libutil.h>
56 #endif
57 #include <pthread.h>
58 #include <pwd.h>
59 #include <signal.h>
60 #include <stdint.h>
61 #include <stdio.h>
62 #include <string.h>
63 #include <sysexits.h>
64 #include <unistd.h>
65 
66 #ifndef HAVE_STRLCPY
67 #include <compat/strlcpy.h>
68 #endif
69 #ifndef HAVE_FSTATAT
70 #include "fstatat.h"
71 #endif
72 #ifndef HAVE_OPENAT
73 #include "openat.h"
74 #endif
75 #ifndef HAVE_RENAMEAT
76 #include "renameat.h"
77 #endif
78 
79 #include "auditdistd.h"
80 #include "pjdlog.h"
81 #include "proto.h"
82 #include "sandbox.h"
83 #include "subr.h"
84 #include "synch.h"
85 #include "trail.h"
86 
87 static struct adist_config *adcfg;
88 static struct adist_host *adhost;
89 
90 static TAILQ_HEAD(, adreq) adist_free_list;
91 static pthread_mutex_t adist_free_list_lock;
92 static pthread_cond_t adist_free_list_cond;
93 static TAILQ_HEAD(, adreq) adist_disk_list;
94 static pthread_mutex_t adist_disk_list_lock;
95 static pthread_cond_t adist_disk_list_cond;
96 static TAILQ_HEAD(, adreq) adist_send_list;
97 static pthread_mutex_t adist_send_list_lock;
98 static pthread_cond_t adist_send_list_cond;
99 
100 static void
101 adreq_clear(struct adreq *adreq)
102 {
103 
104 	adreq->adr_error = -1;
105 	adreq->adr_byteorder = ADIST_BYTEORDER_UNDEFINED;
106 	adreq->adr_cmd = ADIST_CMD_UNDEFINED;
107 	adreq->adr_seq = 0;
108 	adreq->adr_datasize = 0;
109 }
110 
111 static void
112 init_environment(void)
113 {
114 	struct adreq *adreq;
115 	unsigned int ii;
116 
117 	TAILQ_INIT(&adist_free_list);
118 	mtx_init(&adist_free_list_lock);
119 	cv_init(&adist_free_list_cond);
120 	TAILQ_INIT(&adist_disk_list);
121 	mtx_init(&adist_disk_list_lock);
122 	cv_init(&adist_disk_list_cond);
123 	TAILQ_INIT(&adist_send_list);
124 	mtx_init(&adist_send_list_lock);
125 	cv_init(&adist_send_list_cond);
126 
127 	for (ii = 0; ii < ADIST_QUEUE_SIZE; ii++) {
128 		adreq = malloc(sizeof(*adreq) + ADIST_BUF_SIZE);
129 		if (adreq == NULL) {
130 			pjdlog_exitx(EX_TEMPFAIL,
131 			    "Unable to allocate %zu bytes of memory for adreq object.",
132 			    sizeof(*adreq) + ADIST_BUF_SIZE);
133 		}
134 		adreq_clear(adreq);
135 		TAILQ_INSERT_TAIL(&adist_free_list, adreq, adr_next);
136 	}
137 }
138 
139 static void
140 adreq_decode_and_validate_header(struct adreq *adreq)
141 {
142 
143 	/* Byte-swap only is the sender is using different byte order. */
144 	if (adreq->adr_byteorder != ADIST_BYTEORDER) {
145 		adreq->adr_byteorder = ADIST_BYTEORDER;
146 		adreq->adr_seq = bswap64(adreq->adr_seq);
147 		adreq->adr_datasize = bswap32(adreq->adr_datasize);
148 	}
149 
150 	/* Validate packet header. */
151 
152 	if (adreq->adr_datasize > ADIST_BUF_SIZE) {
153 		pjdlog_exitx(EX_PROTOCOL, "Invalid datasize received (%ju).",
154 		    (uintmax_t)adreq->adr_datasize);
155 	}
156 
157 	switch (adreq->adr_cmd) {
158 	case ADIST_CMD_OPEN:
159 	case ADIST_CMD_APPEND:
160 	case ADIST_CMD_CLOSE:
161 		if (adreq->adr_datasize == 0) {
162 			pjdlog_exitx(EX_PROTOCOL,
163 			    "Invalid datasize received (%ju).",
164 			    (uintmax_t)adreq->adr_datasize);
165 		}
166 		break;
167 	case ADIST_CMD_KEEPALIVE:
168 	case ADIST_CMD_ERROR:
169 		if (adreq->adr_datasize > 0) {
170 			pjdlog_exitx(EX_PROTOCOL,
171 			    "Invalid datasize received (%ju).",
172 			    (uintmax_t)adreq->adr_datasize);
173 		}
174 		break;
175 	default:
176 		pjdlog_exitx(EX_PROTOCOL, "Invalid command received (%hhu).",
177 		    adreq->adr_cmd);
178 	}
179 }
180 
181 static void
182 adreq_validate_data(const struct adreq *adreq)
183 {
184 
185 	/* Validate packet data. */
186 
187 	switch (adreq->adr_cmd) {
188 	case ADIST_CMD_OPEN:
189 	case ADIST_CMD_CLOSE:
190 		/*
191 		 * File name must end up with '\0' and there must be no '\0'
192 		 * in the middle.
193 		 */
194 		if (adreq->adr_data[adreq->adr_datasize - 1] != '\0' ||
195 		    strchr(adreq->adr_data, '\0') !=
196 		    (const char *)adreq->adr_data + adreq->adr_datasize - 1) {
197 			pjdlog_exitx(EX_PROTOCOL,
198 			    "Invalid file name received.");
199 		}
200 		break;
201 	}
202 }
203 
204 /*
205  * Thread receives requests from the sender.
206  */
207 static void *
208 recv_thread(void *arg __unused)
209 {
210 	struct adreq *adreq;
211 
212 	for (;;) {
213 		pjdlog_debug(3, "recv: Taking free request.");
214 		QUEUE_TAKE(adreq, &adist_free_list, 0);
215 		pjdlog_debug(3, "recv: (%p) Got request.", adreq);
216 
217 		if (proto_recv(adhost->adh_remote, &adreq->adr_packet,
218 		    sizeof(adreq->adr_packet)) == -1) {
219 			pjdlog_exit(EX_TEMPFAIL,
220 			    "Unable to receive request header");
221 		}
222 		adreq_decode_and_validate_header(adreq);
223 
224 		switch (adreq->adr_cmd) {
225 		case ADIST_CMD_KEEPALIVE:
226 			adreq->adr_error = 0;
227 			adreq_log(LOG_DEBUG, 2, -1, adreq,
228 			    "recv: (%p) Got request header: ", adreq);
229 			pjdlog_debug(3,
230 			    "recv: (%p) Moving request to the send queue.",
231 			    adreq);
232 			QUEUE_INSERT(adreq, &adist_send_list);
233 			continue;
234 		case ADIST_CMD_ERROR:
235 			pjdlog_error("An error occured on the sender while reading \"%s/%s\".",
236 			    adhost->adh_directory, adhost->adh_trail_name);
237 			adreq_log(LOG_DEBUG, 2, ADIST_ERROR_READ, adreq,
238 			    "recv: (%p) Got request header: ", adreq);
239 			pjdlog_debug(3,
240 			    "recv: (%p) Moving request to the send queue.",
241 			    adreq);
242 			QUEUE_INSERT(adreq, &adist_disk_list);
243 			continue;
244 		case ADIST_CMD_OPEN:
245 		case ADIST_CMD_APPEND:
246 		case ADIST_CMD_CLOSE:
247 			if (proto_recv(adhost->adh_remote, adreq->adr_data,
248 			    adreq->adr_datasize) == -1) {
249 				pjdlog_exit(EX_TEMPFAIL,
250 				    "Unable to receive request data");
251 			}
252 			adreq_validate_data(adreq);
253 			adreq_log(LOG_DEBUG, 2, -1, adreq,
254 			    "recv: (%p) Got request header: ", adreq);
255 			pjdlog_debug(3,
256 			    "recv: (%p) Moving request to the disk queue.",
257 			    adreq);
258 			QUEUE_INSERT(adreq, &adist_disk_list);
259 			break;
260 		default:
261 			PJDLOG_ABORT("Invalid condition.");
262 		}
263 	}
264 	/* NOTREACHED */
265 	return (NULL);
266 }
267 
268 /*
269  * Function that opens trail file requested by the sender.
270  * If the file already exist, it has to be the most recent file and it can
271  * only be open for append.
272  * If the file doesn't already exist, it has to be "older" than all existing
273  * files.
274  */
275 static int
276 receiver_open(const char *filename)
277 {
278 	int fd;
279 
280 	/*
281 	 * Previous file should be closed by now. Sending OPEN request without
282 	 * sending CLOSE for the previous file is a sender bug.
283 	 */
284 	if (adhost->adh_trail_fd != -1) {
285 		pjdlog_error("Sender requested opening file \"%s\" without first closing \"%s\".",
286 		    filename, adhost->adh_trail_name);
287 		return (ADIST_ERROR_WRONG_ORDER);
288 	}
289 
290 	if (!trail_validate_name(filename, NULL)) {
291 		pjdlog_error("Sender wants to open file \"%s\", which has invalid name.",
292 		    filename);
293 		return (ADIST_ERROR_INVALID_NAME);
294 	}
295 
296 	switch (trail_name_compare(filename, adhost->adh_trail_name)) {
297 	case TRAIL_RENAMED:
298 		if (!trail_is_not_terminated(adhost->adh_trail_name)) {
299 			pjdlog_error("Terminated trail \"%s/%s\" was unterminated on the sender as \"%s/%s\"?",
300 			    adhost->adh_directory, adhost->adh_trail_name,
301 			    adhost->adh_directory, filename);
302 			return (ADIST_ERROR_INVALID_NAME);
303 		}
304 		if (renameat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
305 		    adhost->adh_trail_dirfd, filename) == -1) {
306 			pjdlog_errno(LOG_ERR,
307 			    "Unable to rename file \"%s/%s\" to \"%s/%s\"",
308 			    adhost->adh_directory, adhost->adh_trail_name,
309 			    adhost->adh_directory, filename);
310 			PJDLOG_ASSERT(errno > 0);
311 			return (ADIST_ERROR_RENAME);
312 		}
313 		pjdlog_debug(1, "Renamed file \"%s/%s\" to \"%s/%s\".",
314 		    adhost->adh_directory, adhost->adh_trail_name,
315 		    adhost->adh_directory, filename);
316 		/* FALLTHROUGH */
317 	case TRAIL_IDENTICAL:
318 		/* Opening existing file. */
319 		fd = openat(adhost->adh_trail_dirfd, filename,
320 		    O_WRONLY | O_APPEND | O_NOFOLLOW);
321 		if (fd == -1) {
322 			pjdlog_errno(LOG_ERR,
323 			    "Unable to open file \"%s/%s\" for append",
324 			    adhost->adh_directory, filename);
325 			PJDLOG_ASSERT(errno > 0);
326 			return (ADIST_ERROR_OPEN);
327 		}
328 		pjdlog_debug(1, "Opened file \"%s/%s\".",
329 		    adhost->adh_directory, filename);
330 		break;
331 	case TRAIL_NEWER:
332 		/* Opening new file. */
333 		fd = openat(adhost->adh_trail_dirfd, filename,
334 		    O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
335 		if (fd == -1) {
336 			pjdlog_errno(LOG_ERR,
337 			    "Unable to create file \"%s/%s\"",
338 			    adhost->adh_directory, filename);
339 			PJDLOG_ASSERT(errno > 0);
340 			return (ADIST_ERROR_CREATE);
341 		}
342 		pjdlog_debug(1, "Created file \"%s/%s\".",
343 		    adhost->adh_directory, filename);
344 		break;
345 	case TRAIL_OLDER:
346 		/* Trying to open old file. */
347 		pjdlog_error("Sender wants to open an old file \"%s\".", filename);
348 		return (ADIST_ERROR_OPEN_OLD);
349 	default:
350 		PJDLOG_ABORT("Unknown return value from trail_name_compare().");
351 	}
352 	PJDLOG_VERIFY(strlcpy(adhost->adh_trail_name, filename,
353 	    sizeof(adhost->adh_trail_name)) < sizeof(adhost->adh_trail_name));
354 	adhost->adh_trail_fd = fd;
355 	return (0);
356 }
357 
358 /*
359  * Function appends data to the trail file that is currently open.
360  */
361 static int
362 receiver_append(const unsigned char *data, size_t size)
363 {
364 	ssize_t done;
365 	size_t osize;
366 
367 	/* We should have opened trail file. */
368 	if (adhost->adh_trail_fd == -1) {
369 		pjdlog_error("Sender requested append without first opening file.");
370 		return (ADIST_ERROR_WRONG_ORDER);
371 	}
372 
373 	osize = size;
374 	while (size > 0) {
375 		done = write(adhost->adh_trail_fd, data, size);
376 		if (done == -1) {
377 			if (errno == EINTR)
378 				continue;
379 			pjdlog_errno(LOG_ERR, "Write to \"%s/%s\" failed",
380 			    adhost->adh_directory, adhost->adh_trail_name);
381 			PJDLOG_ASSERT(errno > 0);
382 			return (ADIST_ERROR_WRITE);
383 		}
384 		pjdlog_debug(3, "Wrote %zd bytes into \"%s/%s\".", done,
385 		    adhost->adh_directory, adhost->adh_trail_name);
386 		size -= done;
387 	}
388 	pjdlog_debug(2, "Appended %zu bytes to file \"%s/%s\".",
389 	    osize, adhost->adh_directory, adhost->adh_trail_name);
390 	return (0);
391 }
392 
393 static int
394 receiver_close(const char *filename)
395 {
396 
397 	/* We should have opened trail file. */
398 	if (adhost->adh_trail_fd == -1) {
399 		pjdlog_error("Sender requested closing file without first opening it.");
400 		return (ADIST_ERROR_WRONG_ORDER);
401 	}
402 
403 	/* Validate if we can do the rename. */
404 	if (!trail_validate_name(adhost->adh_trail_name, filename)) {
405 		pjdlog_error("Sender wants to close file \"%s\" using name \"%s\".",
406 		    adhost->adh_trail_name, filename);
407 		return (ADIST_ERROR_INVALID_NAME);
408 	}
409 
410 	PJDLOG_VERIFY(close(adhost->adh_trail_fd) == 0);
411 	adhost->adh_trail_fd = -1;
412 
413 	pjdlog_debug(1, "Closed file \"%s/%s\".", adhost->adh_directory,
414 	    adhost->adh_trail_name);
415 
416 	if (strcmp(adhost->adh_trail_name, filename) == 0) {
417 		/* File name didn't change, we are done here. */
418 		return (0);
419 	}
420 
421 	if (renameat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
422 	    adhost->adh_trail_dirfd, filename) == -1) {
423 		pjdlog_errno(LOG_ERR, "Unable to rename \"%s\" to \"%s\"",
424 		    adhost->adh_trail_name, filename);
425 		PJDLOG_ASSERT(errno > 0);
426 		return (ADIST_ERROR_RENAME);
427 	}
428 	pjdlog_debug(1, "Renamed file \"%s/%s\" to \"%s/%s\".",
429 	    adhost->adh_directory, adhost->adh_trail_name,
430 	    adhost->adh_directory, filename);
431 	PJDLOG_VERIFY(strlcpy(adhost->adh_trail_name, filename,
432 	    sizeof(adhost->adh_trail_name)) < sizeof(adhost->adh_trail_name));
433 
434 	return (0);
435 }
436 
437 static int
438 receiver_error(void)
439 {
440 
441 	/* We should have opened trail file. */
442 	if (adhost->adh_trail_fd == -1) {
443 		pjdlog_error("Sender send read error, but file is not open.");
444 		return (ADIST_ERROR_WRONG_ORDER);
445 	}
446 
447 	PJDLOG_VERIFY(close(adhost->adh_trail_fd) == 0);
448 	adhost->adh_trail_fd = -1;
449 
450 	pjdlog_debug(1, "Closed file \"%s/%s\".", adhost->adh_directory,
451 	    adhost->adh_trail_name);
452 
453 	return (0);
454 }
455 
456 static void *
457 disk_thread(void *arg __unused)
458 {
459 	struct adreq *adreq;
460 
461 	for (;;) {
462 		pjdlog_debug(3, "disk: Taking request.");
463 		QUEUE_TAKE(adreq, &adist_disk_list, 0);
464 		adreq_log(LOG_DEBUG, 3, -1, adreq, "disk: (%p) Got request: ",
465 		    adreq);
466 		/* Handle the actual request. */
467 		switch (adreq->adr_cmd) {
468 		case ADIST_CMD_OPEN:
469 			adreq->adr_error = receiver_open(adreq->adr_data);
470 			break;
471 		case ADIST_CMD_APPEND:
472 			adreq->adr_error = receiver_append(adreq->adr_data,
473 			    adreq->adr_datasize);
474 			break;
475 		case ADIST_CMD_CLOSE:
476 			adreq->adr_error = receiver_close(adreq->adr_data);
477 			break;
478 		case ADIST_CMD_ERROR:
479 			adreq->adr_error = receiver_error();
480 			break;
481 		default:
482 			PJDLOG_ABORT("Unexpected command (cmd=%hhu).",
483 			    adreq->adr_cmd);
484 		}
485 		if (adreq->adr_error != 0) {
486 			adreq_log(LOG_ERR, 0, adreq->adr_error, adreq,
487 			    "Request failed: ");
488 		}
489 		pjdlog_debug(3, "disk: (%p) Moving request to the send queue.",
490 		    adreq);
491 		QUEUE_INSERT(adreq, &adist_send_list);
492 	}
493 	/* NOTREACHED */
494 	return (NULL);
495 }
496 
497 /*
498  * Thread sends requests back to primary node.
499  */
500 static void *
501 send_thread(void *arg __unused)
502 {
503 	struct adreq *adreq;
504 	struct adrep adrep;
505 
506 	for (;;) {
507 		pjdlog_debug(3, "send: Taking request.");
508 		QUEUE_TAKE(adreq, &adist_send_list, 0);
509 		adreq_log(LOG_DEBUG, 3, -1, adreq, "send: (%p) Got request: ",
510 		    adreq);
511 		adrep.adrp_byteorder = ADIST_BYTEORDER;
512 		adrep.adrp_seq = adreq->adr_seq;
513 		adrep.adrp_error = adreq->adr_error;
514 		if (proto_send(adhost->adh_remote, &adrep,
515 		    sizeof(adrep)) == -1) {
516 			pjdlog_exit(EX_TEMPFAIL, "Unable to send reply");
517 		}
518 		pjdlog_debug(3, "send: (%p) Moving request to the free queue.",
519 		    adreq);
520 		adreq_clear(adreq);
521 		QUEUE_INSERT(adreq, &adist_free_list);
522 	}
523 	/* NOTREACHED */
524 	return (NULL);
525 }
526 
527 static void
528 receiver_directory_create(void)
529 {
530 	struct passwd *pw;
531 
532 	/*
533 	 * According to getpwnam(3) we have to clear errno before calling the
534 	 * function to be able to distinguish between an error and missing
535 	 * entry (with is not treated as error by getpwnam(3)).
536 	 */
537 	errno = 0;
538 	pw = getpwnam(ADIST_USER);
539 	if (pw == NULL) {
540 		if (errno != 0) {
541 			pjdlog_exit(EX_NOUSER,
542 			    "Unable to find info about '%s' user", ADIST_USER);
543 		} else {
544 			pjdlog_exitx(EX_NOUSER, "User '%s' doesn't exist.",
545 			    ADIST_USER);
546 		}
547 	}
548 
549 	if (mkdir(adhost->adh_directory, 0700) == -1) {
550 		pjdlog_exit(EX_OSFILE, "Unable to create directory \"%s\"",
551 		    adhost->adh_directory);
552 	}
553 	if (chown(adhost->adh_directory, pw->pw_uid, pw->pw_gid) == -1) {
554 		pjdlog_errno(LOG_ERR,
555 		    "Unable to change owner of the directory \"%s\"",
556 		    adhost->adh_directory);
557 		(void)rmdir(adhost->adh_directory);
558 		exit(EX_OSFILE);
559 	}
560 }
561 
562 static void
563 receiver_directory_open(void)
564 {
565 
566 #ifdef HAVE_FDOPENDIR
567 	adhost->adh_trail_dirfd = open(adhost->adh_directory,
568 	    O_RDONLY | O_DIRECTORY);
569 	if (adhost->adh_trail_dirfd == -1) {
570 		if (errno == ENOENT) {
571 			receiver_directory_create();
572 			adhost->adh_trail_dirfd = open(adhost->adh_directory,
573 			    O_RDONLY | O_DIRECTORY);
574 		}
575 		if (adhost->adh_trail_dirfd == -1) {
576 			pjdlog_exit(EX_CONFIG,
577 			    "Unable to open directory \"%s\"",
578 			    adhost->adh_directory);
579 		}
580 	}
581 	adhost->adh_trail_dirfp = fdopendir(adhost->adh_trail_dirfd);
582 	if (adhost->adh_trail_dirfp == NULL) {
583 		pjdlog_exit(EX_CONFIG, "Unable to fdopen directory \"%s\"",
584 		    adhost->adh_directory);
585 	}
586 #else
587 	struct stat sb;
588 
589 	if (stat(adhost->adh_directory, &sb) == -1) {
590 		if (errno == ENOENT) {
591 			receiver_directory_create();
592 		} else {
593 			pjdlog_exit(EX_CONFIG,
594 			    "Unable to stat directory \"%s\"",
595 			    adhost->adh_directory);
596 		}
597 	}
598 	adhost->adh_trail_dirfp = opendir(adhost->adh_directory);
599 	if (adhost->adh_trail_dirfp == NULL) {
600 		pjdlog_exit(EX_CONFIG, "Unable to open directory \"%s\"",
601 		    adhost->adh_directory);
602 	}
603 	adhost->adh_trail_dirfd = dirfd(adhost->adh_trail_dirfp);
604 #endif
605 }
606 
607 static void
608 receiver_connect(void)
609 {
610 	uint64_t trail_size;
611 	struct stat sb;
612 
613 	PJDLOG_ASSERT(adhost->adh_trail_dirfp != NULL);
614 
615 	trail_last(adhost->adh_trail_dirfp, adhost->adh_trail_name,
616 	    sizeof(adhost->adh_trail_name));
617 
618 	if (adhost->adh_trail_name[0] == '\0') {
619 		trail_size = 0;
620 	} else {
621 		if (fstatat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
622 		    &sb, AT_SYMLINK_NOFOLLOW) == -1) {
623 			pjdlog_exit(EX_CONFIG, "Unable to stat \"%s/%s\"",
624 			    adhost->adh_directory, adhost->adh_trail_name);
625 		}
626 		if (!S_ISREG(sb.st_mode)) {
627 			pjdlog_exitx(EX_CONFIG,
628 			    "File \"%s/%s\" is not a regular file.",
629 			    adhost->adh_directory, adhost->adh_trail_name);
630 		}
631 		trail_size = sb.st_size;
632 	}
633 	trail_size = htole64(trail_size);
634 	if (proto_send(adhost->adh_remote, &trail_size,
635 	    sizeof(trail_size)) == -1) {
636 		pjdlog_exit(EX_TEMPFAIL,
637 		    "Unable to send size of the most recent trail file");
638 	}
639 	if (proto_send(adhost->adh_remote, adhost->adh_trail_name,
640 	    sizeof(adhost->adh_trail_name)) == -1) {
641 		pjdlog_exit(EX_TEMPFAIL,
642 		    "Unable to send name of the most recent trail file");
643 	}
644 }
645 
646 void
647 adist_receiver(struct adist_config *config, struct adist_host *adh)
648 {
649 	sigset_t mask;
650 	pthread_t td;
651 	pid_t pid;
652 	int error, mode, debuglevel;
653 
654 	pid = fork();
655 	if (pid == -1) {
656 		pjdlog_errno(LOG_ERR, "Unable to fork");
657 		proto_close(adh->adh_remote);
658 		adh->adh_remote = NULL;
659 		return;
660 	}
661 
662 	if (pid > 0) {
663 		/* This is parent. */
664 		proto_close(adh->adh_remote);
665 		adh->adh_remote = NULL;
666 		adh->adh_worker_pid = pid;
667 		return;
668 	}
669 
670 	adcfg = config;
671 	adhost = adh;
672 	mode = pjdlog_mode_get();
673 	debuglevel = pjdlog_debug_get();
674 
675 	descriptors_cleanup(adhost);
676 
677 //	descriptors_assert(adhost, mode);
678 
679 	pjdlog_init(mode);
680 	pjdlog_debug_set(debuglevel);
681 	pjdlog_prefix_set("[%s] (%s) ", adhost->adh_name,
682 	    role2str(adhost->adh_role));
683 #ifdef HAVE_SETPROCTITLE
684 	setproctitle("%s (%s)", adhost->adh_name, role2str(adhost->adh_role));
685 #endif
686 
687 	PJDLOG_VERIFY(sigemptyset(&mask) == 0);
688 	PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
689 
690 	/* Error in setting timeout is not critical, but why should it fail? */
691 	if (proto_timeout(adhost->adh_remote, adcfg->adc_timeout) == -1)
692 		pjdlog_errno(LOG_WARNING, "Unable to set connection timeout");
693 
694 	init_environment();
695 
696 	adhost->adh_trail_fd = -1;
697 	receiver_directory_open();
698 
699 	if (sandbox(ADIST_USER, true, "auditdistd: %s (%s)",
700 	    role2str(adhost->adh_role), adhost->adh_name) != 0) {
701 		exit(EX_CONFIG);
702 	}
703 	pjdlog_info("Privileges successfully dropped.");
704 
705 	receiver_connect();
706 
707 	error = pthread_create(&td, NULL, recv_thread, adhost);
708 	PJDLOG_ASSERT(error == 0);
709 	error = pthread_create(&td, NULL, disk_thread, adhost);
710 	PJDLOG_ASSERT(error == 0);
711 	(void)send_thread(adhost);
712 }
713