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 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 words = strv_split_quoted(getter);
156 r = strv_extend(&words, url);
158 log_error("Failed to create command line: %s", strerror(-r));
162 r = spawn_child(words[0], words);
164 log_error("Failed to spawn getter %s: %m", getter);
169 #define filename_escape(s) xescape((s), "/ ")
171 static int open_output(Writer *w, const char* host) {
172 _cleanup_free_ char *_output = NULL;
176 switch (arg_split_mode) {
177 case JOURNAL_WRITE_SPLIT_NONE:
178 output = arg_output ?: REMOTE_JOURNAL_PATH "/remote.journal";
181 case JOURNAL_WRITE_SPLIT_HOST: {
182 _cleanup_free_ char *name;
186 name = filename_escape(host);
190 r = asprintf(&_output, "%s/remote-%s.journal",
191 arg_output ?: REMOTE_JOURNAL_PATH,
201 assert_not_reached("what?");
204 r = journal_file_open_reliably(output,
205 O_RDWR|O_CREAT, 0640,
206 arg_compress, arg_seal,
211 log_error("Failed to open output journal %s: %s",
212 output, strerror(-r));
214 log_info("Opened output file %s", w->journal->path);
218 /**********************************************************************
219 **********************************************************************
220 **********************************************************************/
222 static int init_writer_hashmap(RemoteServer *s) {
223 static const struct {
224 hash_func_t hash_func;
225 compare_func_t compare_func;
227 [JOURNAL_WRITE_SPLIT_NONE] = {trivial_hash_func,
228 trivial_compare_func},
229 [JOURNAL_WRITE_SPLIT_HOST] = {string_hash_func,
230 string_compare_func},
233 assert(arg_split_mode >= 0 && arg_split_mode < (int) ELEMENTSOF(functions));
235 s->writers = hashmap_new(functions[arg_split_mode].hash_func,
236 functions[arg_split_mode].compare_func);
243 static int get_writer(RemoteServer *s, const char *host,
246 _cleanup_writer_unref_ Writer *w = NULL;
249 switch(arg_split_mode) {
250 case JOURNAL_WRITE_SPLIT_NONE:
251 key = "one and only";
254 case JOURNAL_WRITE_SPLIT_HOST:
260 assert_not_reached("what split mode?");
263 w = hashmap_get(s->writers, key);
271 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST) {
272 w->hashmap_key = strdup(key);
277 r = open_output(w, host);
281 r = hashmap_put(s->writers, w->hashmap_key ?: key, w);
291 /**********************************************************************
292 **********************************************************************
293 **********************************************************************/
295 /* This should go away as soon as µhttpd allows state to be passed around. */
296 static RemoteServer *server;
298 static int dispatch_raw_source_event(sd_event_source *event,
302 static int dispatch_raw_connection_event(sd_event_source *event,
306 static int dispatch_http_event(sd_event_source *event,
311 static int get_source_for_fd(RemoteServer *s,
312 int fd, char *name, RemoteSource **source) {
319 if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
322 r = get_writer(s, name, &writer);
324 log_warning("Failed to get writer for source %s: %s",
329 if (s->sources[fd] == NULL) {
330 s->sources[fd] = source_new(fd, false, name, writer);
331 if (!s->sources[fd]) {
332 writer_unref(writer);
339 *source = s->sources[fd];
343 static int remove_source(RemoteServer *s, int fd) {
344 RemoteSource *source;
347 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
349 source = s->sources[fd];
351 /* this closes fd too */
353 s->sources[fd] = NULL;
360 static int add_source(RemoteServer *s, int fd, char* name, bool own_name) {
362 RemoteSource *source;
375 r = get_source_for_fd(s, fd, name, &source);
377 log_error("Failed to create source for fd:%d (%s): %s",
378 fd, name, strerror(-r));
382 r = sd_event_add_io(s->events, &source->event,
383 fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI,
384 dispatch_raw_source_event, s);
386 log_error("Failed to register event source for fd:%d: %s",
391 return 1; /* work to do */
394 remove_source(s, fd);
398 static int add_raw_socket(RemoteServer *s, int fd) {
401 r = sd_event_add_io(s->events, &s->listen_event,
403 dispatch_raw_connection_event, s);
413 static int setup_raw_socket(RemoteServer *s, const char *address) {
416 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
420 return add_raw_socket(s, fd);
423 /**********************************************************************
424 **********************************************************************
425 **********************************************************************/
427 static RemoteSource *request_meta(void **connection_cls, int fd, char *hostname) {
428 RemoteSource *source;
432 assert(connection_cls);
434 return *connection_cls;
436 r = get_writer(server, hostname, &writer);
438 log_warning("Failed to get writer for source %s: %s",
439 hostname, strerror(-r));
443 source = source_new(fd, true, hostname, writer);
446 writer_unref(writer);
450 log_debug("Added RemoteSource as connection metadata %p", source);
452 *connection_cls = source;
456 static void request_meta_free(void *cls,
457 struct MHD_Connection *connection,
458 void **connection_cls,
459 enum MHD_RequestTerminationCode toe) {
462 assert(connection_cls);
465 log_debug("Cleaning up connection metadata %p", s);
467 *connection_cls = NULL;
470 static int process_http_upload(
471 struct MHD_Connection *connection,
472 const char *upload_data,
473 size_t *upload_data_size,
474 RemoteSource *source) {
476 bool finished = false;
482 log_debug("request_handler_upload: connection %p, %zu bytes",
483 connection, *upload_data_size);
485 if (*upload_data_size) {
486 log_debug("Received %zu bytes", *upload_data_size);
488 r = push_data(source, upload_data, *upload_data_size);
490 return mhd_respond_oom(connection);
492 *upload_data_size = 0;
497 r = process_source(source, arg_compress, arg_seal);
498 if (r == -EAGAIN || r == -EWOULDBLOCK)
501 log_warning("Failed to process data for connection %p", connection);
503 return mhd_respondf(connection,
504 MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
505 "Entry is too large, maximum is %u bytes.\n",
508 return mhd_respondf(connection,
509 MHD_HTTP_UNPROCESSABLE_ENTITY,
510 "Processing failed: %s.", strerror(-r));
517 /* The upload is finished */
519 remaining = source_non_empty(source);
521 log_warning("Premature EOFbyte. %zu bytes lost.", remaining);
522 return mhd_respondf(connection, MHD_HTTP_EXPECTATION_FAILED,
523 "Premature EOF. %zu bytes of trailing data not processed.",
527 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
530 static int request_handler(
532 struct MHD_Connection *connection,
536 const char *upload_data,
537 size_t *upload_data_size,
538 void **connection_cls) {
542 _cleanup_free_ char *hostname = NULL;
545 assert(connection_cls);
549 log_debug("Handling a connection %s %s %s", method, url, version);
552 return process_http_upload(connection,
553 upload_data, upload_data_size,
556 if (!streq(method, "POST"))
557 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
558 "Unsupported method.\n");
560 if (!streq(url, "/upload"))
561 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
564 header = MHD_lookup_connection_value(connection,
565 MHD_HEADER_KIND, "Content-Type");
566 if (!header || !streq(header, "application/vnd.fdo.journal"))
567 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
568 "Content-Type: application/vnd.fdo.journal"
572 const union MHD_ConnectionInfo *ci;
574 ci = MHD_get_connection_info(connection,
575 MHD_CONNECTION_INFO_CONNECTION_FD);
577 log_error("MHD_get_connection_info failed: cannot get remote fd");
578 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
579 "Cannot check remote address");
587 if (server->check_trust) {
588 r = check_permissions(connection, &code, &hostname);
592 r = getnameinfo_pretty(fd, &hostname);
594 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
595 "Cannot check remote hostname");
601 if (!request_meta(connection_cls, fd, hostname))
602 return respond_oom(connection);
607 static int setup_microhttpd_server(RemoteServer *s,
612 struct MHD_OptionItem opts[] = {
613 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
614 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
615 { MHD_OPTION_LISTEN_SOCKET, fd},
623 MHD_USE_PEDANTIC_CHECKS |
624 MHD_USE_EPOLL_LINUX_ONLY |
627 const union MHD_DaemonInfo *info;
633 r = fd_nonblock(fd, true);
635 log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
642 opts[opts_pos++] = (struct MHD_OptionItem)
643 {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key};
644 opts[opts_pos++] = (struct MHD_OptionItem)
645 {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert};
647 flags |= MHD_USE_SSL;
650 opts[opts_pos++] = (struct MHD_OptionItem)
651 {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
654 d = new(MHDDaemonWrapper, 1);
658 d->fd = (uint64_t) fd;
660 d->daemon = MHD_start_daemon(flags, 0,
662 request_handler, NULL,
663 MHD_OPTION_ARRAY, opts,
666 log_error("Failed to start µhttp daemon");
671 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
672 key ? "HTTPS" : "HTTP", fd, d);
675 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
677 log_error("µhttp returned NULL daemon info");
682 epoll_fd = info->listen_fd;
684 log_error("µhttp epoll fd is invalid");
689 r = sd_event_add_io(s->events, &d->event,
691 dispatch_http_event, d);
693 log_error("Failed to add event callback: %s", strerror(-r));
697 r = hashmap_ensure_allocated(&s->daemons, uint64_hash_func, uint64_compare_func);
703 r = hashmap_put(s->daemons, &d->fd, d);
705 log_error("Failed to add daemon to hashmap: %s", strerror(-r));
713 MHD_stop_daemon(d->daemon);
719 static int setup_microhttpd_socket(RemoteServer *s,
726 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
730 return setup_microhttpd_server(s, fd, key, cert, trust);
733 static int dispatch_http_event(sd_event_source *event,
737 MHDDaemonWrapper *d = userdata;
742 r = MHD_run(d->daemon);
744 log_error("MHD_run failed!");
745 // XXX: unregister daemon
749 return 1; /* work to do */
752 /**********************************************************************
753 **********************************************************************
754 **********************************************************************/
756 static int dispatch_sigterm(sd_event_source *event,
757 const struct signalfd_siginfo *si,
759 RemoteServer *s = userdata;
763 log_received_signal(LOG_INFO, si);
765 sd_event_exit(s->events, 0);
769 static int setup_signals(RemoteServer *s) {
775 assert_se(sigemptyset(&mask) == 0);
776 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
777 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
779 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
783 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
790 static int fd_fd(const char *spec) {
793 r = safe_atoi(spec, &fd);
801 static int remoteserver_init(RemoteServer *s,
806 const char *output_name = NULL;
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",
873 output_name = "socket";
877 const char *url, *hostname;
879 url = strappenda(arg_url, "/entries");
882 log_info("Spawning getter %s...", url);
883 fd = spawn_getter(arg_getter, url);
885 log_info("Spawning curl %s...", url);
886 fd = spawn_curl(url);
892 startswith(arg_url, "https://") ?:
893 startswith(arg_url, "http://") ?:
896 r = add_source(s, fd, (char*) hostname, false);
900 output_name = arg_url;
903 if (arg_listen_raw) {
904 log_info("Listening on a socket...");
905 r = setup_raw_socket(s, arg_listen_raw);
909 output_name = arg_listen_raw;
912 if (arg_listen_http) {
913 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
917 output_name = arg_listen_http;
920 if (arg_listen_https) {
921 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
925 output_name = arg_listen_https;
928 STRV_FOREACH(file, arg_files) {
929 if (streq(*file, "-")) {
930 log_info("Using standard input as source.");
933 output_name = "stdin";
935 log_info("Reading file %s...", *file);
937 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
939 log_error("Failed to open %s: %m", *file);
945 r = add_source(s, fd, (char*) output_name, false);
950 if (s->active == 0) {
951 log_error("Zarro sources specified");
955 if (!!n + !!arg_url + !!arg_listen_raw + !!arg_files)
956 output_name = "multiple";
958 r = init_writer_hashmap(s);
962 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
963 /* In this case we know what the writer will be
964 called, so we can create it and verify that we can
965 create output as expected. */
966 r = get_writer(s, NULL, &s->_single_writer);
974 static void server_destroy(RemoteServer *s) {
978 while ((d = hashmap_steal_first(s->daemons))) {
979 MHD_stop_daemon(d->daemon);
980 sd_event_source_unref(d->event);
984 hashmap_free(s->daemons);
986 assert(s->sources_size == 0 || s->sources);
987 for (i = 0; i < s->sources_size; i++)
991 writer_unref(s->_single_writer);
992 hashmap_free(s->writers);
994 sd_event_source_unref(s->sigterm_event);
995 sd_event_source_unref(s->sigint_event);
996 sd_event_source_unref(s->listen_event);
997 sd_event_unref(s->events);
999 /* fds that we're listening on remain open... */
1002 /**********************************************************************
1003 **********************************************************************
1004 **********************************************************************/
1006 static int dispatch_raw_source_event(sd_event_source *event,
1011 RemoteServer *s = userdata;
1012 RemoteSource *source;
1015 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
1016 source = s->sources[fd];
1017 assert(source->fd == fd);
1019 r = process_source(source, arg_compress, arg_seal);
1020 if (source->state == STATE_EOF) {
1023 log_info("EOF reached with source fd:%d (%s)",
1024 source->fd, source->name);
1026 remaining = source_non_empty(source);
1028 log_warning("Premature EOF. %zu bytes lost.", remaining);
1029 remove_source(s, source->fd);
1030 log_info("%zd active sources remaining", s->active);
1032 } else if (r == -E2BIG) {
1033 log_error("Entry too big, skipped");
1035 } else if (r == -EAGAIN) {
1038 log_info("Closing connection: %s", strerror(-r));
1039 remove_source(server, fd);
1045 static int accept_connection(const char* type, int fd,
1046 SocketAddress *addr, char **hostname) {
1049 log_debug("Accepting new %s connection on fd:%d", type, fd);
1050 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
1052 log_error("accept() on fd:%d failed: %m", fd);
1056 switch(socket_address_family(addr)) {
1059 _cleanup_free_ char *a = NULL;
1062 r = socket_address_print(addr, &a);
1064 log_error("socket_address_print(): %s", strerror(-r));
1069 r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
1075 log_info("Accepted %s %s connection from %s",
1077 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
1085 log_error("Rejected %s connection with unsupported family %d",
1086 type, socket_address_family(addr));
1093 static int dispatch_raw_connection_event(sd_event_source *event,
1097 RemoteServer *s = userdata;
1099 SocketAddress addr = {
1100 .size = sizeof(union sockaddr_union),
1101 .type = SOCK_STREAM,
1105 fd2 = accept_connection("raw", fd, &addr, &hostname);
1109 r = add_source(s, fd2, hostname, true);
1115 /**********************************************************************
1116 **********************************************************************
1117 **********************************************************************/
1119 static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
1120 [JOURNAL_WRITE_SPLIT_NONE] = "none",
1121 [JOURNAL_WRITE_SPLIT_HOST] = "host",
1124 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode);
1125 static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
1126 journal_write_split_mode,
1127 JournalWriteSplitMode,
1128 "Failed to parse split mode setting");
1130 static int parse_config(void) {
1131 const ConfigTableItem items[] = {
1132 { "Remote", "SplitMode", config_parse_write_split_mode, 0, &arg_split_mode },
1133 { "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
1134 { "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
1135 { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
1139 r = config_parse(NULL, PKGSYSCONFDIR "/journal-remote.conf", NULL,
1141 config_item_table_lookup, items,
1142 false, false, NULL);
1144 log_error("Failed to parse configuration file: %s", strerror(-r));
1149 static void help(void) {
1150 printf("%s [OPTIONS...] {FILE|-}...\n\n"
1151 "Write external journal events to journal file(s).\n\n"
1153 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
1154 " --getter=COMMAND Read events from the output of COMMAND\n"
1155 " --listen-raw=ADDR Listen for connections at ADDR\n"
1156 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
1157 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
1158 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
1159 " --[no-]compress Use XZ-compression in the output journal (default: yes)\n"
1160 " --[no-]seal Use Event sealing in the output journal (default: no)\n"
1161 " --key=FILENAME Specify key in PEM format (default:\n"
1162 " \"" KEY_FILE "\")\n"
1163 " --cert=FILENAME Specify certificate in PEM format (default:\n"
1164 " \"" CERT_FILE "\")\n"
1165 " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
1166 " \"" TRUST_FILE "\")\n"
1167 " --gnutls-log=CATEGORY...\n"
1168 " Specify a list of gnutls logging categories\n"
1169 " -h --help Show this help and exit\n"
1170 " --version Print version string and exit\n"
1172 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
1173 , program_invocation_short_name);
1176 static int parse_argv(int argc, char *argv[]) {
1178 ARG_VERSION = 0x100,
1195 static const struct option options[] = {
1196 { "help", no_argument, NULL, 'h' },
1197 { "version", no_argument, NULL, ARG_VERSION },
1198 { "url", required_argument, NULL, ARG_URL },
1199 { "getter", required_argument, NULL, ARG_GETTER },
1200 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
1201 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1202 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1203 { "output", required_argument, NULL, 'o' },
1204 { "split-mode", required_argument, NULL, ARG_SPLIT_MODE },
1205 { "compress", no_argument, NULL, ARG_COMPRESS },
1206 { "no-compress", no_argument, NULL, ARG_NO_COMPRESS },
1207 { "seal", no_argument, NULL, ARG_SEAL },
1208 { "no-seal", no_argument, NULL, ARG_NO_SEAL },
1209 { "key", required_argument, NULL, ARG_KEY },
1210 { "cert", required_argument, NULL, ARG_CERT },
1211 { "trust", required_argument, NULL, ARG_TRUST },
1212 { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG },
1217 bool type_a, type_b;
1222 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1226 return 0 /* done */;
1229 puts(PACKAGE_STRING);
1230 puts(SYSTEMD_FEATURES);
1231 return 0 /* done */;
1235 log_error("cannot currently set more than one --url");
1244 log_error("cannot currently use --getter more than once");
1248 arg_getter = optarg;
1251 case ARG_LISTEN_RAW:
1252 if (arg_listen_raw) {
1253 log_error("cannot currently use --listen-raw more than once");
1257 arg_listen_raw = optarg;
1260 case ARG_LISTEN_HTTP:
1261 if (arg_listen_http || http_socket >= 0) {
1262 log_error("cannot currently use --listen-http more than once");
1270 arg_listen_http = optarg;
1273 case ARG_LISTEN_HTTPS:
1274 if (arg_listen_https || https_socket >= 0) {
1275 log_error("cannot currently use --listen-https more than once");
1283 arg_listen_https = optarg;
1289 log_error("Key file specified twice");
1293 arg_key = strdup(optarg);
1301 log_error("Certificate file specified twice");
1305 arg_cert = strdup(optarg);
1312 if (arg_trust || arg_trust_all) {
1313 log_error("Confusing trusted CA configuration");
1317 if (streq(optarg, "all"))
1318 arg_trust_all = true;
1321 arg_trust = strdup(optarg);
1325 log_error("Option --trust is not available.");
1334 log_error("cannot use --output/-o more than once");
1338 arg_output = optarg;
1341 case ARG_SPLIT_MODE:
1342 arg_split_mode = journal_write_split_mode_from_string(optarg);
1343 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
1344 log_error("Invalid split mode: %s", optarg);
1350 arg_compress = true;
1352 case ARG_NO_COMPRESS:
1353 arg_compress = false;
1362 case ARG_GNUTLS_LOG: {
1367 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
1370 cat = strndup(word, size);
1374 if (strv_consume(&arg_gnutls_log, cat) < 0)
1379 log_error("Option --gnutls-log is not available.");
1388 log_error("Unknown option code %c", c);
1393 arg_files = argv + optind;
1395 type_a = arg_getter || !strv_isempty(arg_files);
1398 || arg_listen_http || arg_listen_https
1399 || sd_listen_fds(false) > 0;
1400 if (type_a && type_b) {
1401 log_error("Cannot use file input or --getter with "
1402 "--arg-listen-... or socket activation.");
1407 log_error("Option --output must be specified with file input or --getter.");
1411 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
1414 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE
1415 && arg_output && is_dir(arg_output, true) > 0) {
1416 log_error("For SplitMode=none, output must be a file.");
1420 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
1421 && arg_output && is_dir(arg_output, true) <= 0) {
1422 log_error("For SplitMode=host, output must be a directory.");
1426 log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1427 journal_write_split_mode_to_string(arg_split_mode),
1432 return 1 /* work to do */;
1435 static int load_certificates(char **key, char **cert, char **trust) {
1438 r = read_full_file(arg_key ?: KEY_FILE, key, NULL);
1440 log_error("Failed to read key from file '%s': %s",
1441 arg_key ?: KEY_FILE, strerror(-r));
1445 r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
1447 log_error("Failed to read certificate from file '%s': %s",
1448 arg_cert ?: CERT_FILE, strerror(-r));
1453 log_info("Certificate checking disabled.");
1455 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
1457 log_error("Failed to read CA certificate file '%s': %s",
1458 arg_trust ?: TRUST_FILE, strerror(-r));
1466 static int setup_gnutls_logger(char **categories) {
1467 if (!arg_listen_http && !arg_listen_https)
1475 gnutls_global_set_log_function(log_func_gnutls);
1478 STRV_FOREACH(cat, categories) {
1479 r = log_enable_gnutls_category(*cat);
1484 log_reset_gnutls_level();
1491 int main(int argc, char **argv) {
1492 RemoteServer s = {};
1494 _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
1496 log_show_color(true);
1497 log_parse_environment();
1501 return EXIT_FAILURE;
1503 r = parse_argv(argc, argv);
1505 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1507 r = setup_gnutls_logger(arg_gnutls_log);
1509 return EXIT_FAILURE;
1511 if (arg_listen_https || https_socket >= 0)
1512 if (load_certificates(&key, &cert, &trust) < 0)
1513 return EXIT_FAILURE;
1515 if (remoteserver_init(&s, key, cert, trust) < 0)
1516 return EXIT_FAILURE;
1518 sd_event_set_watchdog(s.events, true);
1520 log_debug("%s running as pid "PID_FMT,
1521 program_invocation_short_name, getpid());
1524 "STATUS=Processing requests...");
1527 r = sd_event_get_state(s.events);
1530 if (r == SD_EVENT_FINISHED)
1533 r = sd_event_run(s.events, -1);
1535 log_error("Failed to run event loop: %s", strerror(-r));
1541 log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
1543 sd_notify(false, "STATUS=Shutting down...");
1549 return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;