]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal-remote/journal-remote.c
Merge pull request #1419 from keszybz/shell-completion
[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_PEDANTIC_CHECKS |
651 MHD_USE_EPOLL_LINUX_ONLY |
652 MHD_USE_DUAL_STACK;
653
654 const union MHD_DaemonInfo *info;
655 int r, epoll_fd;
656 MHDDaemonWrapper *d;
657
658 assert(fd >= 0);
659
660 r = fd_nonblock(fd, true);
661 if (r < 0)
662 return log_error_errno(r, "Failed to make fd:%d nonblocking: %m", fd);
663
664 if (key) {
665 assert(cert);
666
667 opts[opts_pos++] = (struct MHD_OptionItem)
668 {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key};
669 opts[opts_pos++] = (struct MHD_OptionItem)
670 {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert};
671
672 flags |= MHD_USE_SSL;
673
674 if (trust)
675 opts[opts_pos++] = (struct MHD_OptionItem)
676 {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
677 }
678
679 d = new(MHDDaemonWrapper, 1);
680 if (!d)
681 return log_oom();
682
683 d->fd = (uint64_t) fd;
684
685 d->daemon = MHD_start_daemon(flags, 0,
686 NULL, NULL,
687 request_handler, NULL,
688 MHD_OPTION_ARRAY, opts,
689 MHD_OPTION_END);
690 if (!d->daemon) {
691 log_error("Failed to start µhttp daemon");
692 r = -EINVAL;
693 goto error;
694 }
695
696 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
697 key ? "HTTPS" : "HTTP", fd, d);
698
699
700 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
701 if (!info) {
702 log_error("µhttp returned NULL daemon info");
703 r = -EOPNOTSUPP;
704 goto error;
705 }
706
707 epoll_fd = info->listen_fd;
708 if (epoll_fd < 0) {
709 log_error("µhttp epoll fd is invalid");
710 r = -EUCLEAN;
711 goto error;
712 }
713
714 r = sd_event_add_io(s->events, &d->event,
715 epoll_fd, EPOLLIN,
716 dispatch_http_event, d);
717 if (r < 0) {
718 log_error_errno(r, "Failed to add event callback: %m");
719 goto error;
720 }
721
722 r = sd_event_source_set_description(d->event, "epoll-fd");
723 if (r < 0) {
724 log_error_errno(r, "Failed to set source name: %m");
725 goto error;
726 }
727
728 r = hashmap_ensure_allocated(&s->daemons, &uint64_hash_ops);
729 if (r < 0) {
730 log_oom();
731 goto error;
732 }
733
734 r = hashmap_put(s->daemons, &d->fd, d);
735 if (r < 0) {
736 log_error_errno(r, "Failed to add daemon to hashmap: %m");
737 goto error;
738 }
739
740 s->active ++;
741 return 0;
742
743 error:
744 MHD_stop_daemon(d->daemon);
745 free(d->daemon);
746 free(d);
747 return r;
748 }
749
750 static int setup_microhttpd_socket(RemoteServer *s,
751 const char *address,
752 const char *key,
753 const char *cert,
754 const char *trust) {
755 int fd;
756
757 fd = make_socket_fd(LOG_DEBUG, address, SOCK_STREAM | SOCK_CLOEXEC);
758 if (fd < 0)
759 return fd;
760
761 return setup_microhttpd_server(s, fd, key, cert, trust);
762 }
763
764 static int dispatch_http_event(sd_event_source *event,
765 int fd,
766 uint32_t revents,
767 void *userdata) {
768 MHDDaemonWrapper *d = userdata;
769 int r;
770
771 assert(d);
772
773 r = MHD_run(d->daemon);
774 if (r == MHD_NO) {
775 log_error("MHD_run failed!");
776 // XXX: unregister daemon
777 return -EINVAL;
778 }
779
780 return 1; /* work to do */
781 }
782
783 /**********************************************************************
784 **********************************************************************
785 **********************************************************************/
786
787 static int setup_signals(RemoteServer *s) {
788 int r;
789
790 assert(s);
791
792 assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0);
793
794 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, NULL, s);
795 if (r < 0)
796 return r;
797
798 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, NULL, s);
799 if (r < 0)
800 return r;
801
802 return 0;
803 }
804
805 static int negative_fd(const char *spec) {
806 /* Return a non-positive number as its inverse, -EINVAL otherwise. */
807
808 int fd, r;
809
810 r = safe_atoi(spec, &fd);
811 if (r < 0)
812 return r;
813
814 if (fd > 0)
815 return -EINVAL;
816 else
817 return -fd;
818 }
819
820 static int remoteserver_init(RemoteServer *s,
821 const char* key,
822 const char* cert,
823 const char* trust) {
824 int r, n, fd;
825 char **file;
826
827 assert(s);
828
829 if ((arg_listen_raw || arg_listen_http) && trust) {
830 log_error("Option --trust makes all non-HTTPS connections untrusted.");
831 return -EINVAL;
832 }
833
834 r = sd_event_default(&s->events);
835 if (r < 0)
836 return log_error_errno(r, "Failed to allocate event loop: %m");
837
838 setup_signals(s);
839
840 assert(server == NULL);
841 server = s;
842
843 r = init_writer_hashmap(s);
844 if (r < 0)
845 return r;
846
847 n = sd_listen_fds(true);
848 if (n < 0)
849 return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
850 else
851 log_debug("Received %d descriptors", n);
852
853 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
854 log_error("Received fewer sockets than expected");
855 return -EBADFD;
856 }
857
858 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
859 if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
860 log_debug("Received a listening socket (fd:%d)", fd);
861
862 if (fd == http_socket)
863 r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
864 else if (fd == https_socket)
865 r = setup_microhttpd_server(s, fd, key, cert, trust);
866 else
867 r = add_raw_socket(s, fd);
868 } else if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
869 char *hostname;
870
871 r = getnameinfo_pretty(fd, &hostname);
872 if (r < 0)
873 return log_error_errno(r, "Failed to retrieve remote name: %m");
874
875 log_debug("Received a connection socket (fd:%d) from %s", fd, hostname);
876
877 r = add_source(s, fd, hostname, true);
878 } else {
879 log_error("Unknown socket passed on fd:%d", fd);
880
881 return -EINVAL;
882 }
883
884 if (r < 0)
885 return log_error_errno(r, "Failed to register socket (fd:%d): %m",
886 fd);
887 }
888
889 if (arg_url) {
890 const char *url, *hostname;
891
892 url = strjoina(arg_url, "/entries");
893
894 if (arg_getter) {
895 log_info("Spawning getter %s...", url);
896 fd = spawn_getter(arg_getter, url);
897 } else {
898 log_info("Spawning curl %s...", url);
899 fd = spawn_curl(url);
900 }
901 if (fd < 0)
902 return fd;
903
904 hostname =
905 startswith(arg_url, "https://") ?:
906 startswith(arg_url, "http://") ?:
907 arg_url;
908
909 r = add_source(s, fd, (char*) hostname, false);
910 if (r < 0)
911 return r;
912 }
913
914 if (arg_listen_raw) {
915 log_debug("Listening on a socket...");
916 r = setup_raw_socket(s, arg_listen_raw);
917 if (r < 0)
918 return r;
919 }
920
921 if (arg_listen_http) {
922 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
923 if (r < 0)
924 return r;
925 }
926
927 if (arg_listen_https) {
928 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
929 if (r < 0)
930 return r;
931 }
932
933 STRV_FOREACH(file, arg_files) {
934 const char *output_name;
935
936 if (streq(*file, "-")) {
937 log_debug("Using standard input as source.");
938
939 fd = STDIN_FILENO;
940 output_name = "stdin";
941 } else {
942 log_debug("Reading file %s...", *file);
943
944 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
945 if (fd < 0)
946 return log_error_errno(errno, "Failed to open %s: %m", *file);
947 output_name = *file;
948 }
949
950 r = add_source(s, fd, (char*) output_name, false);
951 if (r < 0)
952 return r;
953 }
954
955 if (s->active == 0) {
956 log_error("Zarro sources specified");
957 return -EINVAL;
958 }
959
960 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
961 /* In this case we know what the writer will be
962 called, so we can create it and verify that we can
963 create output as expected. */
964 r = get_writer(s, NULL, &s->_single_writer);
965 if (r < 0)
966 return r;
967 }
968
969 return 0;
970 }
971
972 static void server_destroy(RemoteServer *s) {
973 size_t i;
974 MHDDaemonWrapper *d;
975
976 while ((d = hashmap_steal_first(s->daemons))) {
977 MHD_stop_daemon(d->daemon);
978 sd_event_source_unref(d->event);
979 free(d);
980 }
981
982 hashmap_free(s->daemons);
983
984 assert(s->sources_size == 0 || s->sources);
985 for (i = 0; i < s->sources_size; i++)
986 remove_source(s, i);
987 free(s->sources);
988
989 writer_unref(s->_single_writer);
990 hashmap_free(s->writers);
991
992 sd_event_source_unref(s->sigterm_event);
993 sd_event_source_unref(s->sigint_event);
994 sd_event_source_unref(s->listen_event);
995 sd_event_unref(s->events);
996
997 /* fds that we're listening on remain open... */
998 }
999
1000 /**********************************************************************
1001 **********************************************************************
1002 **********************************************************************/
1003
1004 static int handle_raw_source(sd_event_source *event,
1005 int fd,
1006 uint32_t revents,
1007 RemoteServer *s) {
1008
1009 RemoteSource *source;
1010 int r;
1011
1012 /* Returns 1 if there might be more data pending,
1013 * 0 if data is currently exhausted, negative on error.
1014 */
1015
1016 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
1017 source = s->sources[fd];
1018 assert(source->fd == fd);
1019
1020 r = process_source(source, arg_compress, arg_seal);
1021 if (source->state == STATE_EOF) {
1022 size_t remaining;
1023
1024 log_debug("EOF reached with source fd:%d (%s)",
1025 source->fd, source->name);
1026
1027 remaining = source_non_empty(source);
1028 if (remaining > 0)
1029 log_notice("Premature EOF. %zu bytes lost.", remaining);
1030 remove_source(s, source->fd);
1031 log_debug("%zu active sources remaining", s->active);
1032 return 0;
1033 } else if (r == -E2BIG) {
1034 log_notice_errno(E2BIG, "Entry too big, skipped");
1035 return 1;
1036 } else if (r == -EAGAIN) {
1037 return 0;
1038 } else if (r < 0) {
1039 log_debug_errno(r, "Closing connection: %m");
1040 remove_source(server, fd);
1041 return 0;
1042 } else
1043 return 1;
1044 }
1045
1046 static int dispatch_raw_source_until_block(sd_event_source *event,
1047 void *userdata) {
1048 RemoteSource *source = userdata;
1049 int r;
1050
1051 /* Make sure event stays around even if source is destroyed */
1052 sd_event_source_ref(event);
1053
1054 r = handle_raw_source(event, source->fd, EPOLLIN, server);
1055 if (r != 1)
1056 /* No more data for now */
1057 sd_event_source_set_enabled(event, SD_EVENT_OFF);
1058
1059 sd_event_source_unref(event);
1060
1061 return r;
1062 }
1063
1064 static int dispatch_raw_source_event(sd_event_source *event,
1065 int fd,
1066 uint32_t revents,
1067 void *userdata) {
1068 RemoteSource *source = userdata;
1069 int r;
1070
1071 assert(source->event);
1072 assert(source->buffer_event);
1073
1074 r = handle_raw_source(event, fd, EPOLLIN, server);
1075 if (r == 1)
1076 /* Might have more data. We need to rerun the handler
1077 * until we are sure the buffer is exhausted. */
1078 sd_event_source_set_enabled(source->buffer_event, SD_EVENT_ON);
1079
1080 return r;
1081 }
1082
1083 static int dispatch_blocking_source_event(sd_event_source *event,
1084 void *userdata) {
1085 RemoteSource *source = userdata;
1086
1087 return handle_raw_source(event, source->fd, EPOLLIN, server);
1088 }
1089
1090 static int accept_connection(const char* type, int fd,
1091 SocketAddress *addr, char **hostname) {
1092 int fd2, r;
1093
1094 log_debug("Accepting new %s connection on fd:%d", type, fd);
1095 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
1096 if (fd2 < 0)
1097 return log_error_errno(errno, "accept() on fd:%d failed: %m", fd);
1098
1099 switch(socket_address_family(addr)) {
1100 case AF_INET:
1101 case AF_INET6: {
1102 _cleanup_free_ char *a = NULL;
1103 char *b;
1104
1105 r = socket_address_print(addr, &a);
1106 if (r < 0) {
1107 log_error_errno(r, "socket_address_print(): %m");
1108 close(fd2);
1109 return r;
1110 }
1111
1112 r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
1113 if (r < 0) {
1114 log_error_errno(r, "Resolving hostname failed: %m");
1115 close(fd2);
1116 return r;
1117 }
1118
1119 log_debug("Accepted %s %s connection from %s",
1120 type,
1121 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
1122 a);
1123
1124 *hostname = b;
1125
1126 return fd2;
1127 };
1128 default:
1129 log_error("Rejected %s connection with unsupported family %d",
1130 type, socket_address_family(addr));
1131 close(fd2);
1132
1133 return -EINVAL;
1134 }
1135 }
1136
1137 static int dispatch_raw_connection_event(sd_event_source *event,
1138 int fd,
1139 uint32_t revents,
1140 void *userdata) {
1141 RemoteServer *s = userdata;
1142 int fd2;
1143 SocketAddress addr = {
1144 .size = sizeof(union sockaddr_union),
1145 .type = SOCK_STREAM,
1146 };
1147 char *hostname = NULL;
1148
1149 fd2 = accept_connection("raw", fd, &addr, &hostname);
1150 if (fd2 < 0)
1151 return fd2;
1152
1153 return add_source(s, fd2, hostname, true);
1154 }
1155
1156 /**********************************************************************
1157 **********************************************************************
1158 **********************************************************************/
1159
1160 static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
1161 [JOURNAL_WRITE_SPLIT_NONE] = "none",
1162 [JOURNAL_WRITE_SPLIT_HOST] = "host",
1163 };
1164
1165 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode);
1166 static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
1167 journal_write_split_mode,
1168 JournalWriteSplitMode,
1169 "Failed to parse split mode setting");
1170
1171 static int parse_config(void) {
1172 const ConfigTableItem items[] = {
1173 { "Remote", "SplitMode", config_parse_write_split_mode, 0, &arg_split_mode },
1174 { "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
1175 { "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
1176 { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
1177 {}};
1178
1179 return config_parse_many(PKGSYSCONFDIR "/journal-remote.conf",
1180 CONF_DIRS_NULSTR("systemd/journal-remote.conf"),
1181 "Remote\0", config_item_table_lookup, items,
1182 false, NULL);
1183 }
1184
1185 static void help(void) {
1186 printf("%s [OPTIONS...] {FILE|-}...\n\n"
1187 "Write external journal events to journal file(s).\n\n"
1188 " -h --help Show this help\n"
1189 " --version Show package version\n"
1190 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
1191 " --getter=COMMAND Read events from the output of COMMAND\n"
1192 " --listen-raw=ADDR Listen for connections at ADDR\n"
1193 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
1194 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
1195 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
1196 " --compress[=BOOL] XZ-compress the output journal (default: yes)\n"
1197 " --seal[=BOOL] Use event sealing (default: no)\n"
1198 " --key=FILENAME SSL key in PEM format (default:\n"
1199 " \"" PRIV_KEY_FILE "\")\n"
1200 " --cert=FILENAME SSL certificate in PEM format (default:\n"
1201 " \"" CERT_FILE "\")\n"
1202 " --trust=FILENAME|all SSL CA certificate or disable checking (default:\n"
1203 " \"" TRUST_FILE "\")\n"
1204 " --gnutls-log=CATEGORY...\n"
1205 " Specify a list of gnutls logging categories\n"
1206 " --split-mode=none|host How many output files to create\n"
1207 "\n"
1208 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
1209 , program_invocation_short_name);
1210 }
1211
1212 static int parse_argv(int argc, char *argv[]) {
1213 enum {
1214 ARG_VERSION = 0x100,
1215 ARG_URL,
1216 ARG_LISTEN_RAW,
1217 ARG_LISTEN_HTTP,
1218 ARG_LISTEN_HTTPS,
1219 ARG_GETTER,
1220 ARG_SPLIT_MODE,
1221 ARG_COMPRESS,
1222 ARG_SEAL,
1223 ARG_KEY,
1224 ARG_CERT,
1225 ARG_TRUST,
1226 ARG_GNUTLS_LOG,
1227 };
1228
1229 static const struct option options[] = {
1230 { "help", no_argument, NULL, 'h' },
1231 { "version", no_argument, NULL, ARG_VERSION },
1232 { "url", required_argument, NULL, ARG_URL },
1233 { "getter", required_argument, NULL, ARG_GETTER },
1234 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
1235 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1236 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1237 { "output", required_argument, NULL, 'o' },
1238 { "split-mode", required_argument, NULL, ARG_SPLIT_MODE },
1239 { "compress", optional_argument, NULL, ARG_COMPRESS },
1240 { "seal", optional_argument, NULL, ARG_SEAL },
1241 { "key", required_argument, NULL, ARG_KEY },
1242 { "cert", required_argument, NULL, ARG_CERT },
1243 { "trust", required_argument, NULL, ARG_TRUST },
1244 { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG },
1245 {}
1246 };
1247
1248 int c, r;
1249 bool type_a, type_b;
1250
1251 assert(argc >= 0);
1252 assert(argv);
1253
1254 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1255 switch(c) {
1256 case 'h':
1257 help();
1258 return 0 /* done */;
1259
1260 case ARG_VERSION:
1261 return version();
1262
1263 case ARG_URL:
1264 if (arg_url) {
1265 log_error("cannot currently set more than one --url");
1266 return -EINVAL;
1267 }
1268
1269 arg_url = optarg;
1270 break;
1271
1272 case ARG_GETTER:
1273 if (arg_getter) {
1274 log_error("cannot currently use --getter more than once");
1275 return -EINVAL;
1276 }
1277
1278 arg_getter = optarg;
1279 break;
1280
1281 case ARG_LISTEN_RAW:
1282 if (arg_listen_raw) {
1283 log_error("cannot currently use --listen-raw more than once");
1284 return -EINVAL;
1285 }
1286
1287 arg_listen_raw = optarg;
1288 break;
1289
1290 case ARG_LISTEN_HTTP:
1291 if (arg_listen_http || http_socket >= 0) {
1292 log_error("cannot currently use --listen-http more than once");
1293 return -EINVAL;
1294 }
1295
1296 r = negative_fd(optarg);
1297 if (r >= 0)
1298 http_socket = r;
1299 else
1300 arg_listen_http = optarg;
1301 break;
1302
1303 case ARG_LISTEN_HTTPS:
1304 if (arg_listen_https || https_socket >= 0) {
1305 log_error("cannot currently use --listen-https more than once");
1306 return -EINVAL;
1307 }
1308
1309 r = negative_fd(optarg);
1310 if (r >= 0)
1311 https_socket = r;
1312 else
1313 arg_listen_https = optarg;
1314
1315 break;
1316
1317 case ARG_KEY:
1318 if (arg_key) {
1319 log_error("Key file specified twice");
1320 return -EINVAL;
1321 }
1322
1323 arg_key = strdup(optarg);
1324 if (!arg_key)
1325 return log_oom();
1326
1327 break;
1328
1329 case ARG_CERT:
1330 if (arg_cert) {
1331 log_error("Certificate file specified twice");
1332 return -EINVAL;
1333 }
1334
1335 arg_cert = strdup(optarg);
1336 if (!arg_cert)
1337 return log_oom();
1338
1339 break;
1340
1341 case ARG_TRUST:
1342 if (arg_trust || arg_trust_all) {
1343 log_error("Confusing trusted CA configuration");
1344 return -EINVAL;
1345 }
1346
1347 if (streq(optarg, "all"))
1348 arg_trust_all = true;
1349 else {
1350 #ifdef HAVE_GNUTLS
1351 arg_trust = strdup(optarg);
1352 if (!arg_trust)
1353 return log_oom();
1354 #else
1355 log_error("Option --trust is not available.");
1356 return -EINVAL;
1357 #endif
1358 }
1359
1360 break;
1361
1362 case 'o':
1363 if (arg_output) {
1364 log_error("cannot use --output/-o more than once");
1365 return -EINVAL;
1366 }
1367
1368 arg_output = optarg;
1369 break;
1370
1371 case ARG_SPLIT_MODE:
1372 arg_split_mode = journal_write_split_mode_from_string(optarg);
1373 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
1374 log_error("Invalid split mode: %s", optarg);
1375 return -EINVAL;
1376 }
1377 break;
1378
1379 case ARG_COMPRESS:
1380 if (optarg) {
1381 r = parse_boolean(optarg);
1382 if (r < 0) {
1383 log_error("Failed to parse --compress= parameter.");
1384 return -EINVAL;
1385 }
1386
1387 arg_compress = !!r;
1388 } else
1389 arg_compress = true;
1390
1391 break;
1392
1393 case ARG_SEAL:
1394 if (optarg) {
1395 r = parse_boolean(optarg);
1396 if (r < 0) {
1397 log_error("Failed to parse --seal= parameter.");
1398 return -EINVAL;
1399 }
1400
1401 arg_seal = !!r;
1402 } else
1403 arg_seal = true;
1404
1405 break;
1406
1407 case ARG_GNUTLS_LOG: {
1408 #ifdef HAVE_GNUTLS
1409 const char *word, *state;
1410 size_t size;
1411
1412 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
1413 char *cat;
1414
1415 cat = strndup(word, size);
1416 if (!cat)
1417 return log_oom();
1418
1419 if (strv_consume(&arg_gnutls_log, cat) < 0)
1420 return log_oom();
1421 }
1422 break;
1423 #else
1424 log_error("Option --gnutls-log is not available.");
1425 return -EINVAL;
1426 #endif
1427 }
1428
1429 case '?':
1430 return -EINVAL;
1431
1432 default:
1433 assert_not_reached("Unknown option code.");
1434 }
1435
1436 if (optind < argc)
1437 arg_files = argv + optind;
1438
1439 type_a = arg_getter || !strv_isempty(arg_files);
1440 type_b = arg_url
1441 || arg_listen_raw
1442 || arg_listen_http || arg_listen_https
1443 || sd_listen_fds(false) > 0;
1444 if (type_a && type_b) {
1445 log_error("Cannot use file input or --getter with "
1446 "--arg-listen-... or socket activation.");
1447 return -EINVAL;
1448 }
1449 if (type_a) {
1450 if (!arg_output) {
1451 log_error("Option --output must be specified with file input or --getter.");
1452 return -EINVAL;
1453 }
1454
1455 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
1456 }
1457
1458 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE
1459 && arg_output && is_dir(arg_output, true) > 0) {
1460 log_error("For SplitMode=none, output must be a file.");
1461 return -EINVAL;
1462 }
1463
1464 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
1465 && arg_output && is_dir(arg_output, true) <= 0) {
1466 log_error("For SplitMode=host, output must be a directory.");
1467 return -EINVAL;
1468 }
1469
1470 log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1471 journal_write_split_mode_to_string(arg_split_mode),
1472 strna(arg_key),
1473 strna(arg_cert),
1474 strna(arg_trust));
1475
1476 return 1 /* work to do */;
1477 }
1478
1479 static int load_certificates(char **key, char **cert, char **trust) {
1480 int r;
1481
1482 r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL);
1483 if (r < 0)
1484 return log_error_errno(r, "Failed to read key from file '%s': %m",
1485 arg_key ?: PRIV_KEY_FILE);
1486
1487 r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
1488 if (r < 0)
1489 return log_error_errno(r, "Failed to read certificate from file '%s': %m",
1490 arg_cert ?: CERT_FILE);
1491
1492 if (arg_trust_all)
1493 log_info("Certificate checking disabled.");
1494 else {
1495 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
1496 if (r < 0)
1497 return log_error_errno(r, "Failed to read CA certificate file '%s': %m",
1498 arg_trust ?: TRUST_FILE);
1499 }
1500
1501 return 0;
1502 }
1503
1504 int main(int argc, char **argv) {
1505 RemoteServer s = {};
1506 int r;
1507 _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
1508
1509 log_show_color(true);
1510 log_parse_environment();
1511
1512 r = parse_config();
1513 if (r < 0)
1514 return EXIT_FAILURE;
1515
1516 r = parse_argv(argc, argv);
1517 if (r <= 0)
1518 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1519
1520
1521 if (arg_listen_http || arg_listen_https) {
1522 r = setup_gnutls_logger(arg_gnutls_log);
1523 if (r < 0)
1524 return EXIT_FAILURE;
1525 }
1526
1527 if (arg_listen_https || https_socket >= 0)
1528 if (load_certificates(&key, &cert, &trust) < 0)
1529 return EXIT_FAILURE;
1530
1531 if (remoteserver_init(&s, key, cert, trust) < 0)
1532 return EXIT_FAILURE;
1533
1534 r = sd_event_set_watchdog(s.events, true);
1535 if (r < 0)
1536 log_error_errno(r, "Failed to enable watchdog: %m");
1537 else
1538 log_debug("Watchdog is %s.", r > 0 ? "enabled" : "disabled");
1539
1540 log_debug("%s running as pid "PID_FMT,
1541 program_invocation_short_name, getpid());
1542 sd_notify(false,
1543 "READY=1\n"
1544 "STATUS=Processing requests...");
1545
1546 while (s.active) {
1547 r = sd_event_get_state(s.events);
1548 if (r < 0)
1549 break;
1550 if (r == SD_EVENT_FINISHED)
1551 break;
1552
1553 r = sd_event_run(s.events, -1);
1554 if (r < 0) {
1555 log_error_errno(r, "Failed to run event loop: %m");
1556 break;
1557 }
1558 }
1559
1560 sd_notifyf(false,
1561 "STOPPING=1\n"
1562 "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count);
1563 log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
1564
1565 server_destroy(&s);
1566
1567 free(arg_key);
1568 free(arg_cert);
1569 free(arg_trust);
1570
1571 return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1572 }