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