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;
497 log_debug("request_handler_upload: connection %p, %zu bytes",
498 connection, *upload_data_size);
500 r = get_writer(server, source->name, NULL, &w);
502 log_warning("Failed to get writer for source %s (%s): %s",
503 source->name, source->name, strerror(-r));
504 return mhd_respondf(connection,
505 MHD_HTTP_SERVICE_UNAVAILABLE,
506 "Failed to get writer for connection: %s.\n",
510 if (*upload_data_size) {
511 log_info("Received %zu bytes", *upload_data_size);
513 r = push_data(source, upload_data, *upload_data_size);
515 return mhd_respond_oom(connection);
517 *upload_data_size = 0;
523 r = process_source(source, w, arg_compress, arg_seal);
524 if (r == -EAGAIN || r == -EWOULDBLOCK)
527 log_warning("Failed to process data for connection %p", connection);
529 return mhd_respondf(connection,
530 MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
531 "Entry is too large, maximum is %u bytes.\n",
534 return mhd_respondf(connection,
535 MHD_HTTP_UNPROCESSABLE_ENTITY,
536 "Processing failed: %s.", strerror(-r));
543 /* The upload is finished */
545 if (source_non_empty(source)) {
546 log_warning("EOF reached with incomplete data");
547 return mhd_respond(connection, MHD_HTTP_EXPECTATION_FAILED,
548 "Trailing data not processed.");
551 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
554 static int request_handler(
556 struct MHD_Connection *connection,
560 const char *upload_data,
561 size_t *upload_data_size,
562 void **connection_cls) {
569 assert(connection_cls);
573 log_debug("Handling a connection %s %s %s", method, url, version);
576 return process_http_upload(connection,
577 upload_data, upload_data_size,
580 if (!streq(method, "POST"))
581 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
582 "Unsupported method.\n");
584 if (!streq(url, "/upload"))
585 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
588 header = MHD_lookup_connection_value(connection,
589 MHD_HEADER_KIND, "Content-Type");
590 if (!header || !streq(header, "application/vnd.fdo.journal"))
591 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
592 "Content-Type: application/vnd.fdo.journal"
596 const union MHD_ConnectionInfo *ci;
598 ci = MHD_get_connection_info(connection,
599 MHD_CONNECTION_INFO_CONNECTION_FD);
601 log_error("MHD_get_connection_info failed: cannot get remote fd");
602 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
603 "Cannot check remote address");
611 if (server->check_trust) {
612 r = check_permissions(connection, &code, &hostname);
616 r = getnameinfo_pretty(fd, &hostname);
618 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
619 "Cannot check remote hostname");
625 if (!request_meta(connection_cls, fd, hostname))
626 return respond_oom(connection);
630 static int setup_microhttpd_server(RemoteServer *s,
635 struct MHD_OptionItem opts[] = {
636 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
637 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
638 { MHD_OPTION_LISTEN_SOCKET, fd},
646 MHD_USE_PEDANTIC_CHECKS |
647 MHD_USE_EPOLL_LINUX_ONLY |
650 const union MHD_DaemonInfo *info;
656 r = fd_nonblock(fd, true);
658 log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
665 opts[opts_pos++] = (struct MHD_OptionItem)
666 {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key};
667 opts[opts_pos++] = (struct MHD_OptionItem)
668 {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert};
670 flags |= MHD_USE_SSL;
673 opts[opts_pos++] = (struct MHD_OptionItem)
674 {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
677 d = new(MHDDaemonWrapper, 1);
681 d->fd = (uint64_t) fd;
683 d->daemon = MHD_start_daemon(flags, 0,
685 request_handler, NULL,
686 MHD_OPTION_ARRAY, opts,
689 log_error("Failed to start µhttp daemon");
694 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
695 key ? "HTTPS" : "HTTP", fd, d);
698 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
700 log_error("µhttp returned NULL daemon info");
705 epoll_fd = info->listen_fd;
707 log_error("µhttp epoll fd is invalid");
712 r = sd_event_add_io(s->events, &d->event,
714 dispatch_http_event, d);
716 log_error("Failed to add event callback: %s", strerror(-r));
720 r = hashmap_ensure_allocated(&s->daemons, uint64_hash_func, uint64_compare_func);
726 r = hashmap_put(s->daemons, &d->fd, d);
728 log_error("Failed to add daemon to hashmap: %s", strerror(-r));
736 MHD_stop_daemon(d->daemon);
742 static int setup_microhttpd_socket(RemoteServer *s,
749 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
753 return setup_microhttpd_server(s, fd, key, cert, trust);
756 static int dispatch_http_event(sd_event_source *event,
760 MHDDaemonWrapper *d = userdata;
765 r = MHD_run(d->daemon);
767 log_error("MHD_run failed!");
768 // XXX: unregister daemon
772 return 1; /* work to do */
775 /**********************************************************************
776 **********************************************************************
777 **********************************************************************/
779 static int dispatch_sigterm(sd_event_source *event,
780 const struct signalfd_siginfo *si,
782 RemoteServer *s = userdata;
786 log_received_signal(LOG_INFO, si);
788 sd_event_exit(s->events, 0);
792 static int setup_signals(RemoteServer *s) {
798 assert_se(sigemptyset(&mask) == 0);
799 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
800 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
802 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
806 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
813 static int fd_fd(const char *spec) {
816 r = safe_atoi(spec, &fd);
824 static int remoteserver_init(RemoteServer *s,
829 const char *output_name = NULL;
835 if ((arg_listen_raw || arg_listen_http) && trust) {
836 log_error("Option --trust makes all non-HTTPS connections untrusted.");
840 sd_event_default(&s->events);
844 assert(server == NULL);
847 n = sd_listen_fds(true);
849 log_error("Failed to read listening file descriptors from environment: %s",
853 log_info("Received %d descriptors", n);
855 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
856 log_error("Received fewer sockets than expected");
860 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
861 if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
862 log_info("Received a listening socket (fd:%d)", fd);
864 if (fd == http_socket)
865 r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
866 else if (fd == https_socket)
867 r = setup_microhttpd_server(s, fd, key, cert, trust);
869 r = add_raw_socket(s, fd);
870 } else if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
871 _cleanup_free_ char *hostname = NULL;
873 r = getnameinfo_pretty(fd, &hostname);
875 log_error("Failed to retrieve remote name: %s", strerror(-r));
879 log_info("Received a connection socket (fd:%d) from %s", fd, hostname);
881 r = add_source(s, fd, hostname);
883 log_error("Unknown socket passed on fd:%d", fd);
889 log_error("Failed to register socket (fd:%d): %s",
894 output_name = "socket";
898 const char *url, *hostname;
900 url = strappenda(arg_url, "/entries");
903 log_info("Spawning getter %s...", url);
904 fd = spawn_getter(arg_getter, url);
906 log_info("Spawning curl %s...", url);
907 fd = spawn_curl(url);
913 startswith(arg_url, "https://") ?:
914 startswith(arg_url, "http://") ?:
917 r = add_source(s, fd, hostname);
921 output_name = arg_url;
924 if (arg_listen_raw) {
925 log_info("Listening on a socket...");
926 r = setup_raw_socket(s, arg_listen_raw);
930 output_name = arg_listen_raw;
933 if (arg_listen_http) {
934 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
938 output_name = arg_listen_http;
941 if (arg_listen_https) {
942 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
946 output_name = arg_listen_https;
949 STRV_FOREACH(file, arg_files) {
950 if (streq(*file, "-")) {
951 log_info("Reading standard input...");
954 output_name = "stdin";
956 log_info("Reading file %s...", *file);
958 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
960 log_error("Failed to open %s: %m", *file);
966 r = add_source(s, fd, output_name);
971 if (s->active == 0) {
972 log_error("Zarro sources specified");
976 if (!!n + !!arg_url + !!arg_listen_raw + !!arg_files)
977 output_name = "multiple";
979 r = init_writer_hashmap(s);
983 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
984 /* In this case we know what the writer will be
985 called, so we can create it and verify that we can
986 create output as expected. */
989 r = get_writer(s, NULL, NULL, &w);
997 static int server_destroy(RemoteServer *s, uint64_t *event_count) {
1001 MHDDaemonWrapper *d;
1005 while ((w = hashmap_steal_first(s->writers))) {
1006 *event_count += w->seqnum;
1008 r = writer_close(w);
1010 log_warning("Failed to close writer: %s", strerror(-r));
1014 hashmap_free(s->writers);
1016 while ((d = hashmap_steal_first(s->daemons))) {
1017 MHD_stop_daemon(d->daemon);
1018 sd_event_source_unref(d->event);
1022 hashmap_free(s->daemons);
1024 assert(s->sources_size == 0 || s->sources);
1025 for (i = 0; i < s->sources_size; i++)
1026 remove_source(s, i);
1030 sd_event_source_unref(s->sigterm_event);
1031 sd_event_source_unref(s->sigint_event);
1032 sd_event_source_unref(s->listen_event);
1033 sd_event_unref(s->events);
1035 /* fds that we're listening on remain open... */
1040 /**********************************************************************
1041 **********************************************************************
1042 **********************************************************************/
1044 static int dispatch_raw_source_event(sd_event_source *event,
1050 RemoteServer *s = userdata;
1051 RemoteSource *source;
1054 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
1055 source = s->sources[fd];
1056 assert(source->fd == fd);
1058 r = get_writer(s, source->name, NULL, &w);
1060 log_warning("Failed to get writer for source %s (%s): %s",
1061 source->name, source->name, strerror(-r));
1065 r = process_source(source, w, arg_compress, arg_seal);
1066 if (source->state == STATE_EOF) {
1067 log_info("EOF reached with source fd:%d (%s)",
1068 source->fd, source->name);
1069 if (source_non_empty(source))
1070 log_warning("EOF reached with incomplete data");
1071 remove_source(s, source->fd);
1072 log_info("%zd active source remaining", s->active);
1074 } else if (r == -E2BIG) {
1075 log_error("Entry too big, skipped");
1077 } else if (r == -EAGAIN) {
1080 log_info("Closing connection: %s", strerror(-r));
1081 remove_source(server, fd);
1087 static int accept_connection(const char* type, int fd,
1088 SocketAddress *addr, char **hostname) {
1091 log_debug("Accepting new %s connection on fd:%d", type, fd);
1092 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
1094 log_error("accept() on fd:%d failed: %m", fd);
1098 switch(socket_address_family(addr)) {
1101 _cleanup_free_ char *a = NULL;
1104 r = socket_address_print(addr, &a);
1106 log_error("socket_address_print(): %s", strerror(-r));
1111 r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
1117 log_info("Accepted %s %s connection from %s",
1119 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
1127 log_error("Rejected %s connection with unsupported family %d",
1128 type, socket_address_family(addr));
1135 static int dispatch_raw_connection_event(sd_event_source *event,
1139 RemoteServer *s = userdata;
1141 SocketAddress addr = {
1142 .size = sizeof(union sockaddr_union),
1143 .type = SOCK_STREAM,
1147 fd2 = accept_connection("raw", fd, &addr, &hostname);
1151 return add_source(s, fd2, hostname);
1154 /**********************************************************************
1155 **********************************************************************
1156 **********************************************************************/
1158 static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
1159 [JOURNAL_WRITE_SPLIT_NONE] = "none",
1160 [JOURNAL_WRITE_SPLIT_HOST] = "host",
1163 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode);
1164 static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
1165 journal_write_split_mode,
1166 JournalWriteSplitMode,
1167 "Failed to parse split mode setting");
1169 static int parse_config(void) {
1170 const ConfigTableItem items[] = {
1171 { "Remote", "SplitMode", config_parse_write_split_mode, 0, &arg_split_mode },
1172 { "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
1173 { "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
1174 { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
1178 r = config_parse(NULL, PKGSYSCONFDIR "/journal-remote.conf", NULL,
1180 config_item_table_lookup, items,
1181 false, false, NULL);
1183 log_error("Failed to parse configuration file: %s", strerror(-r));
1188 static void help(void) {
1189 printf("%s [OPTIONS...] {FILE|-}...\n\n"
1190 "Write external journal events to a journal file.\n\n"
1192 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
1193 " --getter=COMMAND Read events from the output of COMMAND\n"
1194 " --listen-raw=ADDR Listen for connections at ADDR\n"
1195 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
1196 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
1197 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
1198 " --[no-]compress Use XZ-compression in the output journal (default: yes)\n"
1199 " --[no-]seal Use Event sealing in the output journal (default: no)\n"
1200 " --key=FILENAME Specify key in PEM format (default:\n"
1201 " \"" KEY_FILE "\")\n"
1202 " --cert=FILENAME Specify certificate in PEM format (default:\n"
1203 " \"" CERT_FILE "\")\n"
1204 " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
1205 " \"" TRUST_FILE "\")\n"
1206 " --gnutls-log=CATEGORY...\n"
1207 " Specify a list of gnutls logging categories\n"
1208 " -h --help Show this help and exit\n"
1209 " --version Print version string and exit\n"
1211 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
1212 , program_invocation_short_name);
1215 static int parse_argv(int argc, char *argv[]) {
1217 ARG_VERSION = 0x100,
1234 static const struct option options[] = {
1235 { "help", no_argument, NULL, 'h' },
1236 { "version", no_argument, NULL, ARG_VERSION },
1237 { "url", required_argument, NULL, ARG_URL },
1238 { "getter", required_argument, NULL, ARG_GETTER },
1239 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
1240 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1241 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1242 { "output", required_argument, NULL, 'o' },
1243 { "split-mode", required_argument, NULL, ARG_SPLIT_MODE },
1244 { "compress", no_argument, NULL, ARG_COMPRESS },
1245 { "no-compress", no_argument, NULL, ARG_NO_COMPRESS },
1246 { "seal", no_argument, NULL, ARG_SEAL },
1247 { "no-seal", no_argument, NULL, ARG_NO_SEAL },
1248 { "key", required_argument, NULL, ARG_KEY },
1249 { "cert", required_argument, NULL, ARG_CERT },
1250 { "trust", required_argument, NULL, ARG_TRUST },
1251 { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG },
1256 bool type_a, type_b;
1261 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1265 return 0 /* done */;
1268 puts(PACKAGE_STRING);
1269 puts(SYSTEMD_FEATURES);
1270 return 0 /* done */;
1274 log_error("cannot currently set more than one --url");
1283 log_error("cannot currently use --getter more than once");
1287 arg_getter = optarg;
1290 case ARG_LISTEN_RAW:
1291 if (arg_listen_raw) {
1292 log_error("cannot currently use --listen-raw more than once");
1296 arg_listen_raw = optarg;
1299 case ARG_LISTEN_HTTP:
1300 if (arg_listen_http || http_socket >= 0) {
1301 log_error("cannot currently use --listen-http more than once");
1309 arg_listen_http = optarg;
1312 case ARG_LISTEN_HTTPS:
1313 if (arg_listen_https || https_socket >= 0) {
1314 log_error("cannot currently use --listen-https more than once");
1322 arg_listen_https = optarg;
1328 log_error("Key file specified twice");
1332 arg_key = strdup(optarg);
1340 log_error("Certificate file specified twice");
1344 arg_cert = strdup(optarg);
1351 if (arg_trust || arg_trust_all) {
1352 log_error("Confusing trusted CA configuration");
1356 if (streq(optarg, "all"))
1357 arg_trust_all = true;
1360 arg_trust = strdup(optarg);
1364 log_error("Option --trust is not available.");
1373 log_error("cannot use --output/-o more than once");
1377 arg_output = optarg;
1380 case ARG_SPLIT_MODE:
1381 arg_split_mode = journal_write_split_mode_from_string(optarg);
1382 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
1383 log_error("Invalid split mode: %s", optarg);
1389 arg_compress = true;
1391 case ARG_NO_COMPRESS:
1392 arg_compress = false;
1401 case ARG_GNUTLS_LOG: {
1406 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
1409 cat = strndup(word, size);
1413 if (strv_consume(&arg_gnutls_log, cat) < 0)
1418 log_error("Option --gnutls-log is not available.");
1427 log_error("Unknown option code %c", c);
1432 arg_files = argv + optind;
1434 type_a = arg_getter || !strv_isempty(arg_files);
1437 || arg_listen_http || arg_listen_https
1438 || sd_listen_fds(false) > 0;
1439 if (type_a && type_b) {
1440 log_error("Cannot use file input or --getter with "
1441 "--arg-listen-... or socket activation.");
1446 log_error("Option --output must be specified with file input or --getter.");
1450 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
1453 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE
1454 && arg_output && is_dir(arg_output, true) > 0) {
1455 log_error("For SplitMode=none, output must be a file.");
1459 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
1460 && arg_output && is_dir(arg_output, true) <= 0) {
1461 log_error("For SplitMode=host, output must be a directory.");
1465 log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1466 journal_write_split_mode_to_string(arg_split_mode),
1471 return 1 /* work to do */;
1474 static int load_certificates(char **key, char **cert, char **trust) {
1477 r = read_full_file(arg_key ?: KEY_FILE, key, NULL);
1479 log_error("Failed to read key from file '%s': %s",
1480 arg_key ?: KEY_FILE, strerror(-r));
1484 r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
1486 log_error("Failed to read certificate from file '%s': %s",
1487 arg_cert ?: CERT_FILE, strerror(-r));
1492 log_info("Certificate checking disabled.");
1494 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
1496 log_error("Failed to read CA certificate file '%s': %s",
1497 arg_trust ?: TRUST_FILE, strerror(-r));
1505 static int setup_gnutls_logger(char **categories) {
1506 if (!arg_listen_http && !arg_listen_https)
1514 gnutls_global_set_log_function(log_func_gnutls);
1517 STRV_FOREACH(cat, categories) {
1518 r = log_enable_gnutls_category(*cat);
1523 log_reset_gnutls_level();
1530 int main(int argc, char **argv) {
1531 RemoteServer s = {};
1533 _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
1534 uint64_t entry_count;
1536 log_show_color(true);
1537 log_parse_environment();
1541 return EXIT_FAILURE;
1543 r = parse_argv(argc, argv);
1545 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1547 r = setup_gnutls_logger(arg_gnutls_log);
1549 return EXIT_FAILURE;
1551 if (arg_listen_https || https_socket >= 0)
1552 if (load_certificates(&key, &cert, &trust) < 0)
1553 return EXIT_FAILURE;
1555 if (remoteserver_init(&s, key, cert, trust) < 0)
1556 return EXIT_FAILURE;
1558 sd_event_set_watchdog(s.events, true);
1560 log_debug("%s running as pid "PID_FMT,
1561 program_invocation_short_name, getpid());
1564 "STATUS=Processing requests...");
1567 r = sd_event_get_state(s.events);
1570 if (r == SD_EVENT_FINISHED)
1573 r = sd_event_run(s.events, -1);
1575 log_error("Failed to run event loop: %s", strerror(-r));
1580 r2 = server_destroy(&s, &entry_count);
1581 log_info("Finishing after writing %" PRIu64 " entries", entry_count);
1583 sd_notify(false, "STATUS=Shutting down...");
1589 return r >= 0 && r2 >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;