]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal-remote/journal-remote.c
Merge pull request #1452 from poettering/journal-vacuum
[thirdparty/systemd.git] / src / journal-remote / journal-remote.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2012 Zbigniew Jędrzejewski-Szmek
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <getopt.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/prctl.h>
29 #include <sys/socket.h>
30 #include <unistd.h>
31
32 #ifdef HAVE_GNUTLS
33 #include <gnutls/gnutls.h>
34 #endif
35
36 #include "sd-daemon.h"
37
38 #include "conf-parser.h"
39 #include "fileio.h"
40 #include "journal-file.h"
41 #include "journald-native.h"
42 #include "macro.h"
43 #include "signal-util.h"
44 #include "socket-util.h"
45 #include "strv.h"
46 #include "journal-remote-write.h"
47 #include "journal-remote.h"
48
49 #define REMOTE_JOURNAL_PATH "/var/log/journal/remote"
50
51 #define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-remote.pem"
52 #define CERT_FILE CERTIFICATE_ROOT "/certs/journal-remote.pem"
53 #define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
54
55 static char* arg_url = NULL;
56 static char* arg_getter = NULL;
57 static char* arg_listen_raw = NULL;
58 static char* arg_listen_http = NULL;
59 static char* arg_listen_https = NULL;
60 static char** arg_files = NULL;
61 static int arg_compress = true;
62 static int arg_seal = false;
63 static int http_socket = -1, https_socket = -1;
64 static char** arg_gnutls_log = NULL;
65
66 static JournalWriteSplitMode arg_split_mode = JOURNAL_WRITE_SPLIT_HOST;
67 static char* arg_output = NULL;
68
69 static char *arg_key = NULL;
70 static char *arg_cert = NULL;
71 static char *arg_trust = NULL;
72 static bool arg_trust_all = false;
73
74 /**********************************************************************
75 **********************************************************************
76 **********************************************************************/
77
78 static int spawn_child(const char* child, char** argv) {
79 int fd[2];
80 pid_t parent_pid, child_pid;
81 int r;
82
83 if (pipe(fd) < 0)
84 return log_error_errno(errno, "Failed to create pager pipe: %m");
85
86 parent_pid = getpid();
87
88 child_pid = fork();
89 if (child_pid < 0) {
90 r = log_error_errno(errno, "Failed to fork: %m");
91 safe_close_pair(fd);
92 return r;
93 }
94
95 /* In the child */
96 if (child_pid == 0) {
97
98 (void) reset_all_signal_handlers();
99 (void) reset_signal_mask();
100
101 r = dup2(fd[1], STDOUT_FILENO);
102 if (r < 0) {
103 log_error_errno(errno, "Failed to dup pipe to stdout: %m");
104 _exit(EXIT_FAILURE);
105 }
106
107 safe_close_pair(fd);
108
109 /* Make sure the child goes away when the parent dies */
110 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
111 _exit(EXIT_FAILURE);
112
113 /* Check whether our parent died before we were able
114 * to set the death signal */
115 if (getppid() != parent_pid)
116 _exit(EXIT_SUCCESS);
117
118 execvp(child, argv);
119 log_error_errno(errno, "Failed to exec child %s: %m", child);
120 _exit(EXIT_FAILURE);
121 }
122
123 r = close(fd[1]);
124 if (r < 0)
125 log_warning_errno(errno, "Failed to close write end of pipe: %m");
126
127 return fd[0];
128 }
129
130 static int spawn_curl(const char* url) {
131 char **argv = STRV_MAKE("curl",
132 "-HAccept: application/vnd.fdo.journal",
133 "--silent",
134 "--show-error",
135 url);
136 int r;
137
138 r = spawn_child("curl", argv);
139 if (r < 0)
140 log_error_errno(errno, "Failed to spawn curl: %m");
141 return r;
142 }
143
144 static int spawn_getter(const char *getter, const char *url) {
145 int r;
146 _cleanup_strv_free_ char **words = NULL;
147
148 assert(getter);
149 r = strv_split_extract(&words, getter, WHITESPACE, EXTRACT_QUOTES);
150 if (r < 0)
151 return log_error_errno(r, "Failed to split getter option: %m");
152
153 r = strv_extend(&words, url);
154 if (r < 0)
155 return log_error_errno(r, "Failed to create command line: %m");
156
157 r = spawn_child(words[0], words);
158 if (r < 0)
159 log_error_errno(errno, "Failed to spawn getter %s: %m", getter);
160
161 return r;
162 }
163
164 #define filename_escape(s) xescape((s), "/ ")
165
166 static int open_output(Writer *w, const char* host) {
167 _cleanup_free_ char *_output = NULL;
168 const char *output;
169 int r;
170
171 switch (arg_split_mode) {
172 case JOURNAL_WRITE_SPLIT_NONE:
173 output = arg_output ?: REMOTE_JOURNAL_PATH "/remote.journal";
174 break;
175
176 case JOURNAL_WRITE_SPLIT_HOST: {
177 _cleanup_free_ char *name;
178
179 assert(host);
180
181 name = filename_escape(host);
182 if (!name)
183 return log_oom();
184
185 r = asprintf(&_output, "%s/remote-%s.journal",
186 arg_output ?: REMOTE_JOURNAL_PATH,
187 name);
188 if (r < 0)
189 return log_oom();
190
191 output = _output;
192 break;
193 }
194
195 default:
196 assert_not_reached("what?");
197 }
198
199 r = journal_file_open_reliably(output,
200 O_RDWR|O_CREAT, 0640,
201 arg_compress, arg_seal,
202 &w->metrics,
203 w->mmap,
204 NULL, &w->journal);
205 if (r < 0)
206 log_error_errno(r, "Failed to open output journal %s: %m",
207 output);
208 else
209 log_debug("Opened output file %s", w->journal->path);
210 return r;
211 }
212
213 /**********************************************************************
214 **********************************************************************
215 **********************************************************************/
216
217 static int init_writer_hashmap(RemoteServer *s) {
218 static const struct hash_ops *hash_ops[] = {
219 [JOURNAL_WRITE_SPLIT_NONE] = NULL,
220 [JOURNAL_WRITE_SPLIT_HOST] = &string_hash_ops,
221 };
222
223 assert(arg_split_mode >= 0 && arg_split_mode < (int) ELEMENTSOF(hash_ops));
224
225 s->writers = hashmap_new(hash_ops[arg_split_mode]);
226 if (!s->writers)
227 return log_oom();
228
229 return 0;
230 }
231
232 static int get_writer(RemoteServer *s, const char *host,
233 Writer **writer) {
234 const void *key;
235 _cleanup_writer_unref_ Writer *w = NULL;
236 int r;
237
238 switch(arg_split_mode) {
239 case JOURNAL_WRITE_SPLIT_NONE:
240 key = "one and only";
241 break;
242
243 case JOURNAL_WRITE_SPLIT_HOST:
244 assert(host);
245 key = host;
246 break;
247
248 default:
249 assert_not_reached("what split mode?");
250 }
251
252 w = hashmap_get(s->writers, key);
253 if (w)
254 writer_ref(w);
255 else {
256 w = writer_new(s);
257 if (!w)
258 return log_oom();
259
260 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST) {
261 w->hashmap_key = strdup(key);
262 if (!w->hashmap_key)
263 return log_oom();
264 }
265
266 r = open_output(w, host);
267 if (r < 0)
268 return r;
269
270 r = hashmap_put(s->writers, w->hashmap_key ?: key, w);
271 if (r < 0)
272 return r;
273 }
274
275 *writer = w;
276 w = NULL;
277 return 0;
278 }
279
280 /**********************************************************************
281 **********************************************************************
282 **********************************************************************/
283
284 /* This should go away as soon as µhttpd allows state to be passed around. */
285 static RemoteServer *server;
286
287 static int dispatch_raw_source_event(sd_event_source *event,
288 int fd,
289 uint32_t revents,
290 void *userdata);
291 static int dispatch_raw_source_until_block(sd_event_source *event,
292 void *userdata);
293 static int dispatch_blocking_source_event(sd_event_source *event,
294 void *userdata);
295 static int dispatch_raw_connection_event(sd_event_source *event,
296 int fd,
297 uint32_t revents,
298 void *userdata);
299 static int dispatch_http_event(sd_event_source *event,
300 int fd,
301 uint32_t revents,
302 void *userdata);
303
304 static int get_source_for_fd(RemoteServer *s,
305 int fd, char *name, RemoteSource **source) {
306 Writer *writer;
307 int r;
308
309 /* This takes ownership of name, but only on success. */
310
311 assert(fd >= 0);
312 assert(source);
313
314 if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
315 return log_oom();
316
317 r = get_writer(s, name, &writer);
318 if (r < 0)
319 return log_warning_errno(r, "Failed to get writer for source %s: %m",
320 name);
321
322 if (s->sources[fd] == NULL) {
323 s->sources[fd] = source_new(fd, false, name, writer);
324 if (!s->sources[fd]) {
325 writer_unref(writer);
326 return log_oom();
327 }
328
329 s->active++;
330 }
331
332 *source = s->sources[fd];
333 return 0;
334 }
335
336 static int remove_source(RemoteServer *s, int fd) {
337 RemoteSource *source;
338
339 assert(s);
340 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
341
342 source = s->sources[fd];
343 if (source) {
344 /* this closes fd too */
345 source_free(source);
346 s->sources[fd] = NULL;
347 s->active--;
348 }
349
350 return 0;
351 }
352
353 static int add_source(RemoteServer *s, int fd, char* name, bool own_name) {
354
355 RemoteSource *source = NULL;
356 int r;
357
358 /* This takes ownership of name, even on failure, if own_name is true. */
359
360 assert(s);
361 assert(fd >= 0);
362 assert(name);
363
364 if (!own_name) {
365 name = strdup(name);
366 if (!name)
367 return log_oom();
368 }
369
370 r = get_source_for_fd(s, fd, name, &source);
371 if (r < 0) {
372 log_error_errno(r, "Failed to create source for fd:%d (%s): %m",
373 fd, name);
374 free(name);
375 return r;
376 }
377
378 r = sd_event_add_io(s->events, &source->event,
379 fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI,
380 dispatch_raw_source_event, source);
381 if (r == 0) {
382 /* Add additional source for buffer processing. It will be
383 * enabled later. */
384 r = sd_event_add_defer(s->events, &source->buffer_event,
385 dispatch_raw_source_until_block, source);
386 if (r == 0)
387 sd_event_source_set_enabled(source->buffer_event, SD_EVENT_OFF);
388 } else if (r == -EPERM) {
389 log_debug("Falling back to sd_event_add_defer for fd:%d (%s)", fd, name);
390 r = sd_event_add_defer(s->events, &source->event,
391 dispatch_blocking_source_event, source);
392 if (r == 0)
393 sd_event_source_set_enabled(source->event, SD_EVENT_ON);
394 }
395 if (r < 0) {
396 log_error_errno(r, "Failed to register event source for fd:%d: %m",
397 fd);
398 goto error;
399 }
400
401 r = sd_event_source_set_description(source->event, name);
402 if (r < 0) {
403 log_error_errno(r, "Failed to set source name for fd:%d: %m", fd);
404 goto error;
405 }
406
407 return 1; /* work to do */
408
409 error:
410 remove_source(s, fd);
411 return r;
412 }
413
414 static int add_raw_socket(RemoteServer *s, int fd) {
415 int r;
416 _cleanup_close_ int fd_ = fd;
417 char name[sizeof("raw-socket-")-1 + DECIMAL_STR_MAX(int) + 1];
418
419 assert(fd >= 0);
420
421 r = sd_event_add_io(s->events, &s->listen_event,
422 fd, EPOLLIN,
423 dispatch_raw_connection_event, s);
424 if (r < 0)
425 return r;
426
427 xsprintf(name, "raw-socket-%d", fd);
428
429 r = sd_event_source_set_description(s->listen_event, name);
430 if (r < 0)
431 return r;
432
433 fd_ = -1;
434 s->active ++;
435 return 0;
436 }
437
438 static int setup_raw_socket(RemoteServer *s, const char *address) {
439 int fd;
440
441 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
442 if (fd < 0)
443 return fd;
444
445 return add_raw_socket(s, fd);
446 }
447
448 /**********************************************************************
449 **********************************************************************
450 **********************************************************************/
451
452 static int request_meta(void **connection_cls, int fd, char *hostname) {
453 RemoteSource *source;
454 Writer *writer;
455 int r;
456
457 assert(connection_cls);
458 if (*connection_cls)
459 return 0;
460
461 r = get_writer(server, hostname, &writer);
462 if (r < 0)
463 return log_warning_errno(r, "Failed to get writer for source %s: %m",
464 hostname);
465
466 source = source_new(fd, true, hostname, writer);
467 if (!source) {
468 writer_unref(writer);
469 return log_oom();
470 }
471
472 log_debug("Added RemoteSource as connection metadata %p", source);
473
474 *connection_cls = source;
475 return 0;
476 }
477
478 static void request_meta_free(void *cls,
479 struct MHD_Connection *connection,
480 void **connection_cls,
481 enum MHD_RequestTerminationCode toe) {
482 RemoteSource *s;
483
484 assert(connection_cls);
485 s = *connection_cls;
486
487 if (s) {
488 log_debug("Cleaning up connection metadata %p", s);
489 source_free(s);
490 *connection_cls = NULL;
491 }
492 }
493
494 static int process_http_upload(
495 struct MHD_Connection *connection,
496 const char *upload_data,
497 size_t *upload_data_size,
498 RemoteSource *source) {
499
500 bool finished = false;
501 size_t remaining;
502 int r;
503
504 assert(source);
505
506 log_trace("%s: connection %p, %zu bytes",
507 __func__, connection, *upload_data_size);
508
509 if (*upload_data_size) {
510 log_trace("Received %zu bytes", *upload_data_size);
511
512 r = push_data(source, upload_data, *upload_data_size);
513 if (r < 0)
514 return mhd_respond_oom(connection);
515
516 *upload_data_size = 0;
517 } else
518 finished = true;
519
520 for (;;) {
521 r = process_source(source, arg_compress, arg_seal);
522 if (r == -EAGAIN)
523 break;
524 else if (r < 0) {
525 log_warning("Failed to process data for connection %p", connection);
526 if (r == -E2BIG)
527 return mhd_respondf(connection,
528 MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
529 "Entry is too large, maximum is %u bytes.\n",
530 DATA_SIZE_MAX);
531 else
532 return mhd_respondf(connection,
533 MHD_HTTP_UNPROCESSABLE_ENTITY,
534 "Processing failed: %s.", strerror(-r));
535 }
536 }
537
538 if (!finished)
539 return MHD_YES;
540
541 /* The upload is finished */
542
543 remaining = source_non_empty(source);
544 if (remaining > 0) {
545 log_warning("Premature EOFbyte. %zu bytes lost.", remaining);
546 return mhd_respondf(connection, MHD_HTTP_EXPECTATION_FAILED,
547 "Premature EOF. %zu bytes of trailing data not processed.",
548 remaining);
549 }
550
551 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
552 };
553
554 static int request_handler(
555 void *cls,
556 struct MHD_Connection *connection,
557 const char *url,
558 const char *method,
559 const char *version,
560 const char *upload_data,
561 size_t *upload_data_size,
562 void **connection_cls) {
563
564 const char *header;
565 int r, code, fd;
566 _cleanup_free_ char *hostname = NULL;
567
568 assert(connection);
569 assert(connection_cls);
570 assert(url);
571 assert(method);
572
573 log_trace("Handling a connection %s %s %s", method, url, version);
574
575 if (*connection_cls)
576 return process_http_upload(connection,
577 upload_data, upload_data_size,
578 *connection_cls);
579
580 if (!streq(method, "POST"))
581 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
582 "Unsupported method.\n");
583
584 if (!streq(url, "/upload"))
585 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
586 "Not found.\n");
587
588 header = MHD_lookup_connection_value(connection,
589 MHD_HEADER_KIND, "Content-Type");
590 if (!header || !streq(header, "application/vnd.fdo.journal"))
591 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
592 "Content-Type: application/vnd.fdo.journal"
593 " is required.\n");
594
595 {
596 const union MHD_ConnectionInfo *ci;
597
598 ci = MHD_get_connection_info(connection,
599 MHD_CONNECTION_INFO_CONNECTION_FD);
600 if (!ci) {
601 log_error("MHD_get_connection_info failed: cannot get remote fd");
602 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
603 "Cannot check remote address");
604 }
605
606 fd = ci->connect_fd;
607 assert(fd >= 0);
608 }
609
610 if (server->check_trust) {
611 r = check_permissions(connection, &code, &hostname);
612 if (r < 0)
613 return code;
614 } else {
615 r = getnameinfo_pretty(fd, &hostname);
616 if (r < 0)
617 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
618 "Cannot check remote hostname");
619 }
620
621 assert(hostname);
622
623 r = request_meta(connection_cls, fd, hostname);
624 if (r == -ENOMEM)
625 return respond_oom(connection);
626 else if (r < 0)
627 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
628 strerror(-r));
629
630 hostname = NULL;
631 return MHD_YES;
632 }
633
634 static int setup_microhttpd_server(RemoteServer *s,
635 int fd,
636 const char *key,
637 const char *cert,
638 const char *trust) {
639 struct MHD_OptionItem opts[] = {
640 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
641 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
642 { MHD_OPTION_LISTEN_SOCKET, fd},
643 { MHD_OPTION_END},
644 { MHD_OPTION_END},
645 { MHD_OPTION_END},
646 { MHD_OPTION_END}};
647 int opts_pos = 3;
648 int flags =
649 MHD_USE_DEBUG |
650 MHD_USE_DUAL_STACK |
651 MHD_USE_EPOLL_LINUX_ONLY |
652 MHD_USE_PEDANTIC_CHECKS |
653 MHD_USE_PIPE_FOR_SHUTDOWN;
654
655 const union MHD_DaemonInfo *info;
656 int r, epoll_fd;
657 MHDDaemonWrapper *d;
658
659 assert(fd >= 0);
660
661 r = fd_nonblock(fd, true);
662 if (r < 0)
663 return log_error_errno(r, "Failed to make fd:%d nonblocking: %m", fd);
664
665 if (key) {
666 assert(cert);
667
668 opts[opts_pos++] = (struct MHD_OptionItem)
669 {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key};
670 opts[opts_pos++] = (struct MHD_OptionItem)
671 {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert};
672
673 flags |= MHD_USE_SSL;
674
675 if (trust)
676 opts[opts_pos++] = (struct MHD_OptionItem)
677 {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
678 }
679
680 d = new(MHDDaemonWrapper, 1);
681 if (!d)
682 return log_oom();
683
684 d->fd = (uint64_t) fd;
685
686 d->daemon = MHD_start_daemon(flags, 0,
687 NULL, NULL,
688 request_handler, NULL,
689 MHD_OPTION_ARRAY, opts,
690 MHD_OPTION_END);
691 if (!d->daemon) {
692 log_error("Failed to start µhttp daemon");
693 r = -EINVAL;
694 goto error;
695 }
696
697 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
698 key ? "HTTPS" : "HTTP", fd, d);
699
700
701 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
702 if (!info) {
703 log_error("µhttp returned NULL daemon info");
704 r = -EOPNOTSUPP;
705 goto error;
706 }
707
708 epoll_fd = info->listen_fd;
709 if (epoll_fd < 0) {
710 log_error("µhttp epoll fd is invalid");
711 r = -EUCLEAN;
712 goto error;
713 }
714
715 r = sd_event_add_io(s->events, &d->event,
716 epoll_fd, EPOLLIN,
717 dispatch_http_event, d);
718 if (r < 0) {
719 log_error_errno(r, "Failed to add event callback: %m");
720 goto error;
721 }
722
723 r = sd_event_source_set_description(d->event, "epoll-fd");
724 if (r < 0) {
725 log_error_errno(r, "Failed to set source name: %m");
726 goto error;
727 }
728
729 r = hashmap_ensure_allocated(&s->daemons, &uint64_hash_ops);
730 if (r < 0) {
731 log_oom();
732 goto error;
733 }
734
735 r = hashmap_put(s->daemons, &d->fd, d);
736 if (r < 0) {
737 log_error_errno(r, "Failed to add daemon to hashmap: %m");
738 goto error;
739 }
740
741 s->active ++;
742 return 0;
743
744 error:
745 MHD_stop_daemon(d->daemon);
746 free(d->daemon);
747 free(d);
748 return r;
749 }
750
751 static int setup_microhttpd_socket(RemoteServer *s,
752 const char *address,
753 const char *key,
754 const char *cert,
755 const char *trust) {
756 int fd;
757
758 fd = make_socket_fd(LOG_DEBUG, address, SOCK_STREAM | SOCK_CLOEXEC);
759 if (fd < 0)
760 return fd;
761
762 return setup_microhttpd_server(s, fd, key, cert, trust);
763 }
764
765 static int dispatch_http_event(sd_event_source *event,
766 int fd,
767 uint32_t revents,
768 void *userdata) {
769 MHDDaemonWrapper *d = userdata;
770 int r;
771
772 assert(d);
773
774 r = MHD_run(d->daemon);
775 if (r == MHD_NO) {
776 log_error("MHD_run failed!");
777 // XXX: unregister daemon
778 return -EINVAL;
779 }
780
781 return 1; /* work to do */
782 }
783
784 /**********************************************************************
785 **********************************************************************
786 **********************************************************************/
787
788 static int setup_signals(RemoteServer *s) {
789 int r;
790
791 assert(s);
792
793 assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0);
794
795 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, NULL, s);
796 if (r < 0)
797 return r;
798
799 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, NULL, s);
800 if (r < 0)
801 return r;
802
803 return 0;
804 }
805
806 static int negative_fd(const char *spec) {
807 /* Return a non-positive number as its inverse, -EINVAL otherwise. */
808
809 int fd, r;
810
811 r = safe_atoi(spec, &fd);
812 if (r < 0)
813 return r;
814
815 if (fd > 0)
816 return -EINVAL;
817 else
818 return -fd;
819 }
820
821 static int remoteserver_init(RemoteServer *s,
822 const char* key,
823 const char* cert,
824 const char* trust) {
825 int r, n, fd;
826 char **file;
827
828 assert(s);
829
830 if ((arg_listen_raw || arg_listen_http) && trust) {
831 log_error("Option --trust makes all non-HTTPS connections untrusted.");
832 return -EINVAL;
833 }
834
835 r = sd_event_default(&s->events);
836 if (r < 0)
837 return log_error_errno(r, "Failed to allocate event loop: %m");
838
839 setup_signals(s);
840
841 assert(server == NULL);
842 server = s;
843
844 r = init_writer_hashmap(s);
845 if (r < 0)
846 return r;
847
848 n = sd_listen_fds(true);
849 if (n < 0)
850 return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
851 else
852 log_debug("Received %d descriptors", n);
853
854 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
855 log_error("Received fewer sockets than expected");
856 return -EBADFD;
857 }
858
859 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
860 if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
861 log_debug("Received a listening socket (fd:%d)", fd);
862
863 if (fd == http_socket)
864 r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
865 else if (fd == https_socket)
866 r = setup_microhttpd_server(s, fd, key, cert, trust);
867 else
868 r = add_raw_socket(s, fd);
869 } else if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
870 char *hostname;
871
872 r = getnameinfo_pretty(fd, &hostname);
873 if (r < 0)
874 return log_error_errno(r, "Failed to retrieve remote name: %m");
875
876 log_debug("Received a connection socket (fd:%d) from %s", fd, hostname);
877
878 r = add_source(s, fd, hostname, true);
879 } else {
880 log_error("Unknown socket passed on fd:%d", fd);
881
882 return -EINVAL;
883 }
884
885 if (r < 0)
886 return log_error_errno(r, "Failed to register socket (fd:%d): %m",
887 fd);
888 }
889
890 if (arg_url) {
891 const char *url, *hostname;
892
893 url = strjoina(arg_url, "/entries");
894
895 if (arg_getter) {
896 log_info("Spawning getter %s...", url);
897 fd = spawn_getter(arg_getter, url);
898 } else {
899 log_info("Spawning curl %s...", url);
900 fd = spawn_curl(url);
901 }
902 if (fd < 0)
903 return fd;
904
905 hostname =
906 startswith(arg_url, "https://") ?:
907 startswith(arg_url, "http://") ?:
908 arg_url;
909
910 r = add_source(s, fd, (char*) hostname, false);
911 if (r < 0)
912 return r;
913 }
914
915 if (arg_listen_raw) {
916 log_debug("Listening on a socket...");
917 r = setup_raw_socket(s, arg_listen_raw);
918 if (r < 0)
919 return r;
920 }
921
922 if (arg_listen_http) {
923 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
924 if (r < 0)
925 return r;
926 }
927
928 if (arg_listen_https) {
929 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
930 if (r < 0)
931 return r;
932 }
933
934 STRV_FOREACH(file, arg_files) {
935 const char *output_name;
936
937 if (streq(*file, "-")) {
938 log_debug("Using standard input as source.");
939
940 fd = STDIN_FILENO;
941 output_name = "stdin";
942 } else {
943 log_debug("Reading file %s...", *file);
944
945 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
946 if (fd < 0)
947 return log_error_errno(errno, "Failed to open %s: %m", *file);
948 output_name = *file;
949 }
950
951 r = add_source(s, fd, (char*) output_name, false);
952 if (r < 0)
953 return r;
954 }
955
956 if (s->active == 0) {
957 log_error("Zero sources specified");
958 return -EINVAL;
959 }
960
961 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
962 /* In this case we know what the writer will be
963 called, so we can create it and verify that we can
964 create output as expected. */
965 r = get_writer(s, NULL, &s->_single_writer);
966 if (r < 0)
967 return r;
968 }
969
970 return 0;
971 }
972
973 static void server_destroy(RemoteServer *s) {
974 size_t i;
975 MHDDaemonWrapper *d;
976
977 while ((d = hashmap_steal_first(s->daemons))) {
978 MHD_stop_daemon(d->daemon);
979 sd_event_source_unref(d->event);
980 free(d);
981 }
982
983 hashmap_free(s->daemons);
984
985 assert(s->sources_size == 0 || s->sources);
986 for (i = 0; i < s->sources_size; i++)
987 remove_source(s, i);
988 free(s->sources);
989
990 writer_unref(s->_single_writer);
991 hashmap_free(s->writers);
992
993 sd_event_source_unref(s->sigterm_event);
994 sd_event_source_unref(s->sigint_event);
995 sd_event_source_unref(s->listen_event);
996 sd_event_unref(s->events);
997
998 /* fds that we're listening on remain open... */
999 }
1000
1001 /**********************************************************************
1002 **********************************************************************
1003 **********************************************************************/
1004
1005 static int handle_raw_source(sd_event_source *event,
1006 int fd,
1007 uint32_t revents,
1008 RemoteServer *s) {
1009
1010 RemoteSource *source;
1011 int r;
1012
1013 /* Returns 1 if there might be more data pending,
1014 * 0 if data is currently exhausted, negative on error.
1015 */
1016
1017 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
1018 source = s->sources[fd];
1019 assert(source->fd == fd);
1020
1021 r = process_source(source, arg_compress, arg_seal);
1022 if (source->state == STATE_EOF) {
1023 size_t remaining;
1024
1025 log_debug("EOF reached with source fd:%d (%s)",
1026 source->fd, source->name);
1027
1028 remaining = source_non_empty(source);
1029 if (remaining > 0)
1030 log_notice("Premature EOF. %zu bytes lost.", remaining);
1031 remove_source(s, source->fd);
1032 log_debug("%zu active sources remaining", s->active);
1033 return 0;
1034 } else if (r == -E2BIG) {
1035 log_notice_errno(E2BIG, "Entry too big, skipped");
1036 return 1;
1037 } else if (r == -EAGAIN) {
1038 return 0;
1039 } else if (r < 0) {
1040 log_debug_errno(r, "Closing connection: %m");
1041 remove_source(server, fd);
1042 return 0;
1043 } else
1044 return 1;
1045 }
1046
1047 static int dispatch_raw_source_until_block(sd_event_source *event,
1048 void *userdata) {
1049 RemoteSource *source = userdata;
1050 int r;
1051
1052 /* Make sure event stays around even if source is destroyed */
1053 sd_event_source_ref(event);
1054
1055 r = handle_raw_source(event, source->fd, EPOLLIN, server);
1056 if (r != 1)
1057 /* No more data for now */
1058 sd_event_source_set_enabled(event, SD_EVENT_OFF);
1059
1060 sd_event_source_unref(event);
1061
1062 return r;
1063 }
1064
1065 static int dispatch_raw_source_event(sd_event_source *event,
1066 int fd,
1067 uint32_t revents,
1068 void *userdata) {
1069 RemoteSource *source = userdata;
1070 int r;
1071
1072 assert(source->event);
1073 assert(source->buffer_event);
1074
1075 r = handle_raw_source(event, fd, EPOLLIN, server);
1076 if (r == 1)
1077 /* Might have more data. We need to rerun the handler
1078 * until we are sure the buffer is exhausted. */
1079 sd_event_source_set_enabled(source->buffer_event, SD_EVENT_ON);
1080
1081 return r;
1082 }
1083
1084 static int dispatch_blocking_source_event(sd_event_source *event,
1085 void *userdata) {
1086 RemoteSource *source = userdata;
1087
1088 return handle_raw_source(event, source->fd, EPOLLIN, server);
1089 }
1090
1091 static int accept_connection(const char* type, int fd,
1092 SocketAddress *addr, char **hostname) {
1093 int fd2, r;
1094
1095 log_debug("Accepting new %s connection on fd:%d", type, fd);
1096 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
1097 if (fd2 < 0)
1098 return log_error_errno(errno, "accept() on fd:%d failed: %m", fd);
1099
1100 switch(socket_address_family(addr)) {
1101 case AF_INET:
1102 case AF_INET6: {
1103 _cleanup_free_ char *a = NULL;
1104 char *b;
1105
1106 r = socket_address_print(addr, &a);
1107 if (r < 0) {
1108 log_error_errno(r, "socket_address_print(): %m");
1109 close(fd2);
1110 return r;
1111 }
1112
1113 r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
1114 if (r < 0) {
1115 log_error_errno(r, "Resolving hostname failed: %m");
1116 close(fd2);
1117 return r;
1118 }
1119
1120 log_debug("Accepted %s %s connection from %s",
1121 type,
1122 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
1123 a);
1124
1125 *hostname = b;
1126
1127 return fd2;
1128 };
1129 default:
1130 log_error("Rejected %s connection with unsupported family %d",
1131 type, socket_address_family(addr));
1132 close(fd2);
1133
1134 return -EINVAL;
1135 }
1136 }
1137
1138 static int dispatch_raw_connection_event(sd_event_source *event,
1139 int fd,
1140 uint32_t revents,
1141 void *userdata) {
1142 RemoteServer *s = userdata;
1143 int fd2;
1144 SocketAddress addr = {
1145 .size = sizeof(union sockaddr_union),
1146 .type = SOCK_STREAM,
1147 };
1148 char *hostname = NULL;
1149
1150 fd2 = accept_connection("raw", fd, &addr, &hostname);
1151 if (fd2 < 0)
1152 return fd2;
1153
1154 return add_source(s, fd2, hostname, true);
1155 }
1156
1157 /**********************************************************************
1158 **********************************************************************
1159 **********************************************************************/
1160
1161 static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
1162 [JOURNAL_WRITE_SPLIT_NONE] = "none",
1163 [JOURNAL_WRITE_SPLIT_HOST] = "host",
1164 };
1165
1166 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode);
1167 static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
1168 journal_write_split_mode,
1169 JournalWriteSplitMode,
1170 "Failed to parse split mode setting");
1171
1172 static int parse_config(void) {
1173 const ConfigTableItem items[] = {
1174 { "Remote", "SplitMode", config_parse_write_split_mode, 0, &arg_split_mode },
1175 { "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
1176 { "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
1177 { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
1178 {}};
1179
1180 return config_parse_many(PKGSYSCONFDIR "/journal-remote.conf",
1181 CONF_DIRS_NULSTR("systemd/journal-remote.conf"),
1182 "Remote\0", config_item_table_lookup, items,
1183 false, NULL);
1184 }
1185
1186 static void help(void) {
1187 printf("%s [OPTIONS...] {FILE|-}...\n\n"
1188 "Write external journal events to journal file(s).\n\n"
1189 " -h --help Show this help\n"
1190 " --version Show package version\n"
1191 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
1192 " --getter=COMMAND Read events from the output of COMMAND\n"
1193 " --listen-raw=ADDR Listen for connections at ADDR\n"
1194 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
1195 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
1196 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
1197 " --compress[=BOOL] XZ-compress the output journal (default: yes)\n"
1198 " --seal[=BOOL] Use event sealing (default: no)\n"
1199 " --key=FILENAME SSL key in PEM format (default:\n"
1200 " \"" PRIV_KEY_FILE "\")\n"
1201 " --cert=FILENAME SSL certificate in PEM format (default:\n"
1202 " \"" CERT_FILE "\")\n"
1203 " --trust=FILENAME|all SSL CA certificate or disable checking (default:\n"
1204 " \"" TRUST_FILE "\")\n"
1205 " --gnutls-log=CATEGORY...\n"
1206 " Specify a list of gnutls logging categories\n"
1207 " --split-mode=none|host How many output files to create\n"
1208 "\n"
1209 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
1210 , program_invocation_short_name);
1211 }
1212
1213 static int parse_argv(int argc, char *argv[]) {
1214 enum {
1215 ARG_VERSION = 0x100,
1216 ARG_URL,
1217 ARG_LISTEN_RAW,
1218 ARG_LISTEN_HTTP,
1219 ARG_LISTEN_HTTPS,
1220 ARG_GETTER,
1221 ARG_SPLIT_MODE,
1222 ARG_COMPRESS,
1223 ARG_SEAL,
1224 ARG_KEY,
1225 ARG_CERT,
1226 ARG_TRUST,
1227 ARG_GNUTLS_LOG,
1228 };
1229
1230 static const struct option options[] = {
1231 { "help", no_argument, NULL, 'h' },
1232 { "version", no_argument, NULL, ARG_VERSION },
1233 { "url", required_argument, NULL, ARG_URL },
1234 { "getter", required_argument, NULL, ARG_GETTER },
1235 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
1236 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1237 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1238 { "output", required_argument, NULL, 'o' },
1239 { "split-mode", required_argument, NULL, ARG_SPLIT_MODE },
1240 { "compress", optional_argument, NULL, ARG_COMPRESS },
1241 { "seal", optional_argument, NULL, ARG_SEAL },
1242 { "key", required_argument, NULL, ARG_KEY },
1243 { "cert", required_argument, NULL, ARG_CERT },
1244 { "trust", required_argument, NULL, ARG_TRUST },
1245 { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG },
1246 {}
1247 };
1248
1249 int c, r;
1250 bool type_a, type_b;
1251
1252 assert(argc >= 0);
1253 assert(argv);
1254
1255 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1256 switch(c) {
1257 case 'h':
1258 help();
1259 return 0 /* done */;
1260
1261 case ARG_VERSION:
1262 return version();
1263
1264 case ARG_URL:
1265 if (arg_url) {
1266 log_error("cannot currently set more than one --url");
1267 return -EINVAL;
1268 }
1269
1270 arg_url = optarg;
1271 break;
1272
1273 case ARG_GETTER:
1274 if (arg_getter) {
1275 log_error("cannot currently use --getter more than once");
1276 return -EINVAL;
1277 }
1278
1279 arg_getter = optarg;
1280 break;
1281
1282 case ARG_LISTEN_RAW:
1283 if (arg_listen_raw) {
1284 log_error("cannot currently use --listen-raw more than once");
1285 return -EINVAL;
1286 }
1287
1288 arg_listen_raw = optarg;
1289 break;
1290
1291 case ARG_LISTEN_HTTP:
1292 if (arg_listen_http || http_socket >= 0) {
1293 log_error("cannot currently use --listen-http more than once");
1294 return -EINVAL;
1295 }
1296
1297 r = negative_fd(optarg);
1298 if (r >= 0)
1299 http_socket = r;
1300 else
1301 arg_listen_http = optarg;
1302 break;
1303
1304 case ARG_LISTEN_HTTPS:
1305 if (arg_listen_https || https_socket >= 0) {
1306 log_error("cannot currently use --listen-https more than once");
1307 return -EINVAL;
1308 }
1309
1310 r = negative_fd(optarg);
1311 if (r >= 0)
1312 https_socket = r;
1313 else
1314 arg_listen_https = optarg;
1315
1316 break;
1317
1318 case ARG_KEY:
1319 if (arg_key) {
1320 log_error("Key file specified twice");
1321 return -EINVAL;
1322 }
1323
1324 arg_key = strdup(optarg);
1325 if (!arg_key)
1326 return log_oom();
1327
1328 break;
1329
1330 case ARG_CERT:
1331 if (arg_cert) {
1332 log_error("Certificate file specified twice");
1333 return -EINVAL;
1334 }
1335
1336 arg_cert = strdup(optarg);
1337 if (!arg_cert)
1338 return log_oom();
1339
1340 break;
1341
1342 case ARG_TRUST:
1343 if (arg_trust || arg_trust_all) {
1344 log_error("Confusing trusted CA configuration");
1345 return -EINVAL;
1346 }
1347
1348 if (streq(optarg, "all"))
1349 arg_trust_all = true;
1350 else {
1351 #ifdef HAVE_GNUTLS
1352 arg_trust = strdup(optarg);
1353 if (!arg_trust)
1354 return log_oom();
1355 #else
1356 log_error("Option --trust is not available.");
1357 return -EINVAL;
1358 #endif
1359 }
1360
1361 break;
1362
1363 case 'o':
1364 if (arg_output) {
1365 log_error("cannot use --output/-o more than once");
1366 return -EINVAL;
1367 }
1368
1369 arg_output = optarg;
1370 break;
1371
1372 case ARG_SPLIT_MODE:
1373 arg_split_mode = journal_write_split_mode_from_string(optarg);
1374 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
1375 log_error("Invalid split mode: %s", optarg);
1376 return -EINVAL;
1377 }
1378 break;
1379
1380 case ARG_COMPRESS:
1381 if (optarg) {
1382 r = parse_boolean(optarg);
1383 if (r < 0) {
1384 log_error("Failed to parse --compress= parameter.");
1385 return -EINVAL;
1386 }
1387
1388 arg_compress = !!r;
1389 } else
1390 arg_compress = true;
1391
1392 break;
1393
1394 case ARG_SEAL:
1395 if (optarg) {
1396 r = parse_boolean(optarg);
1397 if (r < 0) {
1398 log_error("Failed to parse --seal= parameter.");
1399 return -EINVAL;
1400 }
1401
1402 arg_seal = !!r;
1403 } else
1404 arg_seal = true;
1405
1406 break;
1407
1408 case ARG_GNUTLS_LOG: {
1409 #ifdef HAVE_GNUTLS
1410 const char *word, *state;
1411 size_t size;
1412
1413 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
1414 char *cat;
1415
1416 cat = strndup(word, size);
1417 if (!cat)
1418 return log_oom();
1419
1420 if (strv_consume(&arg_gnutls_log, cat) < 0)
1421 return log_oom();
1422 }
1423 break;
1424 #else
1425 log_error("Option --gnutls-log is not available.");
1426 return -EINVAL;
1427 #endif
1428 }
1429
1430 case '?':
1431 return -EINVAL;
1432
1433 default:
1434 assert_not_reached("Unknown option code.");
1435 }
1436
1437 if (optind < argc)
1438 arg_files = argv + optind;
1439
1440 type_a = arg_getter || !strv_isempty(arg_files);
1441 type_b = arg_url
1442 || arg_listen_raw
1443 || arg_listen_http || arg_listen_https
1444 || sd_listen_fds(false) > 0;
1445 if (type_a && type_b) {
1446 log_error("Cannot use file input or --getter with "
1447 "--arg-listen-... or socket activation.");
1448 return -EINVAL;
1449 }
1450 if (type_a) {
1451 if (!arg_output) {
1452 log_error("Option --output must be specified with file input or --getter.");
1453 return -EINVAL;
1454 }
1455
1456 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
1457 }
1458
1459 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE
1460 && arg_output && is_dir(arg_output, true) > 0) {
1461 log_error("For SplitMode=none, output must be a file.");
1462 return -EINVAL;
1463 }
1464
1465 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
1466 && arg_output && is_dir(arg_output, true) <= 0) {
1467 log_error("For SplitMode=host, output must be a directory.");
1468 return -EINVAL;
1469 }
1470
1471 log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1472 journal_write_split_mode_to_string(arg_split_mode),
1473 strna(arg_key),
1474 strna(arg_cert),
1475 strna(arg_trust));
1476
1477 return 1 /* work to do */;
1478 }
1479
1480 static int load_certificates(char **key, char **cert, char **trust) {
1481 int r;
1482
1483 r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL);
1484 if (r < 0)
1485 return log_error_errno(r, "Failed to read key from file '%s': %m",
1486 arg_key ?: PRIV_KEY_FILE);
1487
1488 r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
1489 if (r < 0)
1490 return log_error_errno(r, "Failed to read certificate from file '%s': %m",
1491 arg_cert ?: CERT_FILE);
1492
1493 if (arg_trust_all)
1494 log_info("Certificate checking disabled.");
1495 else {
1496 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
1497 if (r < 0)
1498 return log_error_errno(r, "Failed to read CA certificate file '%s': %m",
1499 arg_trust ?: TRUST_FILE);
1500 }
1501
1502 return 0;
1503 }
1504
1505 int main(int argc, char **argv) {
1506 RemoteServer s = {};
1507 int r;
1508 _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
1509
1510 log_show_color(true);
1511 log_parse_environment();
1512
1513 r = parse_config();
1514 if (r < 0)
1515 return EXIT_FAILURE;
1516
1517 r = parse_argv(argc, argv);
1518 if (r <= 0)
1519 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1520
1521
1522 if (arg_listen_http || arg_listen_https) {
1523 r = setup_gnutls_logger(arg_gnutls_log);
1524 if (r < 0)
1525 return EXIT_FAILURE;
1526 }
1527
1528 if (arg_listen_https || https_socket >= 0)
1529 if (load_certificates(&key, &cert, &trust) < 0)
1530 return EXIT_FAILURE;
1531
1532 if (remoteserver_init(&s, key, cert, trust) < 0)
1533 return EXIT_FAILURE;
1534
1535 r = sd_event_set_watchdog(s.events, true);
1536 if (r < 0)
1537 log_error_errno(r, "Failed to enable watchdog: %m");
1538 else
1539 log_debug("Watchdog is %s.", r > 0 ? "enabled" : "disabled");
1540
1541 log_debug("%s running as pid "PID_FMT,
1542 program_invocation_short_name, getpid());
1543 sd_notify(false,
1544 "READY=1\n"
1545 "STATUS=Processing requests...");
1546
1547 while (s.active) {
1548 r = sd_event_get_state(s.events);
1549 if (r < 0)
1550 break;
1551 if (r == SD_EVENT_FINISHED)
1552 break;
1553
1554 r = sd_event_run(s.events, -1);
1555 if (r < 0) {
1556 log_error_errno(r, "Failed to run event loop: %m");
1557 break;
1558 }
1559 }
1560
1561 sd_notifyf(false,
1562 "STOPPING=1\n"
1563 "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count);
1564 log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
1565
1566 server_destroy(&s);
1567
1568 free(arg_key);
1569 free(arg_cert);
1570 free(arg_trust);
1571
1572 return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1573 }