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");
588 if (server->check_trust) {
589 r = check_permissions(connection, &code, &hostname);
593 r = getnameinfo_pretty(fd, &hostname);
595 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
596 "Cannot check remote hostname");
602 if (!request_meta(connection_cls, fd, hostname))
603 return respond_oom(connection);
608 static int setup_microhttpd_server(RemoteServer *s,
613 struct MHD_OptionItem opts[] = {
614 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
615 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
616 { MHD_OPTION_LISTEN_SOCKET, fd},
624 MHD_USE_PEDANTIC_CHECKS |
625 MHD_USE_EPOLL_LINUX_ONLY |
628 const union MHD_DaemonInfo *info;
634 r = fd_nonblock(fd, true);
636 log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
643 opts[opts_pos++] = (struct MHD_OptionItem)
644 {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key};
645 opts[opts_pos++] = (struct MHD_OptionItem)
646 {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert};
648 flags |= MHD_USE_SSL;
651 opts[opts_pos++] = (struct MHD_OptionItem)
652 {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
655 d = new(MHDDaemonWrapper, 1);
659 d->fd = (uint64_t) fd;
661 d->daemon = MHD_start_daemon(flags, 0,
663 request_handler, NULL,
664 MHD_OPTION_ARRAY, opts,
667 log_error("Failed to start µhttp daemon");
672 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
673 key ? "HTTPS" : "HTTP", fd, d);
676 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
678 log_error("µhttp returned NULL daemon info");
683 epoll_fd = info->listen_fd;
685 log_error("µhttp epoll fd is invalid");
690 r = sd_event_add_io(s->events, &d->event,
692 dispatch_http_event, d);
694 log_error("Failed to add event callback: %s", strerror(-r));
698 r = hashmap_ensure_allocated(&s->daemons, uint64_hash_func, uint64_compare_func);
704 r = hashmap_put(s->daemons, &d->fd, d);
706 log_error("Failed to add daemon to hashmap: %s", strerror(-r));
714 MHD_stop_daemon(d->daemon);
720 static int setup_microhttpd_socket(RemoteServer *s,
727 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
731 return setup_microhttpd_server(s, fd, key, cert, trust);
734 static int dispatch_http_event(sd_event_source *event,
738 MHDDaemonWrapper *d = userdata;
743 r = MHD_run(d->daemon);
745 log_error("MHD_run failed!");
746 // XXX: unregister daemon
750 return 1; /* work to do */
753 /**********************************************************************
754 **********************************************************************
755 **********************************************************************/
757 static int dispatch_sigterm(sd_event_source *event,
758 const struct signalfd_siginfo *si,
760 RemoteServer *s = userdata;
764 log_received_signal(LOG_INFO, si);
766 sd_event_exit(s->events, 0);
770 static int setup_signals(RemoteServer *s) {
776 assert_se(sigemptyset(&mask) == 0);
777 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
778 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
780 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
784 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
791 static int fd_fd(const char *spec) {
794 r = safe_atoi(spec, &fd);
803 static int remoteserver_init(RemoteServer *s,
812 if ((arg_listen_raw || arg_listen_http) && trust) {
813 log_error("Option --trust makes all non-HTTPS connections untrusted.");
817 sd_event_default(&s->events);
821 assert(server == NULL);
824 n = sd_listen_fds(true);
826 log_error("Failed to read listening file descriptors from environment: %s",
830 log_info("Received %d descriptors", n);
832 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
833 log_error("Received fewer sockets than expected");
837 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
838 if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
839 log_info("Received a listening socket (fd:%d)", fd);
841 if (fd == http_socket)
842 r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
843 else if (fd == https_socket)
844 r = setup_microhttpd_server(s, fd, key, cert, trust);
846 r = add_raw_socket(s, fd);
847 } else if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
850 r = getnameinfo_pretty(fd, &hostname);
852 log_error("Failed to retrieve remote name: %s", strerror(-r));
856 log_info("Received a connection socket (fd:%d) from %s", fd, hostname);
858 r = add_source(s, fd, hostname, true);
862 log_error("Unknown socket passed on fd:%d", fd);
868 log_error("Failed to register socket (fd:%d): %s",
875 const char *url, *hostname;
877 url = strappenda(arg_url, "/entries");
880 log_info("Spawning getter %s...", url);
881 fd = spawn_getter(arg_getter, url);
883 log_info("Spawning curl %s...", url);
884 fd = spawn_curl(url);
890 startswith(arg_url, "https://") ?:
891 startswith(arg_url, "http://") ?:
894 r = add_source(s, fd, (char*) hostname, false);
899 if (arg_listen_raw) {
900 log_info("Listening on a socket...");
901 r = setup_raw_socket(s, arg_listen_raw);
906 if (arg_listen_http) {
907 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
912 if (arg_listen_https) {
913 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
918 STRV_FOREACH(file, arg_files) {
919 const char *output_name;
921 if (streq(*file, "-")) {
922 log_info("Using standard input as source.");
925 output_name = "stdin";
927 log_info("Reading file %s...", *file);
929 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
931 log_error("Failed to open %s: %m", *file);
937 r = add_source(s, fd, (char*) output_name, false);
942 if (s->active == 0) {
943 log_error("Zarro sources specified");
947 r = init_writer_hashmap(s);
951 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
952 /* In this case we know what the writer will be
953 called, so we can create it and verify that we can
954 create output as expected. */
955 r = get_writer(s, NULL, &s->_single_writer);
963 static void server_destroy(RemoteServer *s) {
967 while ((d = hashmap_steal_first(s->daemons))) {
968 MHD_stop_daemon(d->daemon);
969 sd_event_source_unref(d->event);
973 hashmap_free(s->daemons);
975 assert(s->sources_size == 0 || s->sources);
976 for (i = 0; i < s->sources_size; i++)
980 writer_unref(s->_single_writer);
981 hashmap_free(s->writers);
983 sd_event_source_unref(s->sigterm_event);
984 sd_event_source_unref(s->sigint_event);
985 sd_event_source_unref(s->listen_event);
986 sd_event_unref(s->events);
988 /* fds that we're listening on remain open... */
991 /**********************************************************************
992 **********************************************************************
993 **********************************************************************/
995 static int dispatch_raw_source_event(sd_event_source *event,
1000 RemoteServer *s = userdata;
1001 RemoteSource *source;
1004 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
1005 source = s->sources[fd];
1006 assert(source->fd == fd);
1008 r = process_source(source, arg_compress, arg_seal);
1009 if (source->state == STATE_EOF) {
1012 log_info("EOF reached with source fd:%d (%s)",
1013 source->fd, source->name);
1015 remaining = source_non_empty(source);
1017 log_warning("Premature EOF. %zu bytes lost.", remaining);
1018 remove_source(s, source->fd);
1019 log_info("%zd active sources remaining", s->active);
1021 } else if (r == -E2BIG) {
1022 log_error("Entry too big, skipped");
1024 } else if (r == -EAGAIN) {
1027 log_info("Closing connection: %s", strerror(-r));
1028 remove_source(server, fd);
1034 static int accept_connection(const char* type, int fd,
1035 SocketAddress *addr, char **hostname) {
1038 log_debug("Accepting new %s connection on fd:%d", type, fd);
1039 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
1041 log_error("accept() on fd:%d failed: %m", fd);
1045 switch(socket_address_family(addr)) {
1048 _cleanup_free_ char *a = NULL;
1051 r = socket_address_print(addr, &a);
1053 log_error("socket_address_print(): %s", strerror(-r));
1058 r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
1064 log_info("Accepted %s %s connection from %s",
1066 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
1074 log_error("Rejected %s connection with unsupported family %d",
1075 type, socket_address_family(addr));
1082 static int dispatch_raw_connection_event(sd_event_source *event,
1086 RemoteServer *s = userdata;
1088 SocketAddress addr = {
1089 .size = sizeof(union sockaddr_union),
1090 .type = SOCK_STREAM,
1094 fd2 = accept_connection("raw", fd, &addr, &hostname);
1098 r = add_source(s, fd2, hostname, true);
1104 /**********************************************************************
1105 **********************************************************************
1106 **********************************************************************/
1108 static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
1109 [JOURNAL_WRITE_SPLIT_NONE] = "none",
1110 [JOURNAL_WRITE_SPLIT_HOST] = "host",
1113 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode);
1114 static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
1115 journal_write_split_mode,
1116 JournalWriteSplitMode,
1117 "Failed to parse split mode setting");
1119 static int parse_config(void) {
1120 const ConfigTableItem items[] = {
1121 { "Remote", "SplitMode", config_parse_write_split_mode, 0, &arg_split_mode },
1122 { "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
1123 { "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
1124 { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
1127 return config_parse(NULL, PKGSYSCONFDIR "/journal-remote.conf", NULL,
1129 config_item_table_lookup, items,
1130 false, false, true, NULL);
1133 static void help(void) {
1134 printf("%s [OPTIONS...] {FILE|-}...\n\n"
1135 "Write external journal events to journal file(s).\n\n"
1136 " -h --help Show this help\n"
1137 " --version Show package version\n"
1138 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
1139 " --getter=COMMAND Read events from the output of COMMAND\n"
1140 " --listen-raw=ADDR Listen for connections at ADDR\n"
1141 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
1142 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
1143 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
1144 " --compress[=BOOL] Use XZ-compression in the output journal (default: yes)\n"
1145 " --seal[=BOOL] Use Event sealing in the output journal (default: no)\n"
1146 " --key=FILENAME Specify key in PEM format (default:\n"
1147 " \"" PRIV_KEY_FILE "\")\n"
1148 " --cert=FILENAME Specify certificate in PEM format (default:\n"
1149 " \"" CERT_FILE "\")\n"
1150 " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
1151 " \"" TRUST_FILE "\")\n"
1152 " --gnutls-log=CATEGORY...\n"
1153 " Specify a list of gnutls logging categories\n"
1155 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
1156 , program_invocation_short_name);
1159 static int parse_argv(int argc, char *argv[]) {
1161 ARG_VERSION = 0x100,
1176 static const struct option options[] = {
1177 { "help", no_argument, NULL, 'h' },
1178 { "version", no_argument, NULL, ARG_VERSION },
1179 { "url", required_argument, NULL, ARG_URL },
1180 { "getter", required_argument, NULL, ARG_GETTER },
1181 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
1182 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1183 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1184 { "output", required_argument, NULL, 'o' },
1185 { "split-mode", required_argument, NULL, ARG_SPLIT_MODE },
1186 { "compress", optional_argument, NULL, ARG_COMPRESS },
1187 { "seal", optional_argument, NULL, ARG_SEAL },
1188 { "key", required_argument, NULL, ARG_KEY },
1189 { "cert", required_argument, NULL, ARG_CERT },
1190 { "trust", required_argument, NULL, ARG_TRUST },
1191 { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG },
1196 bool type_a, type_b;
1201 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1205 return 0 /* done */;
1208 puts(PACKAGE_STRING);
1209 puts(SYSTEMD_FEATURES);
1210 return 0 /* done */;
1214 log_error("cannot currently set more than one --url");
1223 log_error("cannot currently use --getter more than once");
1227 arg_getter = optarg;
1230 case ARG_LISTEN_RAW:
1231 if (arg_listen_raw) {
1232 log_error("cannot currently use --listen-raw more than once");
1236 arg_listen_raw = optarg;
1239 case ARG_LISTEN_HTTP:
1240 if (arg_listen_http || http_socket >= 0) {
1241 log_error("cannot currently use --listen-http more than once");
1249 arg_listen_http = optarg;
1252 case ARG_LISTEN_HTTPS:
1253 if (arg_listen_https || https_socket >= 0) {
1254 log_error("cannot currently use --listen-https more than once");
1262 arg_listen_https = optarg;
1268 log_error("Key file specified twice");
1272 arg_key = strdup(optarg);
1280 log_error("Certificate file specified twice");
1284 arg_cert = strdup(optarg);
1291 if (arg_trust || arg_trust_all) {
1292 log_error("Confusing trusted CA configuration");
1296 if (streq(optarg, "all"))
1297 arg_trust_all = true;
1300 arg_trust = strdup(optarg);
1304 log_error("Option --trust is not available.");
1313 log_error("cannot use --output/-o more than once");
1317 arg_output = optarg;
1320 case ARG_SPLIT_MODE:
1321 arg_split_mode = journal_write_split_mode_from_string(optarg);
1322 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
1323 log_error("Invalid split mode: %s", optarg);
1330 r = parse_boolean(optarg);
1332 log_error("Failed to parse --compress= parameter.");
1338 arg_compress = true;
1344 r = parse_boolean(optarg);
1346 log_error("Failed to parse --seal= parameter.");
1356 case ARG_GNUTLS_LOG: {
1358 const char *word, *state;
1361 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
1364 cat = strndup(word, size);
1368 if (strv_consume(&arg_gnutls_log, cat) < 0)
1373 log_error("Option --gnutls-log is not available.");
1382 assert_not_reached("Unknown option code.");
1386 arg_files = argv + optind;
1388 type_a = arg_getter || !strv_isempty(arg_files);
1391 || arg_listen_http || arg_listen_https
1392 || sd_listen_fds(false) > 0;
1393 if (type_a && type_b) {
1394 log_error("Cannot use file input or --getter with "
1395 "--arg-listen-... or socket activation.");
1400 log_error("Option --output must be specified with file input or --getter.");
1404 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
1407 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE
1408 && arg_output && is_dir(arg_output, true) > 0) {
1409 log_error("For SplitMode=none, output must be a file.");
1413 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
1414 && arg_output && is_dir(arg_output, true) <= 0) {
1415 log_error("For SplitMode=host, output must be a directory.");
1419 log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1420 journal_write_split_mode_to_string(arg_split_mode),
1425 return 1 /* work to do */;
1428 static int load_certificates(char **key, char **cert, char **trust) {
1431 r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL);
1433 log_error("Failed to read key from file '%s': %s",
1434 arg_key ?: PRIV_KEY_FILE, strerror(-r));
1438 r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
1440 log_error("Failed to read certificate from file '%s': %s",
1441 arg_cert ?: CERT_FILE, strerror(-r));
1446 log_info("Certificate checking disabled.");
1448 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
1450 log_error("Failed to read CA certificate file '%s': %s",
1451 arg_trust ?: TRUST_FILE, strerror(-r));
1459 static int setup_gnutls_logger(char **categories) {
1460 if (!arg_listen_http && !arg_listen_https)
1468 gnutls_global_set_log_function(log_func_gnutls);
1471 STRV_FOREACH(cat, categories) {
1472 r = log_enable_gnutls_category(*cat);
1477 log_reset_gnutls_level();
1484 int main(int argc, char **argv) {
1485 RemoteServer s = {};
1487 _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
1489 log_show_color(true);
1490 log_parse_environment();
1494 return EXIT_FAILURE;
1496 r = parse_argv(argc, argv);
1498 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1500 r = setup_gnutls_logger(arg_gnutls_log);
1502 return EXIT_FAILURE;
1504 if (arg_listen_https || https_socket >= 0)
1505 if (load_certificates(&key, &cert, &trust) < 0)
1506 return EXIT_FAILURE;
1508 if (remoteserver_init(&s, key, cert, trust) < 0)
1509 return EXIT_FAILURE;
1511 sd_event_set_watchdog(s.events, true);
1513 log_debug("%s running as pid "PID_FMT,
1514 program_invocation_short_name, getpid());
1517 "STATUS=Processing requests...");
1520 r = sd_event_get_state(s.events);
1523 if (r == SD_EVENT_FINISHED)
1526 r = sd_event_run(s.events, -1);
1528 log_error("Failed to run event loop: %s", strerror(-r));
1535 "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count);
1536 log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
1544 return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;