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 hash_ops *hash_ops[] = {
226 [JOURNAL_WRITE_SPLIT_NONE] = NULL,
227 [JOURNAL_WRITE_SPLIT_HOST] = &string_hash_ops,
230 assert(arg_split_mode >= 0 && arg_split_mode < (int) ELEMENTSOF(hash_ops));
232 s->writers = hashmap_new(hash_ops[arg_split_mode]);
239 static int get_writer(RemoteServer *s, const char *host,
242 _cleanup_writer_unref_ Writer *w = NULL;
245 switch(arg_split_mode) {
246 case JOURNAL_WRITE_SPLIT_NONE:
247 key = "one and only";
250 case JOURNAL_WRITE_SPLIT_HOST:
256 assert_not_reached("what split mode?");
259 w = hashmap_get(s->writers, key);
267 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST) {
268 w->hashmap_key = strdup(key);
273 r = open_output(w, host);
277 r = hashmap_put(s->writers, w->hashmap_key ?: key, w);
287 /**********************************************************************
288 **********************************************************************
289 **********************************************************************/
291 /* This should go away as soon as µhttpd allows state to be passed around. */
292 static RemoteServer *server;
294 static int dispatch_raw_source_event(sd_event_source *event,
298 static int dispatch_raw_connection_event(sd_event_source *event,
302 static int dispatch_http_event(sd_event_source *event,
307 static int get_source_for_fd(RemoteServer *s,
308 int fd, char *name, RemoteSource **source) {
315 if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
318 r = get_writer(s, name, &writer);
320 log_warning("Failed to get writer for source %s: %s",
325 if (s->sources[fd] == NULL) {
326 s->sources[fd] = source_new(fd, false, name, writer);
327 if (!s->sources[fd]) {
328 writer_unref(writer);
335 *source = s->sources[fd];
339 static int remove_source(RemoteServer *s, int fd) {
340 RemoteSource *source;
343 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
345 source = s->sources[fd];
347 /* this closes fd too */
349 s->sources[fd] = NULL;
356 static int add_source(RemoteServer *s, int fd, char* name, bool own_name) {
358 RemoteSource *source;
371 r = get_source_for_fd(s, fd, name, &source);
373 log_error("Failed to create source for fd:%d (%s): %s",
374 fd, name, strerror(-r));
378 r = sd_event_add_io(s->events, &source->event,
379 fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI,
380 dispatch_raw_source_event, s);
382 log_error("Failed to register event source for fd:%d: %s",
387 return 1; /* work to do */
390 remove_source(s, fd);
394 static int add_raw_socket(RemoteServer *s, int fd) {
397 r = sd_event_add_io(s->events, &s->listen_event,
399 dispatch_raw_connection_event, s);
409 static int setup_raw_socket(RemoteServer *s, const char *address) {
412 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
416 return add_raw_socket(s, fd);
419 /**********************************************************************
420 **********************************************************************
421 **********************************************************************/
423 static RemoteSource *request_meta(void **connection_cls, int fd, char *hostname) {
424 RemoteSource *source;
428 assert(connection_cls);
430 return *connection_cls;
432 r = get_writer(server, hostname, &writer);
434 log_warning("Failed to get writer for source %s: %s",
435 hostname, strerror(-r));
439 source = source_new(fd, true, hostname, writer);
442 writer_unref(writer);
446 log_debug("Added RemoteSource as connection metadata %p", source);
448 *connection_cls = source;
452 static void request_meta_free(void *cls,
453 struct MHD_Connection *connection,
454 void **connection_cls,
455 enum MHD_RequestTerminationCode toe) {
458 assert(connection_cls);
461 log_debug("Cleaning up connection metadata %p", s);
463 *connection_cls = NULL;
466 static int process_http_upload(
467 struct MHD_Connection *connection,
468 const char *upload_data,
469 size_t *upload_data_size,
470 RemoteSource *source) {
472 bool finished = false;
478 log_debug("request_handler_upload: connection %p, %zu bytes",
479 connection, *upload_data_size);
481 if (*upload_data_size) {
482 log_debug("Received %zu bytes", *upload_data_size);
484 r = push_data(source, upload_data, *upload_data_size);
486 return mhd_respond_oom(connection);
488 *upload_data_size = 0;
493 r = process_source(source, arg_compress, arg_seal);
494 if (r == -EAGAIN || r == -EWOULDBLOCK)
497 log_warning("Failed to process data for connection %p", connection);
499 return mhd_respondf(connection,
500 MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
501 "Entry is too large, maximum is %u bytes.\n",
504 return mhd_respondf(connection,
505 MHD_HTTP_UNPROCESSABLE_ENTITY,
506 "Processing failed: %s.", strerror(-r));
513 /* The upload is finished */
515 remaining = source_non_empty(source);
517 log_warning("Premature EOFbyte. %zu bytes lost.", remaining);
518 return mhd_respondf(connection, MHD_HTTP_EXPECTATION_FAILED,
519 "Premature EOF. %zu bytes of trailing data not processed.",
523 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
526 static int request_handler(
528 struct MHD_Connection *connection,
532 const char *upload_data,
533 size_t *upload_data_size,
534 void **connection_cls) {
538 _cleanup_free_ char *hostname = NULL;
541 assert(connection_cls);
545 log_debug("Handling a connection %s %s %s", method, url, version);
548 return process_http_upload(connection,
549 upload_data, upload_data_size,
552 if (!streq(method, "POST"))
553 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
554 "Unsupported method.\n");
556 if (!streq(url, "/upload"))
557 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
560 header = MHD_lookup_connection_value(connection,
561 MHD_HEADER_KIND, "Content-Type");
562 if (!header || !streq(header, "application/vnd.fdo.journal"))
563 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
564 "Content-Type: application/vnd.fdo.journal"
568 const union MHD_ConnectionInfo *ci;
570 ci = MHD_get_connection_info(connection,
571 MHD_CONNECTION_INFO_CONNECTION_FD);
573 log_error("MHD_get_connection_info failed: cannot get remote fd");
574 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
575 "Cannot check remote address");
582 if (server->check_trust) {
583 r = check_permissions(connection, &code, &hostname);
587 r = getnameinfo_pretty(fd, &hostname);
589 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
590 "Cannot check remote hostname");
596 if (!request_meta(connection_cls, fd, hostname))
597 return respond_oom(connection);
602 static int setup_microhttpd_server(RemoteServer *s,
607 struct MHD_OptionItem opts[] = {
608 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
609 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
610 { MHD_OPTION_LISTEN_SOCKET, fd},
618 MHD_USE_PEDANTIC_CHECKS |
619 MHD_USE_EPOLL_LINUX_ONLY |
622 const union MHD_DaemonInfo *info;
628 r = fd_nonblock(fd, true);
630 log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
637 opts[opts_pos++] = (struct MHD_OptionItem)
638 {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key};
639 opts[opts_pos++] = (struct MHD_OptionItem)
640 {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert};
642 flags |= MHD_USE_SSL;
645 opts[opts_pos++] = (struct MHD_OptionItem)
646 {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
649 d = new(MHDDaemonWrapper, 1);
653 d->fd = (uint64_t) fd;
655 d->daemon = MHD_start_daemon(flags, 0,
657 request_handler, NULL,
658 MHD_OPTION_ARRAY, opts,
661 log_error("Failed to start µhttp daemon");
666 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
667 key ? "HTTPS" : "HTTP", fd, d);
670 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
672 log_error("µhttp returned NULL daemon info");
677 epoll_fd = info->listen_fd;
679 log_error("µhttp epoll fd is invalid");
684 r = sd_event_add_io(s->events, &d->event,
686 dispatch_http_event, d);
688 log_error("Failed to add event callback: %s", strerror(-r));
692 r = hashmap_ensure_allocated(&s->daemons, &uint64_hash_ops);
698 r = hashmap_put(s->daemons, &d->fd, d);
700 log_error("Failed to add daemon to hashmap: %s", strerror(-r));
708 MHD_stop_daemon(d->daemon);
714 static int setup_microhttpd_socket(RemoteServer *s,
721 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
725 return setup_microhttpd_server(s, fd, key, cert, trust);
728 static int dispatch_http_event(sd_event_source *event,
732 MHDDaemonWrapper *d = userdata;
737 r = MHD_run(d->daemon);
739 log_error("MHD_run failed!");
740 // XXX: unregister daemon
744 return 1; /* work to do */
747 /**********************************************************************
748 **********************************************************************
749 **********************************************************************/
751 static int dispatch_sigterm(sd_event_source *event,
752 const struct signalfd_siginfo *si,
754 RemoteServer *s = userdata;
758 log_received_signal(LOG_INFO, si);
760 sd_event_exit(s->events, 0);
764 static int setup_signals(RemoteServer *s) {
770 assert_se(sigemptyset(&mask) == 0);
771 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
772 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
774 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
778 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
785 static int fd_fd(const char *spec) {
788 r = safe_atoi(spec, &fd);
797 static int remoteserver_init(RemoteServer *s,
806 if ((arg_listen_raw || arg_listen_http) && trust) {
807 log_error("Option --trust makes all non-HTTPS connections untrusted.");
811 r = sd_event_default(&s->events);
813 log_error("Failed to allocate event loop: %s", strerror(-r));
819 assert(server == NULL);
822 n = sd_listen_fds(true);
824 log_error("Failed to read listening file descriptors from environment: %s",
828 log_info("Received %d descriptors", n);
830 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
831 log_error("Received fewer sockets than expected");
835 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
836 if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
837 log_info("Received a listening socket (fd:%d)", fd);
839 if (fd == http_socket)
840 r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
841 else if (fd == https_socket)
842 r = setup_microhttpd_server(s, fd, key, cert, trust);
844 r = add_raw_socket(s, fd);
845 } else if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
848 r = getnameinfo_pretty(fd, &hostname);
850 log_error("Failed to retrieve remote name: %s", strerror(-r));
854 log_info("Received a connection socket (fd:%d) from %s", fd, hostname);
856 r = add_source(s, fd, hostname, true);
860 log_error("Unknown socket passed on fd:%d", fd);
866 log_error("Failed to register socket (fd:%d): %s",
873 const char *url, *hostname;
875 url = strappenda(arg_url, "/entries");
878 log_info("Spawning getter %s...", url);
879 fd = spawn_getter(arg_getter, url);
881 log_info("Spawning curl %s...", url);
882 fd = spawn_curl(url);
888 startswith(arg_url, "https://") ?:
889 startswith(arg_url, "http://") ?:
892 r = add_source(s, fd, (char*) hostname, false);
897 if (arg_listen_raw) {
898 log_info("Listening on a socket...");
899 r = setup_raw_socket(s, arg_listen_raw);
904 if (arg_listen_http) {
905 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
910 if (arg_listen_https) {
911 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
916 STRV_FOREACH(file, arg_files) {
917 const char *output_name;
919 if (streq(*file, "-")) {
920 log_info("Using standard input as source.");
923 output_name = "stdin";
925 log_info("Reading file %s...", *file);
927 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
929 log_error("Failed to open %s: %m", *file);
935 r = add_source(s, fd, (char*) output_name, false);
940 if (s->active == 0) {
941 log_error("Zarro sources specified");
945 r = init_writer_hashmap(s);
949 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
950 /* In this case we know what the writer will be
951 called, so we can create it and verify that we can
952 create output as expected. */
953 r = get_writer(s, NULL, &s->_single_writer);
961 static void server_destroy(RemoteServer *s) {
965 while ((d = hashmap_steal_first(s->daemons))) {
966 MHD_stop_daemon(d->daemon);
967 sd_event_source_unref(d->event);
971 hashmap_free(s->daemons);
973 assert(s->sources_size == 0 || s->sources);
974 for (i = 0; i < s->sources_size; i++)
978 writer_unref(s->_single_writer);
979 hashmap_free(s->writers);
981 sd_event_source_unref(s->sigterm_event);
982 sd_event_source_unref(s->sigint_event);
983 sd_event_source_unref(s->listen_event);
984 sd_event_unref(s->events);
986 /* fds that we're listening on remain open... */
989 /**********************************************************************
990 **********************************************************************
991 **********************************************************************/
993 static int dispatch_raw_source_event(sd_event_source *event,
998 RemoteServer *s = userdata;
999 RemoteSource *source;
1002 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
1003 source = s->sources[fd];
1004 assert(source->fd == fd);
1006 r = process_source(source, arg_compress, arg_seal);
1007 if (source->state == STATE_EOF) {
1010 log_info("EOF reached with source fd:%d (%s)",
1011 source->fd, source->name);
1013 remaining = source_non_empty(source);
1015 log_warning("Premature EOF. %zu bytes lost.", remaining);
1016 remove_source(s, source->fd);
1017 log_info("%zd active sources remaining", s->active);
1019 } else if (r == -E2BIG) {
1020 log_error("Entry too big, skipped");
1022 } else if (r == -EAGAIN) {
1025 log_info("Closing connection: %s", strerror(-r));
1026 remove_source(server, fd);
1032 static int accept_connection(const char* type, int fd,
1033 SocketAddress *addr, char **hostname) {
1036 log_debug("Accepting new %s connection on fd:%d", type, fd);
1037 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
1039 log_error("accept() on fd:%d failed: %m", fd);
1043 switch(socket_address_family(addr)) {
1046 _cleanup_free_ char *a = NULL;
1049 r = socket_address_print(addr, &a);
1051 log_error("socket_address_print(): %s", strerror(-r));
1056 r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
1062 log_info("Accepted %s %s connection from %s",
1064 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
1072 log_error("Rejected %s connection with unsupported family %d",
1073 type, socket_address_family(addr));
1080 static int dispatch_raw_connection_event(sd_event_source *event,
1084 RemoteServer *s = userdata;
1086 SocketAddress addr = {
1087 .size = sizeof(union sockaddr_union),
1088 .type = SOCK_STREAM,
1092 fd2 = accept_connection("raw", fd, &addr, &hostname);
1096 r = add_source(s, fd2, hostname, true);
1102 /**********************************************************************
1103 **********************************************************************
1104 **********************************************************************/
1106 static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
1107 [JOURNAL_WRITE_SPLIT_NONE] = "none",
1108 [JOURNAL_WRITE_SPLIT_HOST] = "host",
1111 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode);
1112 static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
1113 journal_write_split_mode,
1114 JournalWriteSplitMode,
1115 "Failed to parse split mode setting");
1117 static int parse_config(void) {
1118 const ConfigTableItem items[] = {
1119 { "Remote", "SplitMode", config_parse_write_split_mode, 0, &arg_split_mode },
1120 { "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
1121 { "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
1122 { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
1125 return config_parse(NULL, PKGSYSCONFDIR "/journal-remote.conf", NULL,
1127 config_item_table_lookup, items,
1128 false, false, true, NULL);
1131 static void help(void) {
1132 printf("%s [OPTIONS...] {FILE|-}...\n\n"
1133 "Write external journal events to journal file(s).\n\n"
1134 " -h --help Show this help\n"
1135 " --version Show package version\n"
1136 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
1137 " --getter=COMMAND Read events from the output of COMMAND\n"
1138 " --listen-raw=ADDR Listen for connections at ADDR\n"
1139 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
1140 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
1141 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
1142 " --compress[=BOOL] Use XZ-compression in the output journal (default: yes)\n"
1143 " --seal[=BOOL] Use Event sealing in the output journal (default: no)\n"
1144 " --key=FILENAME Specify key in PEM format (default:\n"
1145 " \"" PRIV_KEY_FILE "\")\n"
1146 " --cert=FILENAME Specify certificate in PEM format (default:\n"
1147 " \"" CERT_FILE "\")\n"
1148 " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
1149 " \"" TRUST_FILE "\")\n"
1150 " --gnutls-log=CATEGORY...\n"
1151 " Specify a list of gnutls logging categories\n"
1153 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
1154 , program_invocation_short_name);
1157 static int parse_argv(int argc, char *argv[]) {
1159 ARG_VERSION = 0x100,
1174 static const struct option options[] = {
1175 { "help", no_argument, NULL, 'h' },
1176 { "version", no_argument, NULL, ARG_VERSION },
1177 { "url", required_argument, NULL, ARG_URL },
1178 { "getter", required_argument, NULL, ARG_GETTER },
1179 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
1180 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1181 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1182 { "output", required_argument, NULL, 'o' },
1183 { "split-mode", required_argument, NULL, ARG_SPLIT_MODE },
1184 { "compress", optional_argument, NULL, ARG_COMPRESS },
1185 { "seal", optional_argument, NULL, ARG_SEAL },
1186 { "key", required_argument, NULL, ARG_KEY },
1187 { "cert", required_argument, NULL, ARG_CERT },
1188 { "trust", required_argument, NULL, ARG_TRUST },
1189 { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG },
1194 bool type_a, type_b;
1199 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1203 return 0 /* done */;
1206 puts(PACKAGE_STRING);
1207 puts(SYSTEMD_FEATURES);
1208 return 0 /* done */;
1212 log_error("cannot currently set more than one --url");
1221 log_error("cannot currently use --getter more than once");
1225 arg_getter = optarg;
1228 case ARG_LISTEN_RAW:
1229 if (arg_listen_raw) {
1230 log_error("cannot currently use --listen-raw more than once");
1234 arg_listen_raw = optarg;
1237 case ARG_LISTEN_HTTP:
1238 if (arg_listen_http || http_socket >= 0) {
1239 log_error("cannot currently use --listen-http more than once");
1247 arg_listen_http = optarg;
1250 case ARG_LISTEN_HTTPS:
1251 if (arg_listen_https || https_socket >= 0) {
1252 log_error("cannot currently use --listen-https more than once");
1260 arg_listen_https = optarg;
1266 log_error("Key file specified twice");
1270 arg_key = strdup(optarg);
1278 log_error("Certificate file specified twice");
1282 arg_cert = strdup(optarg);
1289 if (arg_trust || arg_trust_all) {
1290 log_error("Confusing trusted CA configuration");
1294 if (streq(optarg, "all"))
1295 arg_trust_all = true;
1298 arg_trust = strdup(optarg);
1302 log_error("Option --trust is not available.");
1311 log_error("cannot use --output/-o more than once");
1315 arg_output = optarg;
1318 case ARG_SPLIT_MODE:
1319 arg_split_mode = journal_write_split_mode_from_string(optarg);
1320 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
1321 log_error("Invalid split mode: %s", optarg);
1328 r = parse_boolean(optarg);
1330 log_error("Failed to parse --compress= parameter.");
1336 arg_compress = true;
1342 r = parse_boolean(optarg);
1344 log_error("Failed to parse --seal= parameter.");
1354 case ARG_GNUTLS_LOG: {
1356 const char *word, *state;
1359 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
1362 cat = strndup(word, size);
1366 if (strv_consume(&arg_gnutls_log, cat) < 0)
1371 log_error("Option --gnutls-log is not available.");
1380 assert_not_reached("Unknown option code.");
1384 arg_files = argv + optind;
1386 type_a = arg_getter || !strv_isempty(arg_files);
1389 || arg_listen_http || arg_listen_https
1390 || sd_listen_fds(false) > 0;
1391 if (type_a && type_b) {
1392 log_error("Cannot use file input or --getter with "
1393 "--arg-listen-... or socket activation.");
1398 log_error("Option --output must be specified with file input or --getter.");
1402 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
1405 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE
1406 && arg_output && is_dir(arg_output, true) > 0) {
1407 log_error("For SplitMode=none, output must be a file.");
1411 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
1412 && arg_output && is_dir(arg_output, true) <= 0) {
1413 log_error("For SplitMode=host, output must be a directory.");
1417 log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1418 journal_write_split_mode_to_string(arg_split_mode),
1423 return 1 /* work to do */;
1426 static int load_certificates(char **key, char **cert, char **trust) {
1429 r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL);
1431 log_error("Failed to read key from file '%s': %s",
1432 arg_key ?: PRIV_KEY_FILE, strerror(-r));
1436 r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
1438 log_error("Failed to read certificate from file '%s': %s",
1439 arg_cert ?: CERT_FILE, strerror(-r));
1444 log_info("Certificate checking disabled.");
1446 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
1448 log_error("Failed to read CA certificate file '%s': %s",
1449 arg_trust ?: TRUST_FILE, strerror(-r));
1457 static int setup_gnutls_logger(char **categories) {
1458 if (!arg_listen_http && !arg_listen_https)
1466 gnutls_global_set_log_function(log_func_gnutls);
1469 STRV_FOREACH(cat, categories) {
1470 r = log_enable_gnutls_category(*cat);
1475 log_reset_gnutls_level();
1482 int main(int argc, char **argv) {
1483 RemoteServer s = {};
1485 _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
1487 log_show_color(true);
1488 log_parse_environment();
1492 return EXIT_FAILURE;
1494 r = parse_argv(argc, argv);
1496 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1498 r = setup_gnutls_logger(arg_gnutls_log);
1500 return EXIT_FAILURE;
1502 if (arg_listen_https || https_socket >= 0)
1503 if (load_certificates(&key, &cert, &trust) < 0)
1504 return EXIT_FAILURE;
1506 if (remoteserver_init(&s, key, cert, trust) < 0)
1507 return EXIT_FAILURE;
1509 sd_event_set_watchdog(s.events, true);
1511 log_debug("%s running as pid "PID_FMT,
1512 program_invocation_short_name, getpid());
1515 "STATUS=Processing requests...");
1518 r = sd_event_get_state(s.events);
1521 if (r == SD_EVENT_FINISHED)
1524 r = sd_event_run(s.events, -1);
1526 log_error("Failed to run event loop: %s", strerror(-r));
1533 "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count);
1534 log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
1542 return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;