]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal-remote/journal-remote.c
tree-wide: update empty-if coccinelle script to cover empty-while and more
[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 = log_error_errno(errno, "Failed to fork: %m");
92 safe_close_pair(fd);
93 return r;
94 }
95
96 /* In the child */
97 if (child_pid == 0) {
98
99 (void) reset_all_signal_handlers();
100 (void) reset_signal_mask();
101
102 r = dup2(fd[1], STDOUT_FILENO);
103 if (r < 0) {
104 log_error_errno(errno, "Failed to dup pipe to stdout: %m");
105 _exit(EXIT_FAILURE);
106 }
107
108 safe_close_pair(fd);
109
110 /* Make sure the child goes away when the parent dies */
111 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
112 _exit(EXIT_FAILURE);
113
114 /* Check whether our parent died before we were able
115 * to set the death signal */
116 if (getppid() != parent_pid)
117 _exit(EXIT_SUCCESS);
118
119 execvp(child, argv);
120 log_error_errno(errno, "Failed to exec child %s: %m", child);
121 _exit(EXIT_FAILURE);
122 }
123
124 r = close(fd[1]);
125 if (r < 0)
126 log_warning_errno(errno, "Failed to close write end of pipe: %m");
127
128 return fd[0];
129 }
130
131 static int spawn_curl(const char* url) {
132 char **argv = STRV_MAKE("curl",
133 "-HAccept: application/vnd.fdo.journal",
134 "--silent",
135 "--show-error",
136 url);
137 int r;
138
139 r = spawn_child("curl", argv);
140 if (r < 0)
141 log_error_errno(errno, "Failed to spawn curl: %m");
142 return r;
143 }
144
145 static int spawn_getter(const char *getter, const char *url) {
146 int r;
147 _cleanup_strv_free_ char **words = NULL;
148
149 assert(getter);
150 r = strv_split_extract(&words, getter, WHITESPACE, EXTRACT_QUOTES);
151 if (r < 0)
152 return log_error_errno(r, "Failed to split getter option: %m");
153
154 r = strv_extend(&words, url);
155 if (r < 0)
156 return log_error_errno(r, "Failed to create command line: %m");
157
158 r = spawn_child(words[0], words);
159 if (r < 0)
160 log_error_errno(errno, "Failed to spawn getter %s: %m", getter);
161
162 return r;
163 }
164
165 #define filename_escape(s) xescape((s), "/ ")
166
167 static int open_output(Writer *w, const char* host) {
168 _cleanup_free_ char *_output = NULL;
169 const char *output;
170 int r;
171
172 switch (arg_split_mode) {
173 case JOURNAL_WRITE_SPLIT_NONE:
174 output = arg_output ?: REMOTE_JOURNAL_PATH "/remote.journal";
175 break;
176
177 case JOURNAL_WRITE_SPLIT_HOST: {
178 _cleanup_free_ char *name;
179
180 assert(host);
181
182 name = filename_escape(host);
183 if (!name)
184 return log_oom();
185
186 r = asprintf(&_output, "%s/remote-%s.journal",
187 arg_output ?: REMOTE_JOURNAL_PATH,
188 name);
189 if (r < 0)
190 return log_oom();
191
192 output = _output;
193 break;
194 }
195
196 default:
197 assert_not_reached("what?");
198 }
199
200 r = journal_file_open_reliably(output,
201 O_RDWR|O_CREAT, 0640,
202 arg_compress, arg_seal,
203 &w->metrics,
204 w->mmap,
205 NULL, &w->journal);
206 if (r < 0)
207 log_error_errno(r, "Failed to open output journal %s: %m",
208 output);
209 else
210 log_debug("Opened output file %s", w->journal->path);
211 return r;
212 }
213
214 /**********************************************************************
215 **********************************************************************
216 **********************************************************************/
217
218 static int init_writer_hashmap(RemoteServer *s) {
219 static const struct hash_ops *hash_ops[] = {
220 [JOURNAL_WRITE_SPLIT_NONE] = NULL,
221 [JOURNAL_WRITE_SPLIT_HOST] = &string_hash_ops,
222 };
223
224 assert(arg_split_mode >= 0 && arg_split_mode < (int) ELEMENTSOF(hash_ops));
225
226 s->writers = hashmap_new(hash_ops[arg_split_mode]);
227 if (!s->writers)
228 return log_oom();
229
230 return 0;
231 }
232
233 static int get_writer(RemoteServer *s, const char *host,
234 Writer **writer) {
235 const void *key;
236 _cleanup_writer_unref_ Writer *w = NULL;
237 int r;
238
239 switch(arg_split_mode) {
240 case JOURNAL_WRITE_SPLIT_NONE:
241 key = "one and only";
242 break;
243
244 case JOURNAL_WRITE_SPLIT_HOST:
245 assert(host);
246 key = host;
247 break;
248
249 default:
250 assert_not_reached("what split mode?");
251 }
252
253 w = hashmap_get(s->writers, key);
254 if (w)
255 writer_ref(w);
256 else {
257 w = writer_new(s);
258 if (!w)
259 return log_oom();
260
261 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST) {
262 w->hashmap_key = strdup(key);
263 if (!w->hashmap_key)
264 return log_oom();
265 }
266
267 r = open_output(w, host);
268 if (r < 0)
269 return r;
270
271 r = hashmap_put(s->writers, w->hashmap_key ?: key, w);
272 if (r < 0)
273 return r;
274 }
275
276 *writer = w;
277 w = NULL;
278 return 0;
279 }
280
281 /**********************************************************************
282 **********************************************************************
283 **********************************************************************/
284
285 /* This should go away as soon as µhttpd allows state to be passed around. */
286 static RemoteServer *server;
287
288 static int dispatch_raw_source_event(sd_event_source *event,
289 int fd,
290 uint32_t revents,
291 void *userdata);
292 static int dispatch_raw_source_until_block(sd_event_source *event,
293 void *userdata);
294 static int dispatch_blocking_source_event(sd_event_source *event,
295 void *userdata);
296 static int dispatch_raw_connection_event(sd_event_source *event,
297 int fd,
298 uint32_t revents,
299 void *userdata);
300 static int dispatch_http_event(sd_event_source *event,
301 int fd,
302 uint32_t revents,
303 void *userdata);
304
305 static int get_source_for_fd(RemoteServer *s,
306 int fd, char *name, RemoteSource **source) {
307 Writer *writer;
308 int r;
309
310 /* This takes ownership of name, but only on success. */
311
312 assert(fd >= 0);
313 assert(source);
314
315 if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
316 return log_oom();
317
318 r = get_writer(s, name, &writer);
319 if (r < 0)
320 return log_warning_errno(r, "Failed to get writer for source %s: %m",
321 name);
322
323 if (s->sources[fd] == NULL) {
324 s->sources[fd] = source_new(fd, false, name, writer);
325 if (!s->sources[fd]) {
326 writer_unref(writer);
327 return log_oom();
328 }
329
330 s->active++;
331 }
332
333 *source = s->sources[fd];
334 return 0;
335 }
336
337 static int remove_source(RemoteServer *s, int fd) {
338 RemoteSource *source;
339
340 assert(s);
341 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
342
343 source = s->sources[fd];
344 if (source) {
345 /* this closes fd too */
346 source_free(source);
347 s->sources[fd] = NULL;
348 s->active--;
349 }
350
351 return 0;
352 }
353
354 static int add_source(RemoteServer *s, int fd, char* name, bool own_name) {
355
356 RemoteSource *source = NULL;
357 int r;
358
359 /* This takes ownership of name, even on failure, if own_name is true. */
360
361 assert(s);
362 assert(fd >= 0);
363 assert(name);
364
365 if (!own_name) {
366 name = strdup(name);
367 if (!name)
368 return log_oom();
369 }
370
371 r = get_source_for_fd(s, fd, name, &source);
372 if (r < 0) {
373 log_error_errno(r, "Failed to create source for fd:%d (%s): %m",
374 fd, name);
375 free(name);
376 return r;
377 }
378
379 r = sd_event_add_io(s->events, &source->event,
380 fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI,
381 dispatch_raw_source_event, source);
382 if (r == 0) {
383 /* Add additional source for buffer processing. It will be
384 * enabled later. */
385 r = sd_event_add_defer(s->events, &source->buffer_event,
386 dispatch_raw_source_until_block, source);
387 if (r == 0)
388 sd_event_source_set_enabled(source->buffer_event, SD_EVENT_OFF);
389 } else if (r == -EPERM) {
390 log_debug("Falling back to sd_event_add_defer for fd:%d (%s)", fd, name);
391 r = sd_event_add_defer(s->events, &source->event,
392 dispatch_blocking_source_event, source);
393 if (r == 0)
394 sd_event_source_set_enabled(source->event, SD_EVENT_ON);
395 }
396 if (r < 0) {
397 log_error_errno(r, "Failed to register event source for fd:%d: %m",
398 fd);
399 goto error;
400 }
401
402 r = sd_event_source_set_description(source->event, name);
403 if (r < 0) {
404 log_error_errno(r, "Failed to set source name for fd:%d: %m", fd);
405 goto error;
406 }
407
408 return 1; /* work to do */
409
410 error:
411 remove_source(s, fd);
412 return r;
413 }
414
415 static int add_raw_socket(RemoteServer *s, int fd) {
416 int r;
417 _cleanup_close_ int fd_ = fd;
418 char name[sizeof("raw-socket-")-1 + DECIMAL_STR_MAX(int) + 1];
419
420 assert(fd >= 0);
421
422 r = sd_event_add_io(s->events, &s->listen_event,
423 fd, EPOLLIN,
424 dispatch_raw_connection_event, s);
425 if (r < 0)
426 return r;
427
428 xsprintf(name, "raw-socket-%d", fd);
429
430 r = sd_event_source_set_description(s->listen_event, name);
431 if (r < 0)
432 return r;
433
434 fd_ = -1;
435 s->active ++;
436 return 0;
437 }
438
439 static int setup_raw_socket(RemoteServer *s, const char *address) {
440 int fd;
441
442 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
443 if (fd < 0)
444 return fd;
445
446 return add_raw_socket(s, fd);
447 }
448
449 /**********************************************************************
450 **********************************************************************
451 **********************************************************************/
452
453 static int request_meta(void **connection_cls, int fd, char *hostname) {
454 RemoteSource *source;
455 Writer *writer;
456 int r;
457
458 assert(connection_cls);
459 if (*connection_cls)
460 return 0;
461
462 r = get_writer(server, hostname, &writer);
463 if (r < 0)
464 return log_warning_errno(r, "Failed to get writer for source %s: %m",
465 hostname);
466
467 source = source_new(fd, true, hostname, writer);
468 if (!source) {
469 writer_unref(writer);
470 return log_oom();
471 }
472
473 log_debug("Added RemoteSource as connection metadata %p", source);
474
475 *connection_cls = source;
476 return 0;
477 }
478
479 static void request_meta_free(void *cls,
480 struct MHD_Connection *connection,
481 void **connection_cls,
482 enum MHD_RequestTerminationCode toe) {
483 RemoteSource *s;
484
485 assert(connection_cls);
486 s = *connection_cls;
487
488 if (s) {
489 log_debug("Cleaning up connection metadata %p", s);
490 source_free(s);
491 *connection_cls = NULL;
492 }
493 }
494
495 static int process_http_upload(
496 struct MHD_Connection *connection,
497 const char *upload_data,
498 size_t *upload_data_size,
499 RemoteSource *source) {
500
501 bool finished = false;
502 size_t remaining;
503 int r;
504
505 assert(source);
506
507 log_trace("%s: connection %p, %zu bytes",
508 __func__, connection, *upload_data_size);
509
510 if (*upload_data_size) {
511 log_trace("Received %zu bytes", *upload_data_size);
512
513 r = push_data(source, upload_data, *upload_data_size);
514 if (r < 0)
515 return mhd_respond_oom(connection);
516
517 *upload_data_size = 0;
518 } else
519 finished = true;
520
521 while (true) {
522 r = process_source(source, arg_compress, arg_seal);
523 if (r == -EAGAIN)
524 break;
525 else if (r < 0) {
526 log_warning("Failed to process data for connection %p", connection);
527 if (r == -E2BIG)
528 return mhd_respondf(connection,
529 MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
530 "Entry is too large, maximum is %u bytes.\n",
531 DATA_SIZE_MAX);
532 else
533 return mhd_respondf(connection,
534 MHD_HTTP_UNPROCESSABLE_ENTITY,
535 "Processing failed: %s.", strerror(-r));
536 }
537 }
538
539 if (!finished)
540 return MHD_YES;
541
542 /* The upload is finished */
543
544 remaining = source_non_empty(source);
545 if (remaining > 0) {
546 log_warning("Premature EOFbyte. %zu bytes lost.", remaining);
547 return mhd_respondf(connection, MHD_HTTP_EXPECTATION_FAILED,
548 "Premature EOF. %zu bytes of trailing data not processed.",
549 remaining);
550 }
551
552 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
553 };
554
555 static int request_handler(
556 void *cls,
557 struct MHD_Connection *connection,
558 const char *url,
559 const char *method,
560 const char *version,
561 const char *upload_data,
562 size_t *upload_data_size,
563 void **connection_cls) {
564
565 const char *header;
566 int r, code, fd;
567 _cleanup_free_ char *hostname = NULL;
568
569 assert(connection);
570 assert(connection_cls);
571 assert(url);
572 assert(method);
573
574 log_trace("Handling a connection %s %s %s", method, url, version);
575
576 if (*connection_cls)
577 return process_http_upload(connection,
578 upload_data, upload_data_size,
579 *connection_cls);
580
581 if (!streq(method, "POST"))
582 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
583 "Unsupported method.\n");
584
585 if (!streq(url, "/upload"))
586 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
587 "Not found.\n");
588
589 header = MHD_lookup_connection_value(connection,
590 MHD_HEADER_KIND, "Content-Type");
591 if (!header || !streq(header, "application/vnd.fdo.journal"))
592 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
593 "Content-Type: application/vnd.fdo.journal"
594 " is required.\n");
595
596 {
597 const union MHD_ConnectionInfo *ci;
598
599 ci = MHD_get_connection_info(connection,
600 MHD_CONNECTION_INFO_CONNECTION_FD);
601 if (!ci) {
602 log_error("MHD_get_connection_info failed: cannot get remote fd");
603 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
604 "Cannot check remote address");
605 }
606
607 fd = ci->connect_fd;
608 assert(fd >= 0);
609 }
610
611 if (server->check_trust) {
612 r = check_permissions(connection, &code, &hostname);
613 if (r < 0)
614 return code;
615 } else {
616 r = getnameinfo_pretty(fd, &hostname);
617 if (r < 0)
618 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
619 "Cannot check remote hostname");
620 }
621
622 assert(hostname);
623
624 r = request_meta(connection_cls, fd, hostname);
625 if (r == -ENOMEM)
626 return respond_oom(connection);
627 else if (r < 0)
628 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
629 strerror(-r));
630
631 hostname = NULL;
632 return MHD_YES;
633 }
634
635 static int setup_microhttpd_server(RemoteServer *s,
636 int fd,
637 const char *key,
638 const char *cert,
639 const char *trust) {
640 struct MHD_OptionItem opts[] = {
641 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
642 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
643 { MHD_OPTION_LISTEN_SOCKET, fd},
644 { MHD_OPTION_END},
645 { MHD_OPTION_END},
646 { MHD_OPTION_END},
647 { MHD_OPTION_END}};
648 int opts_pos = 3;
649 int flags =
650 MHD_USE_DEBUG |
651 MHD_USE_PEDANTIC_CHECKS |
652 MHD_USE_EPOLL_LINUX_ONLY |
653 MHD_USE_DUAL_STACK;
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("Zarro 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 puts(PACKAGE_STRING);
1263 puts(SYSTEMD_FEATURES);
1264 return 0 /* done */;
1265
1266 case ARG_URL:
1267 if (arg_url) {
1268 log_error("cannot currently set more than one --url");
1269 return -EINVAL;
1270 }
1271
1272 arg_url = optarg;
1273 break;
1274
1275 case ARG_GETTER:
1276 if (arg_getter) {
1277 log_error("cannot currently use --getter more than once");
1278 return -EINVAL;
1279 }
1280
1281 arg_getter = optarg;
1282 break;
1283
1284 case ARG_LISTEN_RAW:
1285 if (arg_listen_raw) {
1286 log_error("cannot currently use --listen-raw more than once");
1287 return -EINVAL;
1288 }
1289
1290 arg_listen_raw = optarg;
1291 break;
1292
1293 case ARG_LISTEN_HTTP:
1294 if (arg_listen_http || http_socket >= 0) {
1295 log_error("cannot currently use --listen-http more than once");
1296 return -EINVAL;
1297 }
1298
1299 r = negative_fd(optarg);
1300 if (r >= 0)
1301 http_socket = r;
1302 else
1303 arg_listen_http = optarg;
1304 break;
1305
1306 case ARG_LISTEN_HTTPS:
1307 if (arg_listen_https || https_socket >= 0) {
1308 log_error("cannot currently use --listen-https more than once");
1309 return -EINVAL;
1310 }
1311
1312 r = negative_fd(optarg);
1313 if (r >= 0)
1314 https_socket = r;
1315 else
1316 arg_listen_https = optarg;
1317
1318 break;
1319
1320 case ARG_KEY:
1321 if (arg_key) {
1322 log_error("Key file specified twice");
1323 return -EINVAL;
1324 }
1325
1326 arg_key = strdup(optarg);
1327 if (!arg_key)
1328 return log_oom();
1329
1330 break;
1331
1332 case ARG_CERT:
1333 if (arg_cert) {
1334 log_error("Certificate file specified twice");
1335 return -EINVAL;
1336 }
1337
1338 arg_cert = strdup(optarg);
1339 if (!arg_cert)
1340 return log_oom();
1341
1342 break;
1343
1344 case ARG_TRUST:
1345 if (arg_trust || arg_trust_all) {
1346 log_error("Confusing trusted CA configuration");
1347 return -EINVAL;
1348 }
1349
1350 if (streq(optarg, "all"))
1351 arg_trust_all = true;
1352 else {
1353 #ifdef HAVE_GNUTLS
1354 arg_trust = strdup(optarg);
1355 if (!arg_trust)
1356 return log_oom();
1357 #else
1358 log_error("Option --trust is not available.");
1359 return -EINVAL;
1360 #endif
1361 }
1362
1363 break;
1364
1365 case 'o':
1366 if (arg_output) {
1367 log_error("cannot use --output/-o more than once");
1368 return -EINVAL;
1369 }
1370
1371 arg_output = optarg;
1372 break;
1373
1374 case ARG_SPLIT_MODE:
1375 arg_split_mode = journal_write_split_mode_from_string(optarg);
1376 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
1377 log_error("Invalid split mode: %s", optarg);
1378 return -EINVAL;
1379 }
1380 break;
1381
1382 case ARG_COMPRESS:
1383 if (optarg) {
1384 r = parse_boolean(optarg);
1385 if (r < 0) {
1386 log_error("Failed to parse --compress= parameter.");
1387 return -EINVAL;
1388 }
1389
1390 arg_compress = !!r;
1391 } else
1392 arg_compress = true;
1393
1394 break;
1395
1396 case ARG_SEAL:
1397 if (optarg) {
1398 r = parse_boolean(optarg);
1399 if (r < 0) {
1400 log_error("Failed to parse --seal= parameter.");
1401 return -EINVAL;
1402 }
1403
1404 arg_seal = !!r;
1405 } else
1406 arg_seal = true;
1407
1408 break;
1409
1410 case ARG_GNUTLS_LOG: {
1411 #ifdef HAVE_GNUTLS
1412 const char *word, *state;
1413 size_t size;
1414
1415 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
1416 char *cat;
1417
1418 cat = strndup(word, size);
1419 if (!cat)
1420 return log_oom();
1421
1422 if (strv_consume(&arg_gnutls_log, cat) < 0)
1423 return log_oom();
1424 }
1425 break;
1426 #else
1427 log_error("Option --gnutls-log is not available.");
1428 return -EINVAL;
1429 #endif
1430 }
1431
1432 case '?':
1433 return -EINVAL;
1434
1435 default:
1436 assert_not_reached("Unknown option code.");
1437 }
1438
1439 if (optind < argc)
1440 arg_files = argv + optind;
1441
1442 type_a = arg_getter || !strv_isempty(arg_files);
1443 type_b = arg_url
1444 || arg_listen_raw
1445 || arg_listen_http || arg_listen_https
1446 || sd_listen_fds(false) > 0;
1447 if (type_a && type_b) {
1448 log_error("Cannot use file input or --getter with "
1449 "--arg-listen-... or socket activation.");
1450 return -EINVAL;
1451 }
1452 if (type_a) {
1453 if (!arg_output) {
1454 log_error("Option --output must be specified with file input or --getter.");
1455 return -EINVAL;
1456 }
1457
1458 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
1459 }
1460
1461 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE
1462 && arg_output && is_dir(arg_output, true) > 0) {
1463 log_error("For SplitMode=none, output must be a file.");
1464 return -EINVAL;
1465 }
1466
1467 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
1468 && arg_output && is_dir(arg_output, true) <= 0) {
1469 log_error("For SplitMode=host, output must be a directory.");
1470 return -EINVAL;
1471 }
1472
1473 log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1474 journal_write_split_mode_to_string(arg_split_mode),
1475 strna(arg_key),
1476 strna(arg_cert),
1477 strna(arg_trust));
1478
1479 return 1 /* work to do */;
1480 }
1481
1482 static int load_certificates(char **key, char **cert, char **trust) {
1483 int r;
1484
1485 r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL);
1486 if (r < 0)
1487 return log_error_errno(r, "Failed to read key from file '%s': %m",
1488 arg_key ?: PRIV_KEY_FILE);
1489
1490 r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
1491 if (r < 0)
1492 return log_error_errno(r, "Failed to read certificate from file '%s': %m",
1493 arg_cert ?: CERT_FILE);
1494
1495 if (arg_trust_all)
1496 log_info("Certificate checking disabled.");
1497 else {
1498 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
1499 if (r < 0)
1500 return log_error_errno(r, "Failed to read CA certificate file '%s': %m",
1501 arg_trust ?: TRUST_FILE);
1502 }
1503
1504 return 0;
1505 }
1506
1507 int main(int argc, char **argv) {
1508 RemoteServer s = {};
1509 int r;
1510 _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
1511
1512 log_show_color(true);
1513 log_parse_environment();
1514
1515 r = parse_config();
1516 if (r < 0)
1517 return EXIT_FAILURE;
1518
1519 r = parse_argv(argc, argv);
1520 if (r <= 0)
1521 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1522
1523
1524 if (arg_listen_http || arg_listen_https) {
1525 r = setup_gnutls_logger(arg_gnutls_log);
1526 if (r < 0)
1527 return EXIT_FAILURE;
1528 }
1529
1530 if (arg_listen_https || https_socket >= 0)
1531 if (load_certificates(&key, &cert, &trust) < 0)
1532 return EXIT_FAILURE;
1533
1534 if (remoteserver_init(&s, key, cert, trust) < 0)
1535 return EXIT_FAILURE;
1536
1537 r = sd_event_set_watchdog(s.events, true);
1538 if (r < 0)
1539 log_error_errno(r, "Failed to enable watchdog: %m");
1540 else
1541 log_debug("Watchdog is %s.", r > 0 ? "enabled" : "disabled");
1542
1543 log_debug("%s running as pid "PID_FMT,
1544 program_invocation_short_name, getpid());
1545 sd_notify(false,
1546 "READY=1\n"
1547 "STATUS=Processing requests...");
1548
1549 while (s.active) {
1550 r = sd_event_get_state(s.events);
1551 if (r < 0)
1552 break;
1553 if (r == SD_EVENT_FINISHED)
1554 break;
1555
1556 r = sd_event_run(s.events, -1);
1557 if (r < 0) {
1558 log_error_errno(r, "Failed to run event loop: %m");
1559 break;
1560 }
1561 }
1562
1563 sd_notifyf(false,
1564 "STOPPING=1\n"
1565 "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count);
1566 log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
1567
1568 server_destroy(&s);
1569
1570 free(arg_key);
1571 free(arg_cert);
1572 free(arg_trust);
1573
1574 return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1575 }