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_blocking_source_event(sd_event_source *event,
300 static int dispatch_raw_connection_event(sd_event_source *event,
304 static int dispatch_http_event(sd_event_source *event,
309 static int get_source_for_fd(RemoteServer *s,
310 int fd, char *name, RemoteSource **source) {
314 /* This takes ownership of name, but only on success. */
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;
365 /* This takes ownership of name, even on failure, if own_name is true. */
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));
385 r = sd_event_add_io(s->events, &source->event,
386 fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI,
387 dispatch_raw_source_event, s);
389 log_debug("Falling back to sd_event_add_defer for fd:%d (%s)", fd, name);
390 r = sd_event_add_defer(s->events, &source->event,
391 dispatch_blocking_source_event, source);
393 sd_event_source_set_enabled(source->event, SD_EVENT_ON);
396 log_error("Failed to register event source for fd:%d: %s",
401 r = sd_event_source_set_name(source->event, name);
403 log_error("Failed to set source name for fd:%d: %s", fd, strerror(-r));
407 return 1; /* work to do */
410 remove_source(s, fd);
414 static int add_raw_socket(RemoteServer *s, int fd) {
416 _cleanup_close_ int fd_ = fd;
417 char name[strlen("raw-socket-") + DECIMAL_STR_MAX(int)];
421 r = sd_event_add_io(s->events, &s->listen_event,
423 dispatch_raw_connection_event, s);
427 snprintf(name, sizeof(name), "raw-socket-%d", fd);
429 r = sd_event_source_set_name(s->listen_event, name);
438 static int setup_raw_socket(RemoteServer *s, const char *address) {
441 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
445 return add_raw_socket(s, fd);
448 /**********************************************************************
449 **********************************************************************
450 **********************************************************************/
452 static RemoteSource *request_meta(void **connection_cls, int fd, char *hostname) {
453 RemoteSource *source;
457 assert(connection_cls);
459 return *connection_cls;
461 r = get_writer(server, hostname, &writer);
463 log_warning("Failed to get writer for source %s: %s",
464 hostname, strerror(-r));
468 source = source_new(fd, true, hostname, writer);
471 writer_unref(writer);
475 log_debug("Added RemoteSource as connection metadata %p", source);
477 *connection_cls = source;
481 static void request_meta_free(void *cls,
482 struct MHD_Connection *connection,
483 void **connection_cls,
484 enum MHD_RequestTerminationCode toe) {
487 assert(connection_cls);
490 log_debug("Cleaning up connection metadata %p", s);
492 *connection_cls = NULL;
495 static int process_http_upload(
496 struct MHD_Connection *connection,
497 const char *upload_data,
498 size_t *upload_data_size,
499 RemoteSource *source) {
501 bool finished = false;
507 log_debug("request_handler_upload: connection %p, %zu bytes",
508 connection, *upload_data_size);
510 if (*upload_data_size) {
511 log_debug("Received %zu bytes", *upload_data_size);
513 r = push_data(source, upload_data, *upload_data_size);
515 return mhd_respond_oom(connection);
517 *upload_data_size = 0;
522 r = process_source(source, arg_compress, arg_seal);
523 if (r == -EAGAIN || r == -EWOULDBLOCK)
526 log_warning("Failed to process data for connection %p", connection);
528 return mhd_respondf(connection,
529 MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
530 "Entry is too large, maximum is %u bytes.\n",
533 return mhd_respondf(connection,
534 MHD_HTTP_UNPROCESSABLE_ENTITY,
535 "Processing failed: %s.", strerror(-r));
542 /* The upload is finished */
544 remaining = source_non_empty(source);
546 log_warning("Premature EOFbyte. %zu bytes lost.", remaining);
547 return mhd_respondf(connection, MHD_HTTP_EXPECTATION_FAILED,
548 "Premature EOF. %zu bytes of trailing data not processed.",
552 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
555 static int request_handler(
557 struct MHD_Connection *connection,
561 const char *upload_data,
562 size_t *upload_data_size,
563 void **connection_cls) {
567 _cleanup_free_ char *hostname = NULL;
570 assert(connection_cls);
574 log_debug("Handling a connection %s %s %s", method, url, version);
577 return process_http_upload(connection,
578 upload_data, upload_data_size,
581 if (!streq(method, "POST"))
582 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
583 "Unsupported method.\n");
585 if (!streq(url, "/upload"))
586 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
589 header = MHD_lookup_connection_value(connection,
590 MHD_HEADER_KIND, "Content-Type");
591 if (!header || !streq(header, "application/vnd.fdo.journal"))
592 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
593 "Content-Type: application/vnd.fdo.journal"
597 const union MHD_ConnectionInfo *ci;
599 ci = MHD_get_connection_info(connection,
600 MHD_CONNECTION_INFO_CONNECTION_FD);
602 log_error("MHD_get_connection_info failed: cannot get remote fd");
603 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
604 "Cannot check remote address");
611 if (server->check_trust) {
612 r = check_permissions(connection, &code, &hostname);
616 r = getnameinfo_pretty(fd, &hostname);
618 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
619 "Cannot check remote hostname");
625 if (!request_meta(connection_cls, fd, hostname))
626 return respond_oom(connection);
631 static int setup_microhttpd_server(RemoteServer *s,
636 struct MHD_OptionItem opts[] = {
637 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
638 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
639 { MHD_OPTION_LISTEN_SOCKET, fd},
647 MHD_USE_PEDANTIC_CHECKS |
648 MHD_USE_EPOLL_LINUX_ONLY |
651 const union MHD_DaemonInfo *info;
657 r = fd_nonblock(fd, true);
659 log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
666 opts[opts_pos++] = (struct MHD_OptionItem)
667 {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key};
668 opts[opts_pos++] = (struct MHD_OptionItem)
669 {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert};
671 flags |= MHD_USE_SSL;
674 opts[opts_pos++] = (struct MHD_OptionItem)
675 {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
678 d = new(MHDDaemonWrapper, 1);
682 d->fd = (uint64_t) fd;
684 d->daemon = MHD_start_daemon(flags, 0,
686 request_handler, NULL,
687 MHD_OPTION_ARRAY, opts,
690 log_error("Failed to start µhttp daemon");
695 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
696 key ? "HTTPS" : "HTTP", fd, d);
699 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
701 log_error("µhttp returned NULL daemon info");
706 epoll_fd = info->listen_fd;
708 log_error("µhttp epoll fd is invalid");
713 r = sd_event_add_io(s->events, &d->event,
715 dispatch_http_event, d);
717 log_error("Failed to add event callback: %s", strerror(-r));
721 r = sd_event_source_set_name(d->event, "epoll-fd");
723 log_error("Failed to set source name: %s", strerror(-r));
727 r = hashmap_ensure_allocated(&s->daemons, &uint64_hash_ops);
733 r = hashmap_put(s->daemons, &d->fd, d);
735 log_error("Failed to add daemon to hashmap: %s", strerror(-r));
743 MHD_stop_daemon(d->daemon);
749 static int setup_microhttpd_socket(RemoteServer *s,
756 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
760 return setup_microhttpd_server(s, fd, key, cert, trust);
763 static int dispatch_http_event(sd_event_source *event,
767 MHDDaemonWrapper *d = userdata;
772 r = MHD_run(d->daemon);
774 log_error("MHD_run failed!");
775 // XXX: unregister daemon
779 return 1; /* work to do */
782 /**********************************************************************
783 **********************************************************************
784 **********************************************************************/
786 static int setup_signals(RemoteServer *s) {
792 assert_se(sigemptyset(&mask) == 0);
793 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
794 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
796 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, NULL, s);
800 r = sd_event_source_set_name(s->sigterm_event, "sigterm");
804 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, NULL, s);
808 r = sd_event_source_set_name(s->sigint_event, "sigint");
815 static int negative_fd(const char *spec) {
816 /* Return a non-positive number as its inverse, -EINVAL otherwise. */
820 r = safe_atoi(spec, &fd);
830 static int remoteserver_init(RemoteServer *s,
839 if ((arg_listen_raw || arg_listen_http) && trust) {
840 log_error("Option --trust makes all non-HTTPS connections untrusted.");
844 r = sd_event_default(&s->events);
846 log_error("Failed to allocate event loop: %s", strerror(-r));
852 assert(server == NULL);
855 r = init_writer_hashmap(s);
859 n = sd_listen_fds(true);
861 log_error("Failed to read listening file descriptors from environment: %s",
865 log_info("Received %d descriptors", n);
867 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
868 log_error("Received fewer sockets than expected");
872 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
873 if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
874 log_info("Received a listening socket (fd:%d)", fd);
876 if (fd == http_socket)
877 r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
878 else if (fd == https_socket)
879 r = setup_microhttpd_server(s, fd, key, cert, trust);
881 r = add_raw_socket(s, fd);
882 } else if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
885 r = getnameinfo_pretty(fd, &hostname);
887 log_error("Failed to retrieve remote name: %s", strerror(-r));
891 log_info("Received a connection socket (fd:%d) from %s", fd, hostname);
893 r = add_source(s, fd, hostname, true);
895 log_error("Unknown socket passed on fd:%d", fd);
901 log_error("Failed to register socket (fd:%d): %s",
908 const char *url, *hostname;
910 url = strappenda(arg_url, "/entries");
913 log_info("Spawning getter %s...", url);
914 fd = spawn_getter(arg_getter, url);
916 log_info("Spawning curl %s...", url);
917 fd = spawn_curl(url);
923 startswith(arg_url, "https://") ?:
924 startswith(arg_url, "http://") ?:
927 r = add_source(s, fd, (char*) hostname, false);
932 if (arg_listen_raw) {
933 log_info("Listening on a socket...");
934 r = setup_raw_socket(s, arg_listen_raw);
939 if (arg_listen_http) {
940 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
945 if (arg_listen_https) {
946 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
951 STRV_FOREACH(file, arg_files) {
952 const char *output_name;
954 if (streq(*file, "-")) {
955 log_info("Using standard input as source.");
958 output_name = "stdin";
960 log_info("Reading file %s...", *file);
962 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
964 log_error("Failed to open %s: %m", *file);
970 r = add_source(s, fd, (char*) output_name, false);
975 if (s->active == 0) {
976 log_error("Zarro sources specified");
980 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
981 /* In this case we know what the writer will be
982 called, so we can create it and verify that we can
983 create output as expected. */
984 r = get_writer(s, NULL, &s->_single_writer);
992 static void server_destroy(RemoteServer *s) {
996 while ((d = hashmap_steal_first(s->daemons))) {
997 MHD_stop_daemon(d->daemon);
998 sd_event_source_unref(d->event);
1002 hashmap_free(s->daemons);
1004 assert(s->sources_size == 0 || s->sources);
1005 for (i = 0; i < s->sources_size; i++)
1006 remove_source(s, i);
1009 writer_unref(s->_single_writer);
1010 hashmap_free(s->writers);
1012 sd_event_source_unref(s->sigterm_event);
1013 sd_event_source_unref(s->sigint_event);
1014 sd_event_source_unref(s->listen_event);
1015 sd_event_unref(s->events);
1017 /* fds that we're listening on remain open... */
1020 /**********************************************************************
1021 **********************************************************************
1022 **********************************************************************/
1024 static int dispatch_raw_source_event(sd_event_source *event,
1029 RemoteServer *s = userdata;
1030 RemoteSource *source;
1033 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
1034 source = s->sources[fd];
1035 assert(source->fd == fd);
1037 r = process_source(source, arg_compress, arg_seal);
1038 if (source->state == STATE_EOF) {
1041 log_info("EOF reached with source fd:%d (%s)",
1042 source->fd, source->name);
1044 remaining = source_non_empty(source);
1046 log_warning("Premature EOF. %zu bytes lost.", remaining);
1047 remove_source(s, source->fd);
1048 log_info("%zd active sources remaining", s->active);
1050 } else if (r == -E2BIG) {
1051 log_error("Entry too big, skipped");
1053 } else if (r == -EAGAIN) {
1056 log_info("Closing connection: %s", strerror(-r));
1057 remove_source(server, fd);
1063 static int dispatch_blocking_source_event(sd_event_source *event,
1065 RemoteSource *source = userdata;
1067 return dispatch_raw_source_event(event, source->fd, EPOLLIN, server);
1070 static int accept_connection(const char* type, int fd,
1071 SocketAddress *addr, char **hostname) {
1074 log_debug("Accepting new %s connection on fd:%d", type, fd);
1075 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
1077 log_error("accept() on fd:%d failed: %m", fd);
1081 switch(socket_address_family(addr)) {
1084 _cleanup_free_ char *a = NULL;
1087 r = socket_address_print(addr, &a);
1089 log_error("socket_address_print(): %s", strerror(-r));
1094 r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
1100 log_info("Accepted %s %s connection from %s",
1102 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
1110 log_error("Rejected %s connection with unsupported family %d",
1111 type, socket_address_family(addr));
1118 static int dispatch_raw_connection_event(sd_event_source *event,
1122 RemoteServer *s = userdata;
1124 SocketAddress addr = {
1125 .size = sizeof(union sockaddr_union),
1126 .type = SOCK_STREAM,
1130 fd2 = accept_connection("raw", fd, &addr, &hostname);
1134 return add_source(s, fd2, hostname, true);
1137 /**********************************************************************
1138 **********************************************************************
1139 **********************************************************************/
1141 static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
1142 [JOURNAL_WRITE_SPLIT_NONE] = "none",
1143 [JOURNAL_WRITE_SPLIT_HOST] = "host",
1146 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode);
1147 static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
1148 journal_write_split_mode,
1149 JournalWriteSplitMode,
1150 "Failed to parse split mode setting");
1152 static int parse_config(void) {
1153 const ConfigTableItem items[] = {
1154 { "Remote", "SplitMode", config_parse_write_split_mode, 0, &arg_split_mode },
1155 { "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
1156 { "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
1157 { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
1160 return config_parse(NULL, PKGSYSCONFDIR "/journal-remote.conf", NULL,
1162 config_item_table_lookup, items,
1163 false, false, true, NULL);
1166 static void help(void) {
1167 printf("%s [OPTIONS...] {FILE|-}...\n\n"
1168 "Write external journal events to journal file(s).\n\n"
1169 " -h --help Show this help\n"
1170 " --version Show package version\n"
1171 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
1172 " --getter=COMMAND Read events from the output of COMMAND\n"
1173 " --listen-raw=ADDR Listen for connections at ADDR\n"
1174 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
1175 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
1176 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
1177 " --compress[=BOOL] Use XZ-compression in the output journal (default: yes)\n"
1178 " --seal[=BOOL] Use Event sealing in the output journal (default: no)\n"
1179 " --key=FILENAME Specify key in PEM format (default:\n"
1180 " \"" PRIV_KEY_FILE "\")\n"
1181 " --cert=FILENAME Specify certificate in PEM format (default:\n"
1182 " \"" CERT_FILE "\")\n"
1183 " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
1184 " \"" TRUST_FILE "\")\n"
1185 " --gnutls-log=CATEGORY...\n"
1186 " Specify a list of gnutls logging categories\n"
1188 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
1189 , program_invocation_short_name);
1192 static int parse_argv(int argc, char *argv[]) {
1194 ARG_VERSION = 0x100,
1209 static const struct option options[] = {
1210 { "help", no_argument, NULL, 'h' },
1211 { "version", no_argument, NULL, ARG_VERSION },
1212 { "url", required_argument, NULL, ARG_URL },
1213 { "getter", required_argument, NULL, ARG_GETTER },
1214 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
1215 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1216 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1217 { "output", required_argument, NULL, 'o' },
1218 { "split-mode", required_argument, NULL, ARG_SPLIT_MODE },
1219 { "compress", optional_argument, NULL, ARG_COMPRESS },
1220 { "seal", optional_argument, NULL, ARG_SEAL },
1221 { "key", required_argument, NULL, ARG_KEY },
1222 { "cert", required_argument, NULL, ARG_CERT },
1223 { "trust", required_argument, NULL, ARG_TRUST },
1224 { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG },
1229 bool type_a, type_b;
1234 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1238 return 0 /* done */;
1241 puts(PACKAGE_STRING);
1242 puts(SYSTEMD_FEATURES);
1243 return 0 /* done */;
1247 log_error("cannot currently set more than one --url");
1256 log_error("cannot currently use --getter more than once");
1260 arg_getter = optarg;
1263 case ARG_LISTEN_RAW:
1264 if (arg_listen_raw) {
1265 log_error("cannot currently use --listen-raw more than once");
1269 arg_listen_raw = optarg;
1272 case ARG_LISTEN_HTTP:
1273 if (arg_listen_http || http_socket >= 0) {
1274 log_error("cannot currently use --listen-http more than once");
1278 r = negative_fd(optarg);
1282 arg_listen_http = optarg;
1285 case ARG_LISTEN_HTTPS:
1286 if (arg_listen_https || https_socket >= 0) {
1287 log_error("cannot currently use --listen-https more than once");
1291 r = negative_fd(optarg);
1295 arg_listen_https = optarg;
1301 log_error("Key file specified twice");
1305 arg_key = strdup(optarg);
1313 log_error("Certificate file specified twice");
1317 arg_cert = strdup(optarg);
1324 if (arg_trust || arg_trust_all) {
1325 log_error("Confusing trusted CA configuration");
1329 if (streq(optarg, "all"))
1330 arg_trust_all = true;
1333 arg_trust = strdup(optarg);
1337 log_error("Option --trust is not available.");
1346 log_error("cannot use --output/-o more than once");
1350 arg_output = optarg;
1353 case ARG_SPLIT_MODE:
1354 arg_split_mode = journal_write_split_mode_from_string(optarg);
1355 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
1356 log_error("Invalid split mode: %s", optarg);
1363 r = parse_boolean(optarg);
1365 log_error("Failed to parse --compress= parameter.");
1371 arg_compress = true;
1377 r = parse_boolean(optarg);
1379 log_error("Failed to parse --seal= parameter.");
1389 case ARG_GNUTLS_LOG: {
1391 const char *word, *state;
1394 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
1397 cat = strndup(word, size);
1401 if (strv_consume(&arg_gnutls_log, cat) < 0)
1406 log_error("Option --gnutls-log is not available.");
1415 assert_not_reached("Unknown option code.");
1419 arg_files = argv + optind;
1421 type_a = arg_getter || !strv_isempty(arg_files);
1424 || arg_listen_http || arg_listen_https
1425 || sd_listen_fds(false) > 0;
1426 if (type_a && type_b) {
1427 log_error("Cannot use file input or --getter with "
1428 "--arg-listen-... or socket activation.");
1433 log_error("Option --output must be specified with file input or --getter.");
1437 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
1440 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE
1441 && arg_output && is_dir(arg_output, true) > 0) {
1442 log_error("For SplitMode=none, output must be a file.");
1446 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
1447 && arg_output && is_dir(arg_output, true) <= 0) {
1448 log_error("For SplitMode=host, output must be a directory.");
1452 log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1453 journal_write_split_mode_to_string(arg_split_mode),
1458 return 1 /* work to do */;
1461 static int load_certificates(char **key, char **cert, char **trust) {
1464 r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL);
1466 log_error("Failed to read key from file '%s': %s",
1467 arg_key ?: PRIV_KEY_FILE, strerror(-r));
1471 r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
1473 log_error("Failed to read certificate from file '%s': %s",
1474 arg_cert ?: CERT_FILE, strerror(-r));
1479 log_info("Certificate checking disabled.");
1481 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
1483 log_error("Failed to read CA certificate file '%s': %s",
1484 arg_trust ?: TRUST_FILE, strerror(-r));
1492 static int setup_gnutls_logger(char **categories) {
1493 if (!arg_listen_http && !arg_listen_https)
1501 gnutls_global_set_log_function(log_func_gnutls);
1504 STRV_FOREACH(cat, categories) {
1505 r = log_enable_gnutls_category(*cat);
1510 log_reset_gnutls_level();
1517 int main(int argc, char **argv) {
1518 RemoteServer s = {};
1520 _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
1522 log_show_color(true);
1523 log_parse_environment();
1527 return EXIT_FAILURE;
1529 r = parse_argv(argc, argv);
1531 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1533 r = setup_gnutls_logger(arg_gnutls_log);
1535 return EXIT_FAILURE;
1537 if (arg_listen_https || https_socket >= 0)
1538 if (load_certificates(&key, &cert, &trust) < 0)
1539 return EXIT_FAILURE;
1541 if (remoteserver_init(&s, key, cert, trust) < 0)
1542 return EXIT_FAILURE;
1544 sd_event_set_watchdog(s.events, true);
1546 log_debug("%s running as pid "PID_FMT,
1547 program_invocation_short_name, getpid());
1550 "STATUS=Processing requests...");
1553 r = sd_event_get_state(s.events);
1556 if (r == SD_EVENT_FINISHED)
1559 r = sd_event_run(s.events, -1);
1561 log_error("Failed to run event loop: %s", strerror(-r));
1568 "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count);
1569 log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
1577 return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;