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