1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Zbigniew Jędrzejewski-Szmek
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.
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.
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/>.
28 #include <sys/prctl.h>
29 #include <sys/socket.h>
31 #include <sys/types.h>
35 #include "sd-daemon.h"
37 #include "journal-file.h"
38 #include "journald-native.h"
39 #include "socket-util.h"
46 #include "conf-parser.h"
47 #include "microhttpd-util.h"
48 #include "siphash24.h"
51 #include <gnutls/gnutls.h>
54 #include "journal-remote-parse.h"
55 #include "journal-remote-write.h"
57 #define REMOTE_JOURNAL_PATH "/var/log/journal/remote"
59 #define KEY_FILE CERTIFICATE_ROOT "/private/journal-remote.pem"
60 #define CERT_FILE CERTIFICATE_ROOT "/certs/journal-remote.pem"
61 #define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
63 static char* arg_url = NULL;
64 static char* arg_getter = NULL;
65 static char* arg_listen_raw = NULL;
66 static char* arg_listen_http = NULL;
67 static char* arg_listen_https = NULL;
68 static char** arg_files = NULL;
69 static int arg_compress = true;
70 static int arg_seal = false;
71 static int http_socket = -1, https_socket = -1;
72 static char** arg_gnutls_log = NULL;
74 static JournalWriteSplitMode arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
75 static char* arg_output = NULL;
77 static char *arg_key = NULL;
78 static char *arg_cert = NULL;
79 static char *arg_trust = NULL;
80 static bool arg_trust_all = false;
82 /**********************************************************************
83 **********************************************************************
84 **********************************************************************/
86 static int spawn_child(const char* child, char** argv) {
88 pid_t parent_pid, child_pid;
92 log_error("Failed to create pager pipe: %m");
96 parent_pid = getpid();
101 log_error("Failed to fork: %m");
107 if (child_pid == 0) {
108 r = dup2(fd[1], STDOUT_FILENO);
110 log_error("Failed to dup pipe to stdout: %m");
116 /* Make sure the child goes away when the parent dies */
117 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
120 /* Check whether our parent died before we were able
121 * to set the death signal */
122 if (getppid() != parent_pid)
126 log_error("Failed to exec child %s: %m", child);
132 log_warning("Failed to close write end of pipe: %m");
137 static int spawn_curl(const char* url) {
138 char **argv = STRV_MAKE("curl",
139 "-HAccept: application/vnd.fdo.journal",
145 r = spawn_child("curl", argv);
147 log_error("Failed to spawn curl: %m");
151 static int spawn_getter(const char *getter, const char *url) {
153 _cleanup_strv_free_ char **words = NULL;
156 words = strv_split_quoted(getter);
160 r = strv_extend(&words, url);
162 log_error("Failed to create command line: %s", strerror(-r));
166 r = spawn_child(words[0], words);
168 log_error("Failed to spawn getter %s: %m", getter);
173 #define filename_escape(s) xescape((s), "./ ")
175 static int open_output(Writer *w, const char* host) {
176 _cleanup_free_ char *_output = NULL;
180 switch (arg_split_mode) {
181 case JOURNAL_WRITE_SPLIT_NONE:
182 output = arg_output ?: REMOTE_JOURNAL_PATH "/remote.journal";
185 case JOURNAL_WRITE_SPLIT_HOST: {
186 _cleanup_free_ char *name;
190 name = filename_escape(host);
194 r = asprintf(&_output, "%s/remote-%s.journal",
195 arg_output ?: REMOTE_JOURNAL_PATH,
205 assert_not_reached("what?");
208 r = journal_file_open_reliably(output,
209 O_RDWR|O_CREAT, 0640,
210 arg_compress, arg_seal,
215 log_error("Failed to open output journal %s: %s",
216 output, strerror(-r));
218 log_info("Opened output file %s", w->journal->path);
222 /**********************************************************************
223 **********************************************************************
224 **********************************************************************/
226 typedef struct MHDDaemonWrapper {
228 struct MHD_Daemon *daemon;
230 sd_event_source *event;
233 typedef struct RemoteServer {
234 RemoteSource **sources;
239 sd_event_source *sigterm_event, *sigint_event, *listen_event;
247 /* This should go away as soon as µhttpd allows state to be passed around. */
248 static RemoteServer *server;
250 static int dispatch_raw_source_event(sd_event_source *event,
254 static int dispatch_raw_connection_event(sd_event_source *event,
258 static int dispatch_http_event(sd_event_source *event,
263 static int get_source_for_fd(RemoteServer *s, int fd, RemoteSource **source) {
267 if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
270 if (s->sources[fd] == NULL) {
271 s->sources[fd] = new0(RemoteSource, 1);
274 s->sources[fd]->fd = -1;
278 *source = s->sources[fd];
282 static int remove_source(RemoteServer *s, int fd) {
283 RemoteSource *source;
286 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
288 source = s->sources[fd];
290 /* this closes fd too */
292 s->sources[fd] = NULL;
299 static int add_source(RemoteServer *s, int fd, const char* _name) {
301 RemoteSource *source;
309 log_debug("Creating source for fd:%d (%s)", fd, _name);
311 name = strdup(_name);
315 r = get_source_for_fd(s, fd, &source);
317 log_error("Failed to create source for fd:%d (%s)", fd, name);
323 assert(source->fd < 0);
324 assert(!source->name);
329 r = sd_event_add_io(s->events, &source->event,
330 fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI,
331 dispatch_raw_source_event, s);
333 log_error("Failed to register event source for fd:%d: %s",
338 return 1; /* work to do */
341 remove_source(s, fd);
345 static int add_raw_socket(RemoteServer *s, int fd) {
348 r = sd_event_add_io(s->events, &s->listen_event,
350 dispatch_raw_connection_event, s);
360 static int setup_raw_socket(RemoteServer *s, const char *address) {
363 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
367 return add_raw_socket(s, fd);
370 /**********************************************************************
371 **********************************************************************
372 **********************************************************************/
374 static int init_writer_hashmap(RemoteServer *s) {
375 static const struct {
376 hash_func_t hash_func;
377 compare_func_t compare_func;
379 [JOURNAL_WRITE_SPLIT_NONE] = {trivial_hash_func,
380 trivial_compare_func},
381 [JOURNAL_WRITE_SPLIT_HOST] = {string_hash_func,
382 string_compare_func},
385 assert(arg_split_mode >= 0 && arg_split_mode < (int) ELEMENTSOF(functions));
387 s->writers = hashmap_new(functions[arg_split_mode].hash_func,
388 functions[arg_split_mode].compare_func);
395 static int get_writer(RemoteServer *s, const char *host, sd_id128_t *machine,
401 switch(arg_split_mode) {
402 case JOURNAL_WRITE_SPLIT_NONE:
403 key = "one and only";
406 case JOURNAL_WRITE_SPLIT_HOST:
412 assert_not_reached("what split mode?");
415 w = hashmap_get(s->writers, key);
427 r = hashmap_put(s->writers, key, w);
434 r = open_output(w, host);
443 /**********************************************************************
444 **********************************************************************
445 **********************************************************************/
447 static RemoteSource *request_meta(void **connection_cls, int fd, char *hostname) {
448 RemoteSource *source;
450 assert(connection_cls);
451 if (*connection_cls) {
453 return *connection_cls;
456 source = new0(RemoteSource, 1);
462 source->fd = -1; /* fd */
463 source->name = hostname;
465 log_debug("Added RemoteSource as connection metadata %p", source);
467 *connection_cls = source;
471 static void request_meta_free(void *cls,
472 struct MHD_Connection *connection,
473 void **connection_cls,
474 enum MHD_RequestTerminationCode toe) {
477 assert(connection_cls);
480 log_debug("Cleaning up connection metadata %p", s);
482 *connection_cls = NULL;
485 static int process_http_upload(
486 struct MHD_Connection *connection,
487 const char *upload_data,
488 size_t *upload_data_size,
489 RemoteSource *source) {
493 bool finished = false;
498 log_debug("request_handler_upload: connection %p, %zu bytes",
499 connection, *upload_data_size);
501 r = get_writer(server, source->name, NULL, &w);
503 log_warning("Failed to get writer for source %s: %s",
504 source->name, strerror(-r));
505 return mhd_respondf(connection,
506 MHD_HTTP_SERVICE_UNAVAILABLE,
507 "Failed to get writer for connection: %s.\n",
511 if (*upload_data_size) {
512 log_debug("Received %zu bytes", *upload_data_size);
514 r = push_data(source, upload_data, *upload_data_size);
516 return mhd_respond_oom(connection);
518 *upload_data_size = 0;
524 r = process_source(source, w, arg_compress, arg_seal);
525 if (r == -EAGAIN || r == -EWOULDBLOCK)
528 log_warning("Failed to process data for connection %p", connection);
530 return mhd_respondf(connection,
531 MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
532 "Entry is too large, maximum is %u bytes.\n",
535 return mhd_respondf(connection,
536 MHD_HTTP_UNPROCESSABLE_ENTITY,
537 "Processing failed: %s.", strerror(-r));
544 /* The upload is finished */
546 remaining = source_non_empty(source);
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.",
554 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
557 static int request_handler(
559 struct MHD_Connection *connection,
563 const char *upload_data,
564 size_t *upload_data_size,
565 void **connection_cls) {
572 assert(connection_cls);
576 log_debug("Handling a connection %s %s %s", method, url, version);
579 return process_http_upload(connection,
580 upload_data, upload_data_size,
583 if (!streq(method, "POST"))
584 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
585 "Unsupported method.\n");
587 if (!streq(url, "/upload"))
588 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
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"
599 const union MHD_ConnectionInfo *ci;
601 ci = MHD_get_connection_info(connection,
602 MHD_CONNECTION_INFO_CONNECTION_FD);
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");
614 if (server->check_trust) {
615 r = check_permissions(connection, &code, &hostname);
619 r = getnameinfo_pretty(fd, &hostname);
621 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
622 "Cannot check remote hostname");
628 if (!request_meta(connection_cls, fd, hostname))
629 return respond_oom(connection);
633 static int setup_microhttpd_server(RemoteServer *s,
638 struct MHD_OptionItem opts[] = {
639 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
640 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
641 { MHD_OPTION_LISTEN_SOCKET, fd},
649 MHD_USE_PEDANTIC_CHECKS |
650 MHD_USE_EPOLL_LINUX_ONLY |
653 const union MHD_DaemonInfo *info;
659 r = fd_nonblock(fd, true);
661 log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
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};
673 flags |= MHD_USE_SSL;
676 opts[opts_pos++] = (struct MHD_OptionItem)
677 {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
680 d = new(MHDDaemonWrapper, 1);
684 d->fd = (uint64_t) fd;
686 d->daemon = MHD_start_daemon(flags, 0,
688 request_handler, NULL,
689 MHD_OPTION_ARRAY, opts,
692 log_error("Failed to start µhttp daemon");
697 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
698 key ? "HTTPS" : "HTTP", fd, d);
701 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
703 log_error("µhttp returned NULL daemon info");
708 epoll_fd = info->listen_fd;
710 log_error("µhttp epoll fd is invalid");
715 r = sd_event_add_io(s->events, &d->event,
717 dispatch_http_event, d);
719 log_error("Failed to add event callback: %s", strerror(-r));
723 r = hashmap_ensure_allocated(&s->daemons, uint64_hash_func, uint64_compare_func);
729 r = hashmap_put(s->daemons, &d->fd, d);
731 log_error("Failed to add daemon to hashmap: %s", strerror(-r));
739 MHD_stop_daemon(d->daemon);
745 static int setup_microhttpd_socket(RemoteServer *s,
752 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
756 return setup_microhttpd_server(s, fd, key, cert, trust);
759 static int dispatch_http_event(sd_event_source *event,
763 MHDDaemonWrapper *d = userdata;
768 r = MHD_run(d->daemon);
770 log_error("MHD_run failed!");
771 // XXX: unregister daemon
775 return 1; /* work to do */
778 /**********************************************************************
779 **********************************************************************
780 **********************************************************************/
782 static int dispatch_sigterm(sd_event_source *event,
783 const struct signalfd_siginfo *si,
785 RemoteServer *s = userdata;
789 log_received_signal(LOG_INFO, si);
791 sd_event_exit(s->events, 0);
795 static int setup_signals(RemoteServer *s) {
801 assert_se(sigemptyset(&mask) == 0);
802 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
803 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
805 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
809 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
816 static int fd_fd(const char *spec) {
819 r = safe_atoi(spec, &fd);
827 static int remoteserver_init(RemoteServer *s,
832 const char *output_name = NULL;
838 if ((arg_listen_raw || arg_listen_http) && trust) {
839 log_error("Option --trust makes all non-HTTPS connections untrusted.");
843 sd_event_default(&s->events);
847 assert(server == NULL);
850 n = sd_listen_fds(true);
852 log_error("Failed to read listening file descriptors from environment: %s",
856 log_info("Received %d descriptors", n);
858 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
859 log_error("Received fewer sockets than expected");
863 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
864 if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
865 log_info("Received a listening socket (fd:%d)", fd);
867 if (fd == http_socket)
868 r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
869 else if (fd == https_socket)
870 r = setup_microhttpd_server(s, fd, key, cert, trust);
872 r = add_raw_socket(s, fd);
873 } else if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
874 _cleanup_free_ char *hostname = NULL;
876 r = getnameinfo_pretty(fd, &hostname);
878 log_error("Failed to retrieve remote name: %s", strerror(-r));
882 log_info("Received a connection socket (fd:%d) from %s", fd, hostname);
884 r = add_source(s, fd, hostname);
886 log_error("Unknown socket passed on fd:%d", fd);
892 log_error("Failed to register socket (fd:%d): %s",
897 output_name = "socket";
901 const char *url, *hostname;
903 url = strappenda(arg_url, "/entries");
906 log_info("Spawning getter %s...", url);
907 fd = spawn_getter(arg_getter, url);
909 log_info("Spawning curl %s...", url);
910 fd = spawn_curl(url);
916 startswith(arg_url, "https://") ?:
917 startswith(arg_url, "http://") ?:
920 r = add_source(s, fd, hostname);
924 output_name = arg_url;
927 if (arg_listen_raw) {
928 log_info("Listening on a socket...");
929 r = setup_raw_socket(s, arg_listen_raw);
933 output_name = arg_listen_raw;
936 if (arg_listen_http) {
937 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
941 output_name = arg_listen_http;
944 if (arg_listen_https) {
945 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
949 output_name = arg_listen_https;
952 STRV_FOREACH(file, arg_files) {
953 if (streq(*file, "-")) {
954 log_info("Using standard input as source.");
957 output_name = "stdin";
959 log_info("Reading file %s...", *file);
961 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
963 log_error("Failed to open %s: %m", *file);
969 r = add_source(s, fd, output_name);
974 if (s->active == 0) {
975 log_error("Zarro sources specified");
979 if (!!n + !!arg_url + !!arg_listen_raw + !!arg_files)
980 output_name = "multiple";
982 r = init_writer_hashmap(s);
986 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
987 /* In this case we know what the writer will be
988 called, so we can create it and verify that we can
989 create output as expected. */
992 r = get_writer(s, NULL, NULL, &w);
1000 static int server_destroy(RemoteServer *s, uint64_t *event_count) {
1004 MHDDaemonWrapper *d;
1008 while ((w = hashmap_steal_first(s->writers))) {
1009 log_info("seqnum %"PRIu64, w->seqnum);
1010 *event_count += w->seqnum;
1012 r = writer_close(w);
1014 log_warning("Failed to close writer: %s", strerror(-r));
1018 hashmap_free(s->writers);
1020 while ((d = hashmap_steal_first(s->daemons))) {
1021 MHD_stop_daemon(d->daemon);
1022 sd_event_source_unref(d->event);
1026 hashmap_free(s->daemons);
1028 assert(s->sources_size == 0 || s->sources);
1029 for (i = 0; i < s->sources_size; i++)
1030 remove_source(s, i);
1034 sd_event_source_unref(s->sigterm_event);
1035 sd_event_source_unref(s->sigint_event);
1036 sd_event_source_unref(s->listen_event);
1037 sd_event_unref(s->events);
1039 /* fds that we're listening on remain open... */
1044 /**********************************************************************
1045 **********************************************************************
1046 **********************************************************************/
1048 static int dispatch_raw_source_event(sd_event_source *event,
1054 RemoteServer *s = userdata;
1055 RemoteSource *source;
1058 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
1059 source = s->sources[fd];
1060 assert(source->fd == fd);
1062 r = get_writer(s, source->name, NULL, &w);
1064 log_warning("Failed to get writer for source %s: %s",
1065 source->name, strerror(-r));
1069 r = process_source(source, w, arg_compress, arg_seal);
1070 if (source->state == STATE_EOF) {
1073 log_info("EOF reached with source fd:%d (%s)",
1074 source->fd, source->name);
1076 remaining = source_non_empty(source);
1078 log_warning("Premature EOF. %zu bytes lost.", remaining);
1079 remove_source(s, source->fd);
1080 log_info("%zd active sources remaining", s->active);
1082 } else if (r == -E2BIG) {
1083 log_error("Entry too big, skipped");
1085 } else if (r == -EAGAIN) {
1088 log_info("Closing connection: %s", strerror(-r));
1089 remove_source(server, fd);
1095 static int accept_connection(const char* type, int fd,
1096 SocketAddress *addr, char **hostname) {
1099 log_debug("Accepting new %s connection on fd:%d", type, fd);
1100 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
1102 log_error("accept() on fd:%d failed: %m", fd);
1106 switch(socket_address_family(addr)) {
1109 _cleanup_free_ char *a = NULL;
1112 r = socket_address_print(addr, &a);
1114 log_error("socket_address_print(): %s", strerror(-r));
1119 r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
1125 log_info("Accepted %s %s connection from %s",
1127 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
1135 log_error("Rejected %s connection with unsupported family %d",
1136 type, socket_address_family(addr));
1143 static int dispatch_raw_connection_event(sd_event_source *event,
1147 RemoteServer *s = userdata;
1149 SocketAddress addr = {
1150 .size = sizeof(union sockaddr_union),
1151 .type = SOCK_STREAM,
1155 fd2 = accept_connection("raw", fd, &addr, &hostname);
1159 return add_source(s, fd2, hostname);
1162 /**********************************************************************
1163 **********************************************************************
1164 **********************************************************************/
1166 static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
1167 [JOURNAL_WRITE_SPLIT_NONE] = "none",
1168 [JOURNAL_WRITE_SPLIT_HOST] = "host",
1171 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode);
1172 static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
1173 journal_write_split_mode,
1174 JournalWriteSplitMode,
1175 "Failed to parse split mode setting");
1177 static int parse_config(void) {
1178 const ConfigTableItem items[] = {
1179 { "Remote", "SplitMode", config_parse_write_split_mode, 0, &arg_split_mode },
1180 { "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
1181 { "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
1182 { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
1186 r = config_parse(NULL, PKGSYSCONFDIR "/journal-remote.conf", NULL,
1188 config_item_table_lookup, items,
1189 false, false, NULL);
1191 log_error("Failed to parse configuration file: %s", strerror(-r));
1196 static void help(void) {
1197 printf("%s [OPTIONS...] {FILE|-}...\n\n"
1198 "Write external journal events to journal file(s).\n\n"
1200 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
1201 " --getter=COMMAND Read events from the output of COMMAND\n"
1202 " --listen-raw=ADDR Listen for connections at ADDR\n"
1203 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
1204 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
1205 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
1206 " --[no-]compress Use XZ-compression in the output journal (default: yes)\n"
1207 " --[no-]seal Use Event sealing in the output journal (default: no)\n"
1208 " --key=FILENAME Specify key in PEM format (default:\n"
1209 " \"" KEY_FILE "\")\n"
1210 " --cert=FILENAME Specify certificate in PEM format (default:\n"
1211 " \"" CERT_FILE "\")\n"
1212 " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
1213 " \"" TRUST_FILE "\")\n"
1214 " --gnutls-log=CATEGORY...\n"
1215 " Specify a list of gnutls logging categories\n"
1216 " -h --help Show this help and exit\n"
1217 " --version Print version string and exit\n"
1219 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
1220 , program_invocation_short_name);
1223 static int parse_argv(int argc, char *argv[]) {
1225 ARG_VERSION = 0x100,
1242 static const struct option options[] = {
1243 { "help", no_argument, NULL, 'h' },
1244 { "version", no_argument, NULL, ARG_VERSION },
1245 { "url", required_argument, NULL, ARG_URL },
1246 { "getter", required_argument, NULL, ARG_GETTER },
1247 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
1248 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1249 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1250 { "output", required_argument, NULL, 'o' },
1251 { "split-mode", required_argument, NULL, ARG_SPLIT_MODE },
1252 { "compress", no_argument, NULL, ARG_COMPRESS },
1253 { "no-compress", no_argument, NULL, ARG_NO_COMPRESS },
1254 { "seal", no_argument, NULL, ARG_SEAL },
1255 { "no-seal", no_argument, NULL, ARG_NO_SEAL },
1256 { "key", required_argument, NULL, ARG_KEY },
1257 { "cert", required_argument, NULL, ARG_CERT },
1258 { "trust", required_argument, NULL, ARG_TRUST },
1259 { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG },
1264 bool type_a, type_b;
1269 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1273 return 0 /* done */;
1276 puts(PACKAGE_STRING);
1277 puts(SYSTEMD_FEATURES);
1278 return 0 /* done */;
1282 log_error("cannot currently set more than one --url");
1291 log_error("cannot currently use --getter more than once");
1295 arg_getter = optarg;
1298 case ARG_LISTEN_RAW:
1299 if (arg_listen_raw) {
1300 log_error("cannot currently use --listen-raw more than once");
1304 arg_listen_raw = optarg;
1307 case ARG_LISTEN_HTTP:
1308 if (arg_listen_http || http_socket >= 0) {
1309 log_error("cannot currently use --listen-http more than once");
1317 arg_listen_http = optarg;
1320 case ARG_LISTEN_HTTPS:
1321 if (arg_listen_https || https_socket >= 0) {
1322 log_error("cannot currently use --listen-https more than once");
1330 arg_listen_https = optarg;
1336 log_error("Key file specified twice");
1340 arg_key = strdup(optarg);
1348 log_error("Certificate file specified twice");
1352 arg_cert = strdup(optarg);
1359 if (arg_trust || arg_trust_all) {
1360 log_error("Confusing trusted CA configuration");
1364 if (streq(optarg, "all"))
1365 arg_trust_all = true;
1368 arg_trust = strdup(optarg);
1372 log_error("Option --trust is not available.");
1381 log_error("cannot use --output/-o more than once");
1385 arg_output = optarg;
1388 case ARG_SPLIT_MODE:
1389 arg_split_mode = journal_write_split_mode_from_string(optarg);
1390 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
1391 log_error("Invalid split mode: %s", optarg);
1397 arg_compress = true;
1399 case ARG_NO_COMPRESS:
1400 arg_compress = false;
1409 case ARG_GNUTLS_LOG: {
1414 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
1417 cat = strndup(word, size);
1421 if (strv_consume(&arg_gnutls_log, cat) < 0)
1426 log_error("Option --gnutls-log is not available.");
1435 log_error("Unknown option code %c", c);
1440 arg_files = argv + optind;
1442 type_a = arg_getter || !strv_isempty(arg_files);
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.");
1454 log_error("Option --output must be specified with file input or --getter.");
1458 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
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.");
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.");
1473 log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1474 journal_write_split_mode_to_string(arg_split_mode),
1479 return 1 /* work to do */;
1482 static int load_certificates(char **key, char **cert, char **trust) {
1485 r = read_full_file(arg_key ?: KEY_FILE, key, NULL);
1487 log_error("Failed to read key from file '%s': %s",
1488 arg_key ?: KEY_FILE, strerror(-r));
1492 r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
1494 log_error("Failed to read certificate from file '%s': %s",
1495 arg_cert ?: CERT_FILE, strerror(-r));
1500 log_info("Certificate checking disabled.");
1502 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
1504 log_error("Failed to read CA certificate file '%s': %s",
1505 arg_trust ?: TRUST_FILE, strerror(-r));
1513 static int setup_gnutls_logger(char **categories) {
1514 if (!arg_listen_http && !arg_listen_https)
1522 gnutls_global_set_log_function(log_func_gnutls);
1525 STRV_FOREACH(cat, categories) {
1526 r = log_enable_gnutls_category(*cat);
1531 log_reset_gnutls_level();
1538 int main(int argc, char **argv) {
1539 RemoteServer s = {};
1541 _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
1542 uint64_t entry_count;
1544 log_show_color(true);
1545 log_parse_environment();
1549 return EXIT_FAILURE;
1551 r = parse_argv(argc, argv);
1553 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1555 r = setup_gnutls_logger(arg_gnutls_log);
1557 return EXIT_FAILURE;
1559 if (arg_listen_https || https_socket >= 0)
1560 if (load_certificates(&key, &cert, &trust) < 0)
1561 return EXIT_FAILURE;
1563 if (remoteserver_init(&s, key, cert, trust) < 0)
1564 return EXIT_FAILURE;
1566 sd_event_set_watchdog(s.events, true);
1568 log_debug("%s running as pid "PID_FMT,
1569 program_invocation_short_name, getpid());
1572 "STATUS=Processing requests...");
1575 r = sd_event_get_state(s.events);
1578 if (r == SD_EVENT_FINISHED)
1581 r = sd_event_run(s.events, -1);
1583 log_error("Failed to run event loop: %s", strerror(-r));
1588 r2 = server_destroy(&s, &entry_count);
1589 log_info("Finishing after writing %" PRIu64 " entries", entry_count);
1591 sd_notify(false, "STATUS=Shutting down...");
1597 return r >= 0 && r2 >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;