1243730Srwatson/*-
2243730Srwatson * Copyright (c) 2012 The FreeBSD Foundation
3243730Srwatson * All rights reserved.
4243730Srwatson *
5243730Srwatson * This software was developed by Pawel Jakub Dawidek under sponsorship from
6243730Srwatson * the FreeBSD Foundation.
7243730Srwatson *
8243730Srwatson * Redistribution and use in source and binary forms, with or without
9243730Srwatson * modification, are permitted provided that the following conditions
10243730Srwatson * are met:
11243730Srwatson * 1. Redistributions of source code must retain the above copyright
12243730Srwatson *    notice, this list of conditions and the following disclaimer.
13243730Srwatson * 2. Redistributions in binary form must reproduce the above copyright
14243730Srwatson *    notice, this list of conditions and the following disclaimer in the
15243730Srwatson *    documentation and/or other materials provided with the distribution.
16243730Srwatson *
17243730Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18243730Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19243730Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20243730Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21243730Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22243730Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23243730Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24243730Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25243730Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26243730Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27243730Srwatson * SUCH DAMAGE.
28243730Srwatson */
29243730Srwatson
30243734Srwatson#include <config/config.h>
31243730Srwatson
32243730Srwatson#include <sys/param.h>
33243730Srwatson#if defined(HAVE_SYS_ENDIAN_H) && defined(HAVE_BSWAP)
34243730Srwatson#include <sys/endian.h>
35243730Srwatson#else /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
36243730Srwatson#ifdef HAVE_MACHINE_ENDIAN_H
37243730Srwatson#include <machine/endian.h>
38243730Srwatson#else /* !HAVE_MACHINE_ENDIAN_H */
39243730Srwatson#ifdef HAVE_ENDIAN_H
40243730Srwatson#include <endian.h>
41243730Srwatson#else /* !HAVE_ENDIAN_H */
42243730Srwatson#error "No supported endian.h"
43243730Srwatson#endif /* !HAVE_ENDIAN_H */
44243730Srwatson#endif /* !HAVE_MACHINE_ENDIAN_H */
45243730Srwatson#include <compat/endian.h>
46243730Srwatson#endif /* !HAVE_SYS_ENDIAN_H || !HAVE_BSWAP */
47243730Srwatson#include <sys/queue.h>
48243730Srwatson#include <sys/stat.h>
49243730Srwatson#include <sys/time.h>
50243730Srwatson
51243730Srwatson#include <err.h>
52243730Srwatson#include <errno.h>
53243730Srwatson#include <fcntl.h>
54243730Srwatson#ifdef HAVE_LIBUTIL_H
55243730Srwatson#include <libutil.h>
56243730Srwatson#endif
57243730Srwatson#include <pthread.h>
58243730Srwatson#include <pwd.h>
59243730Srwatson#include <signal.h>
60243730Srwatson#include <stdint.h>
61243730Srwatson#include <stdio.h>
62243730Srwatson#include <string.h>
63243730Srwatson#include <sysexits.h>
64243730Srwatson#include <unistd.h>
65243730Srwatson
66243730Srwatson#ifndef HAVE_STRLCPY
67243730Srwatson#include <compat/strlcpy.h>
68243730Srwatson#endif
69243730Srwatson#ifndef HAVE_FSTATAT
70243730Srwatson#include "fstatat.h"
71243730Srwatson#endif
72243730Srwatson#ifndef HAVE_OPENAT
73243730Srwatson#include "openat.h"
74243730Srwatson#endif
75243730Srwatson#ifndef HAVE_RENAMEAT
76243730Srwatson#include "renameat.h"
77243730Srwatson#endif
78243730Srwatson
79243730Srwatson#include "auditdistd.h"
80243734Srwatson#include "pjdlog.h"
81243730Srwatson#include "proto.h"
82243730Srwatson#include "sandbox.h"
83243730Srwatson#include "subr.h"
84243730Srwatson#include "synch.h"
85243730Srwatson#include "trail.h"
86243730Srwatson
87243730Srwatsonstatic struct adist_config *adcfg;
88243730Srwatsonstatic struct adist_host *adhost;
89243730Srwatson
90243730Srwatsonstatic TAILQ_HEAD(, adreq) adist_free_list;
91243730Srwatsonstatic pthread_mutex_t adist_free_list_lock;
92243730Srwatsonstatic pthread_cond_t adist_free_list_cond;
93243730Srwatsonstatic TAILQ_HEAD(, adreq) adist_disk_list;
94243730Srwatsonstatic pthread_mutex_t adist_disk_list_lock;
95243730Srwatsonstatic pthread_cond_t adist_disk_list_cond;
96243730Srwatsonstatic TAILQ_HEAD(, adreq) adist_send_list;
97243730Srwatsonstatic pthread_mutex_t adist_send_list_lock;
98243730Srwatsonstatic pthread_cond_t adist_send_list_cond;
99243730Srwatson
100243730Srwatsonstatic void
101243730Srwatsonadreq_clear(struct adreq *adreq)
102243730Srwatson{
103243730Srwatson
104243730Srwatson	adreq->adr_error = -1;
105243730Srwatson	adreq->adr_byteorder = ADIST_BYTEORDER_UNDEFINED;
106243730Srwatson	adreq->adr_cmd = ADIST_CMD_UNDEFINED;
107243730Srwatson	adreq->adr_seq = 0;
108243730Srwatson	adreq->adr_datasize = 0;
109243730Srwatson}
110243730Srwatson
111243730Srwatsonstatic void
112243730Srwatsoninit_environment(void)
113243730Srwatson{
114243730Srwatson	struct adreq *adreq;
115243730Srwatson	unsigned int ii;
116243730Srwatson
117243730Srwatson	TAILQ_INIT(&adist_free_list);
118243730Srwatson	mtx_init(&adist_free_list_lock);
119243730Srwatson	cv_init(&adist_free_list_cond);
120243730Srwatson	TAILQ_INIT(&adist_disk_list);
121243730Srwatson	mtx_init(&adist_disk_list_lock);
122243730Srwatson	cv_init(&adist_disk_list_cond);
123243730Srwatson	TAILQ_INIT(&adist_send_list);
124243730Srwatson	mtx_init(&adist_send_list_lock);
125243730Srwatson	cv_init(&adist_send_list_cond);
126243730Srwatson
127243730Srwatson	for (ii = 0; ii < ADIST_QUEUE_SIZE; ii++) {
128243730Srwatson		adreq = malloc(sizeof(*adreq) + ADIST_BUF_SIZE);
129243730Srwatson		if (adreq == NULL) {
130243730Srwatson			pjdlog_exitx(EX_TEMPFAIL,
131243730Srwatson			    "Unable to allocate %zu bytes of memory for adreq object.",
132243730Srwatson			    sizeof(*adreq) + ADIST_BUF_SIZE);
133243730Srwatson		}
134243730Srwatson		adreq_clear(adreq);
135243730Srwatson		TAILQ_INSERT_TAIL(&adist_free_list, adreq, adr_next);
136243730Srwatson	}
137243730Srwatson}
138243730Srwatson
139243730Srwatsonstatic void
140243730Srwatsonadreq_decode_and_validate_header(struct adreq *adreq)
141243730Srwatson{
142243730Srwatson
143243730Srwatson	/* Byte-swap only is the sender is using different byte order. */
144243730Srwatson	if (adreq->adr_byteorder != ADIST_BYTEORDER) {
145243730Srwatson		adreq->adr_byteorder = ADIST_BYTEORDER;
146243730Srwatson		adreq->adr_seq = bswap64(adreq->adr_seq);
147243730Srwatson		adreq->adr_datasize = bswap32(adreq->adr_datasize);
148243730Srwatson	}
149243730Srwatson
150243730Srwatson	/* Validate packet header. */
151243730Srwatson
152243730Srwatson	if (adreq->adr_datasize > ADIST_BUF_SIZE) {
153243730Srwatson		pjdlog_exitx(EX_PROTOCOL, "Invalid datasize received (%ju).",
154243730Srwatson		    (uintmax_t)adreq->adr_datasize);
155243730Srwatson	}
156243730Srwatson
157243730Srwatson	switch (adreq->adr_cmd) {
158243730Srwatson	case ADIST_CMD_OPEN:
159243730Srwatson	case ADIST_CMD_APPEND:
160243730Srwatson	case ADIST_CMD_CLOSE:
161243730Srwatson		if (adreq->adr_datasize == 0) {
162243730Srwatson			pjdlog_exitx(EX_PROTOCOL,
163243730Srwatson			    "Invalid datasize received (%ju).",
164243730Srwatson			    (uintmax_t)adreq->adr_datasize);
165243730Srwatson		}
166243730Srwatson		break;
167243730Srwatson	case ADIST_CMD_KEEPALIVE:
168243730Srwatson	case ADIST_CMD_ERROR:
169243730Srwatson		if (adreq->adr_datasize > 0) {
170243730Srwatson			pjdlog_exitx(EX_PROTOCOL,
171243730Srwatson			    "Invalid datasize received (%ju).",
172243730Srwatson			    (uintmax_t)adreq->adr_datasize);
173243730Srwatson		}
174243730Srwatson		break;
175243730Srwatson	default:
176243730Srwatson		pjdlog_exitx(EX_PROTOCOL, "Invalid command received (%hhu).",
177243730Srwatson		    adreq->adr_cmd);
178243730Srwatson	}
179243730Srwatson}
180243730Srwatson
181243730Srwatsonstatic void
182243730Srwatsonadreq_validate_data(const struct adreq *adreq)
183243730Srwatson{
184243730Srwatson
185243730Srwatson	/* Validate packet data. */
186243730Srwatson
187243730Srwatson	switch (adreq->adr_cmd) {
188243730Srwatson	case ADIST_CMD_OPEN:
189243730Srwatson	case ADIST_CMD_CLOSE:
190243730Srwatson		/*
191243730Srwatson		 * File name must end up with '\0' and there must be no '\0'
192243730Srwatson		 * in the middle.
193243730Srwatson		 */
194243730Srwatson		if (adreq->adr_data[adreq->adr_datasize - 1] != '\0' ||
195243730Srwatson		    strchr(adreq->adr_data, '\0') !=
196243730Srwatson		    (const char *)adreq->adr_data + adreq->adr_datasize - 1) {
197243730Srwatson			pjdlog_exitx(EX_PROTOCOL,
198243730Srwatson			    "Invalid file name received.");
199243730Srwatson		}
200243730Srwatson		break;
201243730Srwatson	}
202243730Srwatson}
203243730Srwatson
204243730Srwatson/*
205243730Srwatson * Thread receives requests from the sender.
206243730Srwatson */
207243730Srwatsonstatic void *
208243730Srwatsonrecv_thread(void *arg __unused)
209243730Srwatson{
210243730Srwatson	struct adreq *adreq;
211243730Srwatson
212243730Srwatson	for (;;) {
213243730Srwatson		pjdlog_debug(3, "recv: Taking free request.");
214243730Srwatson		QUEUE_TAKE(adreq, &adist_free_list, 0);
215243730Srwatson		pjdlog_debug(3, "recv: (%p) Got request.", adreq);
216243730Srwatson
217243730Srwatson		if (proto_recv(adhost->adh_remote, &adreq->adr_packet,
218243730Srwatson		    sizeof(adreq->adr_packet)) == -1) {
219243730Srwatson			pjdlog_exit(EX_TEMPFAIL,
220243730Srwatson			    "Unable to receive request header");
221243730Srwatson		}
222243730Srwatson		adreq_decode_and_validate_header(adreq);
223243730Srwatson
224243730Srwatson		switch (adreq->adr_cmd) {
225243730Srwatson		case ADIST_CMD_KEEPALIVE:
226243730Srwatson			adreq->adr_error = 0;
227243730Srwatson			adreq_log(LOG_DEBUG, 2, -1, adreq,
228243730Srwatson			    "recv: (%p) Got request header: ", adreq);
229243730Srwatson			pjdlog_debug(3,
230243730Srwatson			    "recv: (%p) Moving request to the send queue.",
231243730Srwatson			    adreq);
232243730Srwatson			QUEUE_INSERT(adreq, &adist_send_list);
233243730Srwatson			continue;
234243730Srwatson		case ADIST_CMD_ERROR:
235243730Srwatson			pjdlog_error("An error occured on the sender while reading \"%s/%s\".",
236243730Srwatson			    adhost->adh_directory, adhost->adh_trail_name);
237243730Srwatson			adreq_log(LOG_DEBUG, 2, ADIST_ERROR_READ, adreq,
238243730Srwatson			    "recv: (%p) Got request header: ", adreq);
239243730Srwatson			pjdlog_debug(3,
240243730Srwatson			    "recv: (%p) Moving request to the send queue.",
241243730Srwatson			    adreq);
242243730Srwatson			QUEUE_INSERT(adreq, &adist_disk_list);
243243730Srwatson			continue;
244243730Srwatson		case ADIST_CMD_OPEN:
245243730Srwatson		case ADIST_CMD_APPEND:
246243730Srwatson		case ADIST_CMD_CLOSE:
247243730Srwatson			if (proto_recv(adhost->adh_remote, adreq->adr_data,
248243730Srwatson			    adreq->adr_datasize) == -1) {
249243730Srwatson				pjdlog_exit(EX_TEMPFAIL,
250243730Srwatson				    "Unable to receive request data");
251243730Srwatson			}
252243730Srwatson			adreq_validate_data(adreq);
253243730Srwatson			adreq_log(LOG_DEBUG, 2, -1, adreq,
254243730Srwatson			    "recv: (%p) Got request header: ", adreq);
255243730Srwatson			pjdlog_debug(3,
256243730Srwatson			    "recv: (%p) Moving request to the disk queue.",
257243730Srwatson			    adreq);
258243730Srwatson			QUEUE_INSERT(adreq, &adist_disk_list);
259243730Srwatson			break;
260243730Srwatson		default:
261243730Srwatson			PJDLOG_ABORT("Invalid condition.");
262243730Srwatson		}
263243730Srwatson	}
264243730Srwatson	/* NOTREACHED */
265243730Srwatson	return (NULL);
266243730Srwatson}
267243730Srwatson
268243730Srwatson/*
269243730Srwatson * Function that opens trail file requested by the sender.
270243730Srwatson * If the file already exist, it has to be the most recent file and it can
271243730Srwatson * only be open for append.
272243730Srwatson * If the file doesn't already exist, it has to be "older" than all existing
273243730Srwatson * files.
274243730Srwatson */
275243730Srwatsonstatic int
276243730Srwatsonreceiver_open(const char *filename)
277243730Srwatson{
278243730Srwatson	int fd;
279243730Srwatson
280243730Srwatson	/*
281243730Srwatson	 * Previous file should be closed by now. Sending OPEN request without
282243730Srwatson	 * sending CLOSE for the previous file is a sender bug.
283243730Srwatson	 */
284243730Srwatson	if (adhost->adh_trail_fd != -1) {
285243730Srwatson		pjdlog_error("Sender requested opening file \"%s\" without first closing \"%s\".",
286243730Srwatson		    filename, adhost->adh_trail_name);
287243730Srwatson		return (ADIST_ERROR_WRONG_ORDER);
288243730Srwatson	}
289243730Srwatson
290243730Srwatson	if (!trail_validate_name(filename, NULL)) {
291243730Srwatson		pjdlog_error("Sender wants to open file \"%s\", which has invalid name.",
292243730Srwatson		    filename);
293243730Srwatson		return (ADIST_ERROR_INVALID_NAME);
294243730Srwatson	}
295243730Srwatson
296243730Srwatson	switch (trail_name_compare(filename, adhost->adh_trail_name)) {
297243730Srwatson	case TRAIL_RENAMED:
298243730Srwatson		if (!trail_is_not_terminated(adhost->adh_trail_name)) {
299243730Srwatson			pjdlog_error("Terminated trail \"%s/%s\" was unterminated on the sender as \"%s/%s\"?",
300243730Srwatson			    adhost->adh_directory, adhost->adh_trail_name,
301243730Srwatson			    adhost->adh_directory, filename);
302243730Srwatson			return (ADIST_ERROR_INVALID_NAME);
303243730Srwatson		}
304243730Srwatson		if (renameat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
305243730Srwatson		    adhost->adh_trail_dirfd, filename) == -1) {
306243730Srwatson			pjdlog_errno(LOG_ERR,
307243730Srwatson			    "Unable to rename file \"%s/%s\" to \"%s/%s\"",
308243730Srwatson			    adhost->adh_directory, adhost->adh_trail_name,
309243730Srwatson			    adhost->adh_directory, filename);
310243730Srwatson			PJDLOG_ASSERT(errno > 0);
311243730Srwatson			return (ADIST_ERROR_RENAME);
312243730Srwatson		}
313243730Srwatson		pjdlog_debug(1, "Renamed file \"%s/%s\" to \"%s/%s\".",
314243730Srwatson		    adhost->adh_directory, adhost->adh_trail_name,
315243730Srwatson		    adhost->adh_directory, filename);
316243730Srwatson		/* FALLTHROUGH */
317243730Srwatson	case TRAIL_IDENTICAL:
318243730Srwatson		/* Opening existing file. */
319243730Srwatson		fd = openat(adhost->adh_trail_dirfd, filename,
320243730Srwatson		    O_WRONLY | O_APPEND | O_NOFOLLOW);
321243730Srwatson		if (fd == -1) {
322243730Srwatson			pjdlog_errno(LOG_ERR,
323243730Srwatson			    "Unable to open file \"%s/%s\" for append",
324243730Srwatson			    adhost->adh_directory, filename);
325243730Srwatson			PJDLOG_ASSERT(errno > 0);
326243730Srwatson			return (ADIST_ERROR_OPEN);
327243730Srwatson		}
328243730Srwatson		pjdlog_debug(1, "Opened file \"%s/%s\".",
329243730Srwatson		    adhost->adh_directory, filename);
330243730Srwatson		break;
331243730Srwatson	case TRAIL_NEWER:
332243730Srwatson		/* Opening new file. */
333243730Srwatson		fd = openat(adhost->adh_trail_dirfd, filename,
334243730Srwatson		    O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
335243730Srwatson		if (fd == -1) {
336243730Srwatson			pjdlog_errno(LOG_ERR,
337243730Srwatson			    "Unable to create file \"%s/%s\"",
338243730Srwatson			    adhost->adh_directory, filename);
339243730Srwatson			PJDLOG_ASSERT(errno > 0);
340243730Srwatson			return (ADIST_ERROR_CREATE);
341243730Srwatson		}
342243730Srwatson		pjdlog_debug(1, "Created file \"%s/%s\".",
343243730Srwatson		    adhost->adh_directory, filename);
344243730Srwatson		break;
345243730Srwatson	case TRAIL_OLDER:
346243730Srwatson		/* Trying to open old file. */
347243730Srwatson		pjdlog_error("Sender wants to open an old file \"%s\".", filename);
348243730Srwatson		return (ADIST_ERROR_OPEN_OLD);
349243730Srwatson	default:
350243730Srwatson		PJDLOG_ABORT("Unknown return value from trail_name_compare().");
351243730Srwatson	}
352243730Srwatson	PJDLOG_VERIFY(strlcpy(adhost->adh_trail_name, filename,
353243730Srwatson	    sizeof(adhost->adh_trail_name)) < sizeof(adhost->adh_trail_name));
354243730Srwatson	adhost->adh_trail_fd = fd;
355243730Srwatson	return (0);
356243730Srwatson}
357243730Srwatson
358243730Srwatson/*
359243730Srwatson * Function appends data to the trail file that is currently open.
360243730Srwatson */
361243730Srwatsonstatic int
362243730Srwatsonreceiver_append(const unsigned char *data, size_t size)
363243730Srwatson{
364243730Srwatson	ssize_t done;
365243730Srwatson	size_t osize;
366243730Srwatson
367243730Srwatson	/* We should have opened trail file. */
368243730Srwatson	if (adhost->adh_trail_fd == -1) {
369243730Srwatson		pjdlog_error("Sender requested append without first opening file.");
370243730Srwatson		return (ADIST_ERROR_WRONG_ORDER);
371243730Srwatson	}
372243730Srwatson
373243730Srwatson	osize = size;
374243730Srwatson	while (size > 0) {
375243730Srwatson		done = write(adhost->adh_trail_fd, data, size);
376243730Srwatson		if (done == -1) {
377243730Srwatson			if (errno == EINTR)
378243730Srwatson				continue;
379243730Srwatson			pjdlog_errno(LOG_ERR, "Write to \"%s/%s\" failed",
380243730Srwatson			    adhost->adh_directory, adhost->adh_trail_name);
381243730Srwatson			PJDLOG_ASSERT(errno > 0);
382243730Srwatson			return (ADIST_ERROR_WRITE);
383243730Srwatson		}
384243730Srwatson		pjdlog_debug(3, "Wrote %zd bytes into \"%s/%s\".", done,
385243730Srwatson		    adhost->adh_directory, adhost->adh_trail_name);
386243730Srwatson		size -= done;
387243730Srwatson	}
388243730Srwatson	pjdlog_debug(2, "Appended %zu bytes to file \"%s/%s\".",
389243730Srwatson	    osize, adhost->adh_directory, adhost->adh_trail_name);
390243730Srwatson	return (0);
391243730Srwatson}
392243730Srwatson
393243730Srwatsonstatic int
394243730Srwatsonreceiver_close(const char *filename)
395243730Srwatson{
396243730Srwatson
397243730Srwatson	/* We should have opened trail file. */
398243730Srwatson	if (adhost->adh_trail_fd == -1) {
399243730Srwatson		pjdlog_error("Sender requested closing file without first opening it.");
400243730Srwatson		return (ADIST_ERROR_WRONG_ORDER);
401243730Srwatson	}
402243730Srwatson
403243730Srwatson	/* Validate if we can do the rename. */
404243730Srwatson	if (!trail_validate_name(adhost->adh_trail_name, filename)) {
405243730Srwatson		pjdlog_error("Sender wants to close file \"%s\" using name \"%s\".",
406243730Srwatson		    adhost->adh_trail_name, filename);
407243730Srwatson		return (ADIST_ERROR_INVALID_NAME);
408243730Srwatson	}
409243730Srwatson
410243730Srwatson	PJDLOG_VERIFY(close(adhost->adh_trail_fd) == 0);
411243730Srwatson	adhost->adh_trail_fd = -1;
412243730Srwatson
413243730Srwatson	pjdlog_debug(1, "Closed file \"%s/%s\".", adhost->adh_directory,
414243730Srwatson	    adhost->adh_trail_name);
415243730Srwatson
416243730Srwatson	if (strcmp(adhost->adh_trail_name, filename) == 0) {
417243730Srwatson		/* File name didn't change, we are done here. */
418243730Srwatson		return (0);
419243730Srwatson	}
420243730Srwatson
421243730Srwatson	if (renameat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
422243730Srwatson	    adhost->adh_trail_dirfd, filename) == -1) {
423243730Srwatson		pjdlog_errno(LOG_ERR, "Unable to rename \"%s\" to \"%s\"",
424243730Srwatson		    adhost->adh_trail_name, filename);
425243730Srwatson		PJDLOG_ASSERT(errno > 0);
426243730Srwatson		return (ADIST_ERROR_RENAME);
427243730Srwatson	}
428243730Srwatson	pjdlog_debug(1, "Renamed file \"%s/%s\" to \"%s/%s\".",
429243730Srwatson	    adhost->adh_directory, adhost->adh_trail_name,
430243730Srwatson	    adhost->adh_directory, filename);
431243730Srwatson	PJDLOG_VERIFY(strlcpy(adhost->adh_trail_name, filename,
432243730Srwatson	    sizeof(adhost->adh_trail_name)) < sizeof(adhost->adh_trail_name));
433243730Srwatson
434243730Srwatson	return (0);
435243730Srwatson}
436243730Srwatson
437243730Srwatsonstatic int
438243730Srwatsonreceiver_error(void)
439243730Srwatson{
440243730Srwatson
441243730Srwatson	/* We should have opened trail file. */
442243730Srwatson	if (adhost->adh_trail_fd == -1) {
443243730Srwatson		pjdlog_error("Sender send read error, but file is not open.");
444243730Srwatson		return (ADIST_ERROR_WRONG_ORDER);
445243730Srwatson	}
446243730Srwatson
447243730Srwatson	PJDLOG_VERIFY(close(adhost->adh_trail_fd) == 0);
448243730Srwatson	adhost->adh_trail_fd = -1;
449243730Srwatson
450243730Srwatson	pjdlog_debug(1, "Closed file \"%s/%s\".", adhost->adh_directory,
451243730Srwatson	    adhost->adh_trail_name);
452243730Srwatson
453243730Srwatson	return (0);
454243730Srwatson}
455243730Srwatson
456243730Srwatsonstatic void *
457243730Srwatsondisk_thread(void *arg __unused)
458243730Srwatson{
459243730Srwatson	struct adreq *adreq;
460243730Srwatson
461243730Srwatson	for (;;) {
462243730Srwatson		pjdlog_debug(3, "disk: Taking request.");
463243730Srwatson		QUEUE_TAKE(adreq, &adist_disk_list, 0);
464243730Srwatson		adreq_log(LOG_DEBUG, 3, -1, adreq, "disk: (%p) Got request: ",
465243730Srwatson		    adreq);
466243730Srwatson		/* Handle the actual request. */
467243730Srwatson		switch (adreq->adr_cmd) {
468243730Srwatson		case ADIST_CMD_OPEN:
469243730Srwatson			adreq->adr_error = receiver_open(adreq->adr_data);
470243730Srwatson			break;
471243730Srwatson		case ADIST_CMD_APPEND:
472243730Srwatson			adreq->adr_error = receiver_append(adreq->adr_data,
473243730Srwatson			    adreq->adr_datasize);
474243730Srwatson			break;
475243730Srwatson		case ADIST_CMD_CLOSE:
476243730Srwatson			adreq->adr_error = receiver_close(adreq->adr_data);
477243730Srwatson			break;
478243730Srwatson		case ADIST_CMD_ERROR:
479243730Srwatson			adreq->adr_error = receiver_error();
480243730Srwatson			break;
481243730Srwatson		default:
482243730Srwatson			PJDLOG_ABORT("Unexpected command (cmd=%hhu).",
483243730Srwatson			    adreq->adr_cmd);
484243730Srwatson		}
485243730Srwatson		if (adreq->adr_error != 0) {
486243730Srwatson			adreq_log(LOG_ERR, 0, adreq->adr_error, adreq,
487243730Srwatson			    "Request failed: ");
488243730Srwatson		}
489243730Srwatson		pjdlog_debug(3, "disk: (%p) Moving request to the send queue.",
490243730Srwatson		    adreq);
491243730Srwatson		QUEUE_INSERT(adreq, &adist_send_list);
492243730Srwatson	}
493243730Srwatson	/* NOTREACHED */
494243730Srwatson	return (NULL);
495243730Srwatson}
496243730Srwatson
497243730Srwatson/*
498243730Srwatson * Thread sends requests back to primary node.
499243730Srwatson */
500243730Srwatsonstatic void *
501243730Srwatsonsend_thread(void *arg __unused)
502243730Srwatson{
503243730Srwatson	struct adreq *adreq;
504243730Srwatson	struct adrep adrep;
505243730Srwatson
506243730Srwatson	for (;;) {
507243730Srwatson		pjdlog_debug(3, "send: Taking request.");
508243730Srwatson		QUEUE_TAKE(adreq, &adist_send_list, 0);
509243730Srwatson		adreq_log(LOG_DEBUG, 3, -1, adreq, "send: (%p) Got request: ",
510243730Srwatson		    adreq);
511243730Srwatson		adrep.adrp_byteorder = ADIST_BYTEORDER;
512243730Srwatson		adrep.adrp_seq = adreq->adr_seq;
513243730Srwatson		adrep.adrp_error = adreq->adr_error;
514243730Srwatson		if (proto_send(adhost->adh_remote, &adrep,
515243730Srwatson		    sizeof(adrep)) == -1) {
516243730Srwatson			pjdlog_exit(EX_TEMPFAIL, "Unable to send reply");
517243730Srwatson		}
518243730Srwatson		pjdlog_debug(3, "send: (%p) Moving request to the free queue.",
519243730Srwatson		    adreq);
520243730Srwatson		adreq_clear(adreq);
521243730Srwatson		QUEUE_INSERT(adreq, &adist_free_list);
522243730Srwatson	}
523243730Srwatson	/* NOTREACHED */
524243730Srwatson	return (NULL);
525243730Srwatson}
526243730Srwatson
527243730Srwatsonstatic void
528243730Srwatsonreceiver_directory_create(void)
529243730Srwatson{
530243730Srwatson	struct passwd *pw;
531243730Srwatson
532243730Srwatson	/*
533243730Srwatson	 * According to getpwnam(3) we have to clear errno before calling the
534243730Srwatson	 * function to be able to distinguish between an error and missing
535243730Srwatson	 * entry (with is not treated as error by getpwnam(3)).
536243730Srwatson	 */
537243730Srwatson	errno = 0;
538243730Srwatson	pw = getpwnam(ADIST_USER);
539243730Srwatson	if (pw == NULL) {
540243730Srwatson		if (errno != 0) {
541243730Srwatson			pjdlog_exit(EX_NOUSER,
542243730Srwatson			    "Unable to find info about '%s' user", ADIST_USER);
543243730Srwatson		} else {
544243730Srwatson			pjdlog_exitx(EX_NOUSER, "User '%s' doesn't exist.",
545243730Srwatson			    ADIST_USER);
546243730Srwatson		}
547243730Srwatson	}
548243730Srwatson
549243730Srwatson	if (mkdir(adhost->adh_directory, 0700) == -1) {
550243730Srwatson		pjdlog_exit(EX_OSFILE, "Unable to create directory \"%s\"",
551243730Srwatson		    adhost->adh_directory);
552243730Srwatson	}
553243730Srwatson	if (chown(adhost->adh_directory, pw->pw_uid, pw->pw_gid) == -1) {
554243730Srwatson		pjdlog_errno(LOG_ERR,
555243730Srwatson		    "Unable to change owner of the directory \"%s\"",
556243730Srwatson		    adhost->adh_directory);
557243730Srwatson		(void)rmdir(adhost->adh_directory);
558243730Srwatson		exit(EX_OSFILE);
559243730Srwatson	}
560243730Srwatson}
561243730Srwatson
562243730Srwatsonstatic void
563243730Srwatsonreceiver_directory_open(void)
564243730Srwatson{
565243730Srwatson
566243730Srwatson#ifdef HAVE_FDOPENDIR
567243730Srwatson	adhost->adh_trail_dirfd = open(adhost->adh_directory,
568243730Srwatson	    O_RDONLY | O_DIRECTORY);
569243730Srwatson	if (adhost->adh_trail_dirfd == -1) {
570243730Srwatson		if (errno == ENOENT) {
571243730Srwatson			receiver_directory_create();
572243730Srwatson			adhost->adh_trail_dirfd = open(adhost->adh_directory,
573243730Srwatson			    O_RDONLY | O_DIRECTORY);
574243730Srwatson		}
575243730Srwatson		if (adhost->adh_trail_dirfd == -1) {
576243730Srwatson			pjdlog_exit(EX_CONFIG,
577243730Srwatson			    "Unable to open directory \"%s\"",
578243730Srwatson			    adhost->adh_directory);
579243730Srwatson		}
580243730Srwatson	}
581243730Srwatson	adhost->adh_trail_dirfp = fdopendir(adhost->adh_trail_dirfd);
582243730Srwatson	if (adhost->adh_trail_dirfp == NULL) {
583243730Srwatson		pjdlog_exit(EX_CONFIG, "Unable to fdopen directory \"%s\"",
584243730Srwatson		    adhost->adh_directory);
585243730Srwatson	}
586243730Srwatson#else
587243730Srwatson	struct stat sb;
588243730Srwatson
589243730Srwatson	if (stat(adhost->adh_directory, &sb) == -1) {
590243730Srwatson		if (errno == ENOENT) {
591243730Srwatson			receiver_directory_create();
592243730Srwatson		} else {
593243730Srwatson			pjdlog_exit(EX_CONFIG,
594243730Srwatson			    "Unable to stat directory \"%s\"",
595243730Srwatson			    adhost->adh_directory);
596243730Srwatson		}
597243730Srwatson	}
598243730Srwatson	adhost->adh_trail_dirfp = opendir(adhost->adh_directory);
599243730Srwatson	if (adhost->adh_trail_dirfp == NULL) {
600243730Srwatson		pjdlog_exit(EX_CONFIG, "Unable to open directory \"%s\"",
601243730Srwatson		    adhost->adh_directory);
602243730Srwatson	}
603243730Srwatson	adhost->adh_trail_dirfd = dirfd(adhost->adh_trail_dirfp);
604243730Srwatson#endif
605243730Srwatson}
606243730Srwatson
607243730Srwatsonstatic void
608243730Srwatsonreceiver_connect(void)
609243730Srwatson{
610243730Srwatson	uint64_t trail_size;
611243730Srwatson	struct stat sb;
612243730Srwatson
613243730Srwatson	PJDLOG_ASSERT(adhost->adh_trail_dirfp != NULL);
614243730Srwatson
615243730Srwatson	trail_last(adhost->adh_trail_dirfp, adhost->adh_trail_name,
616243730Srwatson	    sizeof(adhost->adh_trail_name));
617243730Srwatson
618243730Srwatson	if (adhost->adh_trail_name[0] == '\0') {
619243730Srwatson		trail_size = 0;
620243730Srwatson	} else {
621243730Srwatson		if (fstatat(adhost->adh_trail_dirfd, adhost->adh_trail_name,
622243730Srwatson		    &sb, AT_SYMLINK_NOFOLLOW) == -1) {
623243730Srwatson			pjdlog_exit(EX_CONFIG, "Unable to stat \"%s/%s\"",
624243730Srwatson			    adhost->adh_directory, adhost->adh_trail_name);
625243730Srwatson		}
626243730Srwatson		if (!S_ISREG(sb.st_mode)) {
627243730Srwatson			pjdlog_exitx(EX_CONFIG,
628243730Srwatson			    "File \"%s/%s\" is not a regular file.",
629243730Srwatson			    adhost->adh_directory, adhost->adh_trail_name);
630243730Srwatson		}
631243730Srwatson		trail_size = sb.st_size;
632243730Srwatson	}
633243730Srwatson	trail_size = htole64(trail_size);
634243730Srwatson	if (proto_send(adhost->adh_remote, &trail_size,
635243730Srwatson	    sizeof(trail_size)) == -1) {
636243730Srwatson		pjdlog_exit(EX_TEMPFAIL,
637243730Srwatson		    "Unable to send size of the most recent trail file");
638243730Srwatson	}
639243730Srwatson	if (proto_send(adhost->adh_remote, adhost->adh_trail_name,
640243730Srwatson	    sizeof(adhost->adh_trail_name)) == -1) {
641243730Srwatson		pjdlog_exit(EX_TEMPFAIL,
642243730Srwatson		    "Unable to send name of the most recent trail file");
643243730Srwatson	}
644243730Srwatson}
645243730Srwatson
646243730Srwatsonvoid
647243730Srwatsonadist_receiver(struct adist_config *config, struct adist_host *adh)
648243730Srwatson{
649243730Srwatson	sigset_t mask;
650243730Srwatson	pthread_t td;
651243730Srwatson	pid_t pid;
652243730Srwatson	int error, mode, debuglevel;
653243730Srwatson
654243730Srwatson	pid = fork();
655243730Srwatson	if (pid == -1) {
656243730Srwatson		pjdlog_errno(LOG_ERR, "Unable to fork");
657243730Srwatson		proto_close(adh->adh_remote);
658243730Srwatson		adh->adh_remote = NULL;
659243730Srwatson		return;
660243730Srwatson	}
661243730Srwatson
662243730Srwatson	if (pid > 0) {
663243730Srwatson		/* This is parent. */
664243730Srwatson		proto_close(adh->adh_remote);
665243730Srwatson		adh->adh_remote = NULL;
666243730Srwatson		adh->adh_worker_pid = pid;
667243730Srwatson		return;
668243730Srwatson	}
669243730Srwatson
670243730Srwatson	adcfg = config;
671243730Srwatson	adhost = adh;
672243730Srwatson	mode = pjdlog_mode_get();
673243730Srwatson	debuglevel = pjdlog_debug_get();
674243730Srwatson
675243730Srwatson	descriptors_cleanup(adhost);
676243730Srwatson
677243730Srwatson//	descriptors_assert(adhost, mode);
678243730Srwatson
679243730Srwatson	pjdlog_init(mode);
680243730Srwatson	pjdlog_debug_set(debuglevel);
681243730Srwatson	pjdlog_prefix_set("[%s] (%s) ", adhost->adh_name,
682243730Srwatson	    role2str(adhost->adh_role));
683243730Srwatson#ifdef HAVE_SETPROCTITLE
684243730Srwatson	setproctitle("%s (%s)", adhost->adh_name, role2str(adhost->adh_role));
685243730Srwatson#endif
686243730Srwatson
687243730Srwatson	PJDLOG_VERIFY(sigemptyset(&mask) == 0);
688243730Srwatson	PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
689243730Srwatson
690243730Srwatson	/* Error in setting timeout is not critical, but why should it fail? */
691243730Srwatson	if (proto_timeout(adhost->adh_remote, adcfg->adc_timeout) == -1)
692243730Srwatson		pjdlog_errno(LOG_WARNING, "Unable to set connection timeout");
693243730Srwatson
694243730Srwatson	init_environment();
695243730Srwatson
696243730Srwatson	adhost->adh_trail_fd = -1;
697243730Srwatson	receiver_directory_open();
698243730Srwatson
699243730Srwatson	if (sandbox(ADIST_USER, true, "auditdistd: %s (%s)",
700243730Srwatson	    role2str(adhost->adh_role), adhost->adh_name) != 0) {
701243730Srwatson		exit(EX_CONFIG);
702243730Srwatson	}
703243730Srwatson	pjdlog_info("Privileges successfully dropped.");
704243730Srwatson
705243730Srwatson	receiver_connect();
706243730Srwatson
707243730Srwatson	error = pthread_create(&td, NULL, recv_thread, adhost);
708243730Srwatson	PJDLOG_ASSERT(error == 0);
709243730Srwatson	error = pthread_create(&td, NULL, disk_thread, adhost);
710243730Srwatson	PJDLOG_ASSERT(error == 0);
711243730Srwatson	(void)send_thread(adhost);
712243730Srwatson}
713