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/>.
27 #include <sys/prctl.h>
28 #include <sys/socket.h>
30 #include <sys/types.h>
34 #include "sd-daemon.h"
35 #include "journal-file.h"
36 #include "journald-native.h"
37 #include "socket-util.h"
43 #include "conf-parser.h"
44 #include "siphash24.h"
47 #include <gnutls/gnutls.h>
50 #include "journal-remote.h"
51 #include "journal-remote-write.h"
53 #define REMOTE_JOURNAL_PATH "/var/log/journal/remote"
55 #define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-remote.pem"
56 #define CERT_FILE CERTIFICATE_ROOT "/certs/journal-remote.pem"
57 #define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
59 static char* arg_url = NULL;
60 static char* arg_getter = NULL;
61 static char* arg_listen_raw = NULL;
62 static char* arg_listen_http = NULL;
63 static char* arg_listen_https = NULL;
64 static char** arg_files = NULL;
65 static int arg_compress = true;
66 static int arg_seal = false;
67 static int http_socket = -1, https_socket = -1;
68 static char** arg_gnutls_log = NULL;
70 static JournalWriteSplitMode arg_split_mode = JOURNAL_WRITE_SPLIT_HOST;
71 static char* arg_output = NULL;
73 static char *arg_key = NULL;
74 static char *arg_cert = NULL;
75 static char *arg_trust = NULL;
76 static bool arg_trust_all = false;
78 /**********************************************************************
79 **********************************************************************
80 **********************************************************************/
82 static int spawn_child(const char* child, char** argv) {
84 pid_t parent_pid, child_pid;
88 log_error("Failed to create pager pipe: %m");
92 parent_pid = getpid();
97 log_error("Failed to fork: %m");
103 if (child_pid == 0) {
104 r = dup2(fd[1], STDOUT_FILENO);
106 log_error("Failed to dup pipe to stdout: %m");
112 /* Make sure the child goes away when the parent dies */
113 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
116 /* Check whether our parent died before we were able
117 * to set the death signal */
118 if (getppid() != parent_pid)
122 log_error("Failed to exec child %s: %m", child);
128 log_warning("Failed to close write end of pipe: %m");
133 static int spawn_curl(const char* url) {
134 char **argv = STRV_MAKE("curl",
135 "-HAccept: application/vnd.fdo.journal",
141 r = spawn_child("curl", argv);
143 log_error("Failed to spawn curl: %m");
147 static int spawn_getter(const char *getter, const char *url) {
149 _cleanup_strv_free_ char **words = NULL;
152 r = strv_split_quoted(&words, getter);
154 log_error("Failed to split getter option: %s", strerror(-r));
158 r = strv_extend(&words, url);
160 log_error("Failed to create command line: %s", strerror(-r));
164 r = spawn_child(words[0], words);
166 log_error("Failed to spawn getter %s: %m", getter);
171 #define filename_escape(s) xescape((s), "/ ")
173 static int open_output(Writer *w, const char* host) {
174 _cleanup_free_ char *_output = NULL;
178 switch (arg_split_mode) {
179 case JOURNAL_WRITE_SPLIT_NONE:
180 output = arg_output ?: REMOTE_JOURNAL_PATH "/remote.journal";
183 case JOURNAL_WRITE_SPLIT_HOST: {
184 _cleanup_free_ char *name;
188 name = filename_escape(host);
192 r = asprintf(&_output, "%s/remote-%s.journal",
193 arg_output ?: REMOTE_JOURNAL_PATH,
203 assert_not_reached("what?");
206 r = journal_file_open_reliably(output,
207 O_RDWR|O_CREAT, 0640,
208 arg_compress, arg_seal,
213 log_error("Failed to open output journal %s: %s",
214 output, strerror(-r));
216 log_info("Opened output file %s", w->journal->path);
220 /**********************************************************************
221 **********************************************************************
222 **********************************************************************/
224 static int init_writer_hashmap(RemoteServer *s) {
225 static const struct {
226 hash_func_t hash_func;
227 compare_func_t compare_func;
229 [JOURNAL_WRITE_SPLIT_NONE] = {trivial_hash_func,
230 trivial_compare_func},
231 [JOURNAL_WRITE_SPLIT_HOST] = {string_hash_func,
232 string_compare_func},
235 assert(arg_split_mode >= 0 && arg_split_mode < (int) ELEMENTSOF(functions));
237 s->writers = hashmap_new(functions[arg_split_mode].hash_func,
238 functions[arg_split_mode].compare_func);
245 static int get_writer(RemoteServer *s, const char *host,
248 _cleanup_writer_unref_ Writer *w = NULL;
251 switch(arg_split_mode) {
252 case JOURNAL_WRITE_SPLIT_NONE:
253 key = "one and only";
256 case JOURNAL_WRITE_SPLIT_HOST:
262 assert_not_reached("what split mode?");
265 w = hashmap_get(s->writers, key);
273 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST) {
274 w->hashmap_key = strdup(key);
279 r = open_output(w, host);
283 r = hashmap_put(s->writers, w->hashmap_key ?: key, w);
293 /**********************************************************************
294 **********************************************************************
295 **********************************************************************/
297 /* This should go away as soon as µhttpd allows state to be passed around. */
298 static RemoteServer *server;
300 static int dispatch_raw_source_event(sd_event_source *event,
304 static int dispatch_raw_connection_event(sd_event_source *event,
308 static int dispatch_http_event(sd_event_source *event,
313 static int get_source_for_fd(RemoteServer *s,
314 int fd, char *name, RemoteSource **source) {
321 if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
324 r = get_writer(s, name, &writer);
326 log_warning("Failed to get writer for source %s: %s",
331 if (s->sources[fd] == NULL) {
332 s->sources[fd] = source_new(fd, false, name, writer);
333 if (!s->sources[fd]) {
334 writer_unref(writer);
341 *source = s->sources[fd];
345 static int remove_source(RemoteServer *s, int fd) {
346 RemoteSource *source;
349 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
351 source = s->sources[fd];
353 /* this closes fd too */
355 s->sources[fd] = NULL;
362 static int add_source(RemoteServer *s, int fd, char* name, bool own_name) {
364 RemoteSource *source;
377 r = get_source_for_fd(s, fd, name, &source);
379 log_error("Failed to create source for fd:%d (%s): %s",
380 fd, name, strerror(-r));
384 r = sd_event_add_io(s->events, &source->event,
385 fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI,
386 dispatch_raw_source_event, s);
388 log_error("Failed to register event source for fd:%d: %s",
393 return 1; /* work to do */
396 remove_source(s, fd);
400 static int add_raw_socket(RemoteServer *s, int fd) {
403 r = sd_event_add_io(s->events, &s->listen_event,
405 dispatch_raw_connection_event, s);
415 static int setup_raw_socket(RemoteServer *s, const char *address) {
418 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
422 return add_raw_socket(s, fd);
425 /**********************************************************************
426 **********************************************************************
427 **********************************************************************/
429 static RemoteSource *request_meta(void **connection_cls, int fd, char *hostname) {
430 RemoteSource *source;
434 assert(connection_cls);
436 return *connection_cls;
438 r = get_writer(server, hostname, &writer);
440 log_warning("Failed to get writer for source %s: %s",
441 hostname, strerror(-r));
445 source = source_new(fd, true, hostname, writer);
448 writer_unref(writer);
452 log_debug("Added RemoteSource as connection metadata %p", source);
454 *connection_cls = source;
458 static void request_meta_free(void *cls,
459 struct MHD_Connection *connection,
460 void **connection_cls,
461 enum MHD_RequestTerminationCode toe) {
464 assert(connection_cls);
467 log_debug("Cleaning up connection metadata %p", s);
469 *connection_cls = NULL;
472 static int process_http_upload(
473 struct MHD_Connection *connection,
474 const char *upload_data,
475 size_t *upload_data_size,
476 RemoteSource *source) {
478 bool finished = false;
484 log_debug("request_handler_upload: connection %p, %zu bytes",
485 connection, *upload_data_size);
487 if (*upload_data_size) {
488 log_debug("Received %zu bytes", *upload_data_size);
490 r = push_data(source, upload_data, *upload_data_size);
492 return mhd_respond_oom(connection);
494 *upload_data_size = 0;
499 r = process_source(source, arg_compress, arg_seal);
500 if (r == -EAGAIN || r == -EWOULDBLOCK)
503 log_warning("Failed to process data for connection %p", connection);
505 return mhd_respondf(connection,
506 MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
507 "Entry is too large, maximum is %u bytes.\n",
510 return mhd_respondf(connection,
511 MHD_HTTP_UNPROCESSABLE_ENTITY,
512 "Processing failed: %s.", strerror(-r));
519 /* The upload is finished */
521 remaining = source_non_empty(source);
523 log_warning("Premature EOFbyte. %zu bytes lost.", remaining);
524 return mhd_respondf(connection, MHD_HTTP_EXPECTATION_FAILED,
525 "Premature EOF. %zu bytes of trailing data not processed.",
529 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
532 static int request_handler(
534 struct MHD_Connection *connection,
538 const char *upload_data,
539 size_t *upload_data_size,
540 void **connection_cls) {
544 _cleanup_free_ char *hostname = NULL;
547 assert(connection_cls);
551 log_debug("Handling a connection %s %s %s", method, url, version);
554 return process_http_upload(connection,
555 upload_data, upload_data_size,
558 if (!streq(method, "POST"))
559 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
560 "Unsupported method.\n");
562 if (!streq(url, "/upload"))
563 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
566 header = MHD_lookup_connection_value(connection,
567 MHD_HEADER_KIND, "Content-Type");
568 if (!header || !streq(header, "application/vnd.fdo.journal"))
569 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
570 "Content-Type: application/vnd.fdo.journal"
574 const union MHD_ConnectionInfo *ci;
576 ci = MHD_get_connection_info(connection,
577 MHD_CONNECTION_INFO_CONNECTION_FD);
579 log_error("MHD_get_connection_info failed: cannot get remote fd");
580 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
581 "Cannot check remote address");
589 if (server->check_trust) {
590 r = check_permissions(connection, &code, &hostname);
594 r = getnameinfo_pretty(fd, &hostname);
596 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
597 "Cannot check remote hostname");
603 if (!request_meta(connection_cls, fd, hostname))
604 return respond_oom(connection);
609 static int setup_microhttpd_server(RemoteServer *s,
614 struct MHD_OptionItem opts[] = {
615 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
616 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
617 { MHD_OPTION_LISTEN_SOCKET, fd},
625 MHD_USE_PEDANTIC_CHECKS |
626 MHD_USE_EPOLL_LINUX_ONLY |
629 const union MHD_DaemonInfo *info;
635 r = fd_nonblock(fd, true);
637 log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
644 opts[opts_pos++] = (struct MHD_OptionItem)
645 {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key};
646 opts[opts_pos++] = (struct MHD_OptionItem)
647 {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert};
649 flags |= MHD_USE_SSL;
652 opts[opts_pos++] = (struct MHD_OptionItem)
653 {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
656 d = new(MHDDaemonWrapper, 1);
660 d->fd = (uint64_t) fd;
662 d->daemon = MHD_start_daemon(flags, 0,
664 request_handler, NULL,
665 MHD_OPTION_ARRAY, opts,
668 log_error("Failed to start µhttp daemon");
673 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
674 key ? "HTTPS" : "HTTP", fd, d);
677 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
679 log_error("µhttp returned NULL daemon info");
684 epoll_fd = info->listen_fd;
686 log_error("µhttp epoll fd is invalid");
691 r = sd_event_add_io(s->events, &d->event,
693 dispatch_http_event, d);
695 log_error("Failed to add event callback: %s", strerror(-r));
699 r = hashmap_ensure_allocated(&s->daemons, uint64_hash_func, uint64_compare_func);
705 r = hashmap_put(s->daemons, &d->fd, d);
707 log_error("Failed to add daemon to hashmap: %s", strerror(-r));
715 MHD_stop_daemon(d->daemon);
721 static int setup_microhttpd_socket(RemoteServer *s,
728 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
732 return setup_microhttpd_server(s, fd, key, cert, trust);
735 static int dispatch_http_event(sd_event_source *event,
739 MHDDaemonWrapper *d = userdata;
744 r = MHD_run(d->daemon);
746 log_error("MHD_run failed!");
747 // XXX: unregister daemon
751 return 1; /* work to do */
754 /**********************************************************************
755 **********************************************************************
756 **********************************************************************/
758 static int dispatch_sigterm(sd_event_source *event,
759 const struct signalfd_siginfo *si,
761 RemoteServer *s = userdata;
765 log_received_signal(LOG_INFO, si);
767 sd_event_exit(s->events, 0);
771 static int setup_signals(RemoteServer *s) {
777 assert_se(sigemptyset(&mask) == 0);
778 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
779 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
781 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
785 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
792 static int fd_fd(const char *spec) {
795 r = safe_atoi(spec, &fd);
804 static int remoteserver_init(RemoteServer *s,
813 if ((arg_listen_raw || arg_listen_http) && trust) {
814 log_error("Option --trust makes all non-HTTPS connections untrusted.");
818 sd_event_default(&s->events);
822 assert(server == NULL);
825 n = sd_listen_fds(true);
827 log_error("Failed to read listening file descriptors from environment: %s",
831 log_info("Received %d descriptors", n);
833 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
834 log_error("Received fewer sockets than expected");
838 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
839 if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
840 log_info("Received a listening socket (fd:%d)", fd);
842 if (fd == http_socket)
843 r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
844 else if (fd == https_socket)
845 r = setup_microhttpd_server(s, fd, key, cert, trust);
847 r = add_raw_socket(s, fd);
848 } else if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
851 r = getnameinfo_pretty(fd, &hostname);
853 log_error("Failed to retrieve remote name: %s", strerror(-r));
857 log_info("Received a connection socket (fd:%d) from %s", fd, hostname);
859 r = add_source(s, fd, hostname, true);
863 log_error("Unknown socket passed on fd:%d", fd);
869 log_error("Failed to register socket (fd:%d): %s",
876 const char *url, *hostname;
878 url = strappenda(arg_url, "/entries");
881 log_info("Spawning getter %s...", url);
882 fd = spawn_getter(arg_getter, url);
884 log_info("Spawning curl %s...", url);
885 fd = spawn_curl(url);
891 startswith(arg_url, "https://") ?:
892 startswith(arg_url, "http://") ?:
895 r = add_source(s, fd, (char*) hostname, false);
900 if (arg_listen_raw) {
901 log_info("Listening on a socket...");
902 r = setup_raw_socket(s, arg_listen_raw);
907 if (arg_listen_http) {
908 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
913 if (arg_listen_https) {
914 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
919 STRV_FOREACH(file, arg_files) {
920 const char *output_name;
922 if (streq(*file, "-")) {
923 log_info("Using standard input as source.");
926 output_name = "stdin";
928 log_info("Reading file %s...", *file);
930 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
932 log_error("Failed to open %s: %m", *file);
938 r = add_source(s, fd, (char*) output_name, false);
943 if (s->active == 0) {
944 log_error("Zarro sources specified");
948 r = init_writer_hashmap(s);
952 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
953 /* In this case we know what the writer will be
954 called, so we can create it and verify that we can
955 create output as expected. */
956 r = get_writer(s, NULL, &s->_single_writer);
964 static void server_destroy(RemoteServer *s) {
968 while ((d = hashmap_steal_first(s->daemons))) {
969 MHD_stop_daemon(d->daemon);
970 sd_event_source_unref(d->event);
974 hashmap_free(s->daemons);
976 assert(s->sources_size == 0 || s->sources);
977 for (i = 0; i < s->sources_size; i++)
981 writer_unref(s->_single_writer);
982 hashmap_free(s->writers);
984 sd_event_source_unref(s->sigterm_event);
985 sd_event_source_unref(s->sigint_event);
986 sd_event_source_unref(s->listen_event);
987 sd_event_unref(s->events);
989 /* fds that we're listening on remain open... */
992 /**********************************************************************
993 **********************************************************************
994 **********************************************************************/
996 static int dispatch_raw_source_event(sd_event_source *event,
1001 RemoteServer *s = userdata;
1002 RemoteSource *source;
1005 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
1006 source = s->sources[fd];
1007 assert(source->fd == fd);
1009 r = process_source(source, arg_compress, arg_seal);
1010 if (source->state == STATE_EOF) {
1013 log_info("EOF reached with source fd:%d (%s)",
1014 source->fd, source->name);
1016 remaining = source_non_empty(source);
1018 log_warning("Premature EOF. %zu bytes lost.", remaining);
1019 remove_source(s, source->fd);
1020 log_info("%zd active sources remaining", s->active);
1022 } else if (r == -E2BIG) {
1023 log_error("Entry too big, skipped");
1025 } else if (r == -EAGAIN) {
1028 log_info("Closing connection: %s", strerror(-r));
1029 remove_source(server, fd);
1035 static int accept_connection(const char* type, int fd,
1036 SocketAddress *addr, char **hostname) {
1039 log_debug("Accepting new %s connection on fd:%d", type, fd);
1040 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
1042 log_error("accept() on fd:%d failed: %m", fd);
1046 switch(socket_address_family(addr)) {
1049 _cleanup_free_ char *a = NULL;
1052 r = socket_address_print(addr, &a);
1054 log_error("socket_address_print(): %s", strerror(-r));
1059 r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
1065 log_info("Accepted %s %s connection from %s",
1067 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
1075 log_error("Rejected %s connection with unsupported family %d",
1076 type, socket_address_family(addr));
1083 static int dispatch_raw_connection_event(sd_event_source *event,
1087 RemoteServer *s = userdata;
1089 SocketAddress addr = {
1090 .size = sizeof(union sockaddr_union),
1091 .type = SOCK_STREAM,
1095 fd2 = accept_connection("raw", fd, &addr, &hostname);
1099 r = add_source(s, fd2, hostname, true);
1105 /**********************************************************************
1106 **********************************************************************
1107 **********************************************************************/
1109 static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
1110 [JOURNAL_WRITE_SPLIT_NONE] = "none",
1111 [JOURNAL_WRITE_SPLIT_HOST] = "host",
1114 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode);
1115 static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
1116 journal_write_split_mode,
1117 JournalWriteSplitMode,
1118 "Failed to parse split mode setting");
1120 static int parse_config(void) {
1121 const ConfigTableItem items[] = {
1122 { "Remote", "SplitMode", config_parse_write_split_mode, 0, &arg_split_mode },
1123 { "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
1124 { "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
1125 { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
1128 return config_parse(NULL, PKGSYSCONFDIR "/journal-remote.conf", NULL,
1130 config_item_table_lookup, items,
1131 false, false, true, NULL);
1134 static void help(void) {
1135 printf("%s [OPTIONS...] {FILE|-}...\n\n"
1136 "Write external journal events to journal file(s).\n\n"
1137 " -h --help Show this help\n"
1138 " --version Show package version\n"
1139 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
1140 " --getter=COMMAND Read events from the output of COMMAND\n"
1141 " --listen-raw=ADDR Listen for connections at ADDR\n"
1142 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
1143 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
1144 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
1145 " --compress[=BOOL] Use XZ-compression in the output journal (default: yes)\n"
1146 " --seal[=BOOL] Use Event sealing in the output journal (default: no)\n"
1147 " --key=FILENAME Specify key in PEM format (default:\n"
1148 " \"" PRIV_KEY_FILE "\")\n"
1149 " --cert=FILENAME Specify certificate in PEM format (default:\n"
1150 " \"" CERT_FILE "\")\n"
1151 " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
1152 " \"" TRUST_FILE "\")\n"
1153 " --gnutls-log=CATEGORY...\n"
1154 " Specify a list of gnutls logging categories\n"
1156 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
1157 , program_invocation_short_name);
1160 static int parse_argv(int argc, char *argv[]) {
1162 ARG_VERSION = 0x100,
1177 static const struct option options[] = {
1178 { "help", no_argument, NULL, 'h' },
1179 { "version", no_argument, NULL, ARG_VERSION },
1180 { "url", required_argument, NULL, ARG_URL },
1181 { "getter", required_argument, NULL, ARG_GETTER },
1182 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
1183 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1184 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1185 { "output", required_argument, NULL, 'o' },
1186 { "split-mode", required_argument, NULL, ARG_SPLIT_MODE },
1187 { "compress", optional_argument, NULL, ARG_COMPRESS },
1188 { "seal", optional_argument, NULL, ARG_SEAL },
1189 { "key", required_argument, NULL, ARG_KEY },
1190 { "cert", required_argument, NULL, ARG_CERT },
1191 { "trust", required_argument, NULL, ARG_TRUST },
1192 { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG },
1197 bool type_a, type_b;
1202 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1206 return 0 /* done */;
1209 puts(PACKAGE_STRING);
1210 puts(SYSTEMD_FEATURES);
1211 return 0 /* done */;
1215 log_error("cannot currently set more than one --url");
1224 log_error("cannot currently use --getter more than once");
1228 arg_getter = optarg;
1231 case ARG_LISTEN_RAW:
1232 if (arg_listen_raw) {
1233 log_error("cannot currently use --listen-raw more than once");
1237 arg_listen_raw = optarg;
1240 case ARG_LISTEN_HTTP:
1241 if (arg_listen_http || http_socket >= 0) {
1242 log_error("cannot currently use --listen-http more than once");
1250 arg_listen_http = optarg;
1253 case ARG_LISTEN_HTTPS:
1254 if (arg_listen_https || https_socket >= 0) {
1255 log_error("cannot currently use --listen-https more than once");
1263 arg_listen_https = optarg;
1269 log_error("Key file specified twice");
1273 arg_key = strdup(optarg);
1281 log_error("Certificate file specified twice");
1285 arg_cert = strdup(optarg);
1292 if (arg_trust || arg_trust_all) {
1293 log_error("Confusing trusted CA configuration");
1297 if (streq(optarg, "all"))
1298 arg_trust_all = true;
1301 arg_trust = strdup(optarg);
1305 log_error("Option --trust is not available.");
1314 log_error("cannot use --output/-o more than once");
1318 arg_output = optarg;
1321 case ARG_SPLIT_MODE:
1322 arg_split_mode = journal_write_split_mode_from_string(optarg);
1323 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
1324 log_error("Invalid split mode: %s", optarg);
1331 r = parse_boolean(optarg);
1333 log_error("Failed to parse --compress= parameter.");
1339 arg_compress = true;
1345 r = parse_boolean(optarg);
1347 log_error("Failed to parse --seal= parameter.");
1357 case ARG_GNUTLS_LOG: {
1359 const char *word, *state;
1362 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
1365 cat = strndup(word, size);
1369 if (strv_consume(&arg_gnutls_log, cat) < 0)
1374 log_error("Option --gnutls-log is not available.");
1383 assert_not_reached("Unknown option code.");
1387 arg_files = argv + optind;
1389 type_a = arg_getter || !strv_isempty(arg_files);
1392 || arg_listen_http || arg_listen_https
1393 || sd_listen_fds(false) > 0;
1394 if (type_a && type_b) {
1395 log_error("Cannot use file input or --getter with "
1396 "--arg-listen-... or socket activation.");
1401 log_error("Option --output must be specified with file input or --getter.");
1405 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
1408 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE
1409 && arg_output && is_dir(arg_output, true) > 0) {
1410 log_error("For SplitMode=none, output must be a file.");
1414 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
1415 && arg_output && is_dir(arg_output, true) <= 0) {
1416 log_error("For SplitMode=host, output must be a directory.");
1420 log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1421 journal_write_split_mode_to_string(arg_split_mode),
1426 return 1 /* work to do */;
1429 static int load_certificates(char **key, char **cert, char **trust) {
1432 r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL);
1434 log_error("Failed to read key from file '%s': %s",
1435 arg_key ?: PRIV_KEY_FILE, strerror(-r));
1439 r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
1441 log_error("Failed to read certificate from file '%s': %s",
1442 arg_cert ?: CERT_FILE, strerror(-r));
1447 log_info("Certificate checking disabled.");
1449 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
1451 log_error("Failed to read CA certificate file '%s': %s",
1452 arg_trust ?: TRUST_FILE, strerror(-r));
1460 static int setup_gnutls_logger(char **categories) {
1461 if (!arg_listen_http && !arg_listen_https)
1469 gnutls_global_set_log_function(log_func_gnutls);
1472 STRV_FOREACH(cat, categories) {
1473 r = log_enable_gnutls_category(*cat);
1478 log_reset_gnutls_level();
1485 int main(int argc, char **argv) {
1486 RemoteServer s = {};
1488 _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
1490 log_show_color(true);
1491 log_parse_environment();
1495 return EXIT_FAILURE;
1497 r = parse_argv(argc, argv);
1499 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1501 r = setup_gnutls_logger(arg_gnutls_log);
1503 return EXIT_FAILURE;
1505 if (arg_listen_https || https_socket >= 0)
1506 if (load_certificates(&key, &cert, &trust) < 0)
1507 return EXIT_FAILURE;
1509 if (remoteserver_init(&s, key, cert, trust) < 0)
1510 return EXIT_FAILURE;
1512 sd_event_set_watchdog(s.events, true);
1514 log_debug("%s running as pid "PID_FMT,
1515 program_invocation_short_name, getpid());
1518 "STATUS=Processing requests...");
1521 r = sd_event_get_state(s.events);
1524 if (r == SD_EVENT_FINISHED)
1527 r = sd_event_run(s.events, -1);
1529 log_error("Failed to run event loop: %s", strerror(-r));
1535 log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
1537 sd_notify(false, "STATUS=Shutting down...");
1543 return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;