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/>.
28 #include <sys/prctl.h>
29 #include <sys/socket.h>
31 #include <sys/types.h>
35 #include "sd-daemon.h"
37 #include "journal-file.h"
38 #include "journald-native.h"
39 #include "socket-util.h"
45 #include "conf-parser.h"
46 #include "microhttpd-util.h"
49 #include <gnutls/gnutls.h>
52 #include "journal-remote-parse.h"
53 #include "journal-remote-write.h"
55 #define REMOTE_JOURNAL_PATH "/var/log/journal/" SD_ID128_FORMAT_STR "/remote-%s.journal"
57 #define KEY_FILE CERTIFICATE_ROOT "/private/journal-remote.pem"
58 #define CERT_FILE CERTIFICATE_ROOT "/certs/journal-remote.pem"
59 #define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
61 static char* arg_output = NULL;
62 static char* arg_url = NULL;
63 static char* arg_getter = NULL;
64 static char* arg_listen_raw = NULL;
65 static char* arg_listen_http = NULL;
66 static char* arg_listen_https = NULL;
67 static char** arg_files = NULL;
68 static int arg_compress = true;
69 static int arg_seal = false;
70 static int http_socket = -1, https_socket = -1;
71 static char** arg_gnutls_log = 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(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(char *getter, char *url) {
149 _cleanup_strv_free_ char **words = NULL;
152 words = strv_split_quoted(getter);
156 r = spawn_child(words[0], words);
158 log_error("Failed to spawn getter %s: %m", getter);
163 static int open_output(Writer *s, const char* url) {
164 _cleanup_free_ char *name, *output = NULL;
173 for(c = name; *c; c++) {
174 if (*c == '/' || *c == ':' || *c == ' ')
176 else if (*c == '?') {
184 r = sd_id128_get_machine(&machine);
186 log_error("failed to determine machine ID128: %s", strerror(-r));
190 r = asprintf(&output, REMOTE_JOURNAL_PATH,
191 SD_ID128_FORMAT_VAL(machine), name);
195 r = is_dir(arg_output, true);
197 r = asprintf(&output,
198 "%s/remote-%s.journal", arg_output, name);
202 output = strdup(arg_output);
208 r = journal_file_open_reliably(output,
209 O_RDWR|O_CREAT, 0640,
210 arg_compress, arg_seal,
215 log_error("Failed to open output journal %s: %s",
216 arg_output, strerror(-r));
218 log_info("Opened output file %s", s->journal->path);
222 /**********************************************************************
223 **********************************************************************
224 **********************************************************************/
226 typedef struct MHDDaemonWrapper {
228 struct MHD_Daemon *daemon;
230 sd_event_source *event;
233 typedef struct RemoteServer {
234 RemoteSource **sources;
239 sd_event_source *sigterm_event, *sigint_event, *listen_event;
247 /* This should go away as soon as µhttpd allows state to be passed around. */
248 static RemoteServer *server;
250 static int dispatch_raw_source_event(sd_event_source *event,
254 static int dispatch_raw_connection_event(sd_event_source *event,
258 static int dispatch_http_event(sd_event_source *event,
263 static int get_source_for_fd(RemoteServer *s, int fd, RemoteSource **source) {
267 if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
270 if (s->sources[fd] == NULL) {
271 s->sources[fd] = new0(RemoteSource, 1);
274 s->sources[fd]->fd = -1;
278 *source = s->sources[fd];
282 static int remove_source(RemoteServer *s, int fd) {
283 RemoteSource *source;
286 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
288 source = s->sources[fd];
291 s->sources[fd] = NULL;
300 static int add_source(RemoteServer *s, int fd, const char* name) {
301 RemoteSource *source = NULL;
302 _cleanup_free_ char *realname = NULL;
309 realname = strdup(name);
313 r = asprintf(&realname, "fd:%d", fd);
318 log_debug("Creating source for fd:%d (%s)", fd, realname);
320 r = get_source_for_fd(s, fd, &source);
322 log_error("Failed to create source for fd:%d (%s)", fd, realname);
326 assert(source->fd < 0);
329 r = sd_event_add_io(s->events, &source->event,
330 fd, EPOLLIN, dispatch_raw_source_event, s);
332 log_error("Failed to register event source for fd:%d: %s",
337 return 1; /* work to do */
340 remove_source(s, fd);
344 static int add_raw_socket(RemoteServer *s, int fd) {
347 r = sd_event_add_io(s->events, &s->listen_event, fd, EPOLLIN,
348 dispatch_raw_connection_event, s);
358 static int setup_raw_socket(RemoteServer *s, const char *address) {
361 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
365 return add_raw_socket(s, fd);
368 /**********************************************************************
369 **********************************************************************
370 **********************************************************************/
372 static RemoteSource *request_meta(void **connection_cls) {
373 RemoteSource *source;
375 assert(connection_cls);
377 return *connection_cls;
379 source = new0(RemoteSource, 1);
384 log_debug("Added RemoteSource as connection metadata %p", source);
386 *connection_cls = source;
390 static void request_meta_free(void *cls,
391 struct MHD_Connection *connection,
392 void **connection_cls,
393 enum MHD_RequestTerminationCode toe) {
396 assert(connection_cls);
399 log_debug("Cleaning up connection metadata %p", s);
401 *connection_cls = NULL;
404 static int process_http_upload(
405 struct MHD_Connection *connection,
406 const char *upload_data,
407 size_t *upload_data_size,
408 RemoteSource *source) {
410 bool finished = false;
415 log_debug("request_handler_upload: connection %p, %zu bytes",
416 connection, *upload_data_size);
418 if (*upload_data_size) {
419 log_info("Received %zu bytes", *upload_data_size);
421 r = push_data(source, upload_data, *upload_data_size);
423 return mhd_respond_oom(connection);
425 *upload_data_size = 0;
430 r = process_source(source, &server->writer, arg_compress, arg_seal);
431 if (r == -EAGAIN || r == -EWOULDBLOCK)
434 log_warning("Failed to process data for connection %p", connection);
436 return mhd_respondf(connection,
437 MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
438 "Entry is too large, maximum is %u bytes.\n",
441 return mhd_respondf(connection,
442 MHD_HTTP_UNPROCESSABLE_ENTITY,
443 "Processing failed: %s.", strerror(-r));
450 /* The upload is finished */
452 if (source_non_empty(source)) {
453 log_warning("EOF reached with incomplete data");
454 return mhd_respond(connection, MHD_HTTP_EXPECTATION_FAILED,
455 "Trailing data not processed.");
458 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
461 static int request_handler(
463 struct MHD_Connection *connection,
467 const char *upload_data,
468 size_t *upload_data_size,
469 void **connection_cls) {
475 assert(connection_cls);
479 log_debug("Handling a connection %s %s %s", method, url, version);
482 return process_http_upload(connection,
483 upload_data, upload_data_size,
486 if (!streq(method, "POST"))
487 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
488 "Unsupported method.\n");
490 if (!streq(url, "/upload"))
491 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
494 header = MHD_lookup_connection_value(connection,
495 MHD_HEADER_KIND, "Content-Type");
496 if (!header || !streq(header, "application/vnd.fdo.journal"))
497 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
498 "Content-Type: application/vnd.fdo.journal"
501 if (server->check_trust) {
502 r = check_permissions(connection, &code);
507 if (!request_meta(connection_cls))
508 return respond_oom(connection);
512 static int setup_microhttpd_server(RemoteServer *s,
517 struct MHD_OptionItem opts[] = {
518 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
519 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
520 { MHD_OPTION_LISTEN_SOCKET, fd},
528 MHD_USE_PEDANTIC_CHECKS |
529 MHD_USE_EPOLL_LINUX_ONLY |
532 const union MHD_DaemonInfo *info;
538 r = fd_nonblock(fd, true);
540 log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
547 opts[opts_pos++] = (struct MHD_OptionItem)
548 {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key};
549 opts[opts_pos++] = (struct MHD_OptionItem)
550 {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert};
552 flags |= MHD_USE_SSL;
555 opts[opts_pos++] = (struct MHD_OptionItem)
556 {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
559 d = new(MHDDaemonWrapper, 1);
563 d->fd = (uint64_t) fd;
565 d->daemon = MHD_start_daemon(flags, 0,
567 request_handler, NULL,
568 MHD_OPTION_ARRAY, opts,
571 log_error("Failed to start µhttp daemon");
576 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
577 key ? "HTTPS" : "HTTP", fd, d);
580 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
582 log_error("µhttp returned NULL daemon info");
587 epoll_fd = info->listen_fd;
589 log_error("µhttp epoll fd is invalid");
594 r = sd_event_add_io(s->events, &d->event,
595 epoll_fd, EPOLLIN, dispatch_http_event, d);
597 log_error("Failed to add event callback: %s", strerror(-r));
601 r = hashmap_ensure_allocated(&s->daemons, uint64_hash_func, uint64_compare_func);
607 r = hashmap_put(s->daemons, &d->fd, d);
609 log_error("Failed to add daemon to hashmap: %s", strerror(-r));
617 MHD_stop_daemon(d->daemon);
623 static int setup_microhttpd_socket(RemoteServer *s,
630 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
634 return setup_microhttpd_server(s, fd, key, cert, trust);
637 static int dispatch_http_event(sd_event_source *event,
641 MHDDaemonWrapper *d = userdata;
646 r = MHD_run(d->daemon);
648 log_error("MHD_run failed!");
649 // XXX: unregister daemon
653 return 1; /* work to do */
656 /**********************************************************************
657 **********************************************************************
658 **********************************************************************/
660 static int dispatch_sigterm(sd_event_source *event,
661 const struct signalfd_siginfo *si,
663 RemoteServer *s = userdata;
667 log_received_signal(LOG_INFO, si);
669 sd_event_exit(s->events, 0);
673 static int setup_signals(RemoteServer *s) {
679 assert_se(sigemptyset(&mask) == 0);
680 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
681 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
683 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
687 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
694 static int fd_fd(const char *spec) {
697 r = safe_atoi(spec, &fd);
705 static int remoteserver_init(RemoteServer *s,
710 const char *output_name = NULL;
716 if ((arg_listen_raw || arg_listen_http) && trust) {
717 log_error("Option --trust makes all non-HTTPS connections untrusted.");
721 sd_event_default(&s->events);
725 assert(server == NULL);
728 n = sd_listen_fds(true);
730 log_error("Failed to read listening file descriptors from environment: %s",
734 log_info("Received %d descriptors", n);
736 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
737 log_error("Received fewer sockets than expected");
741 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
742 if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
743 log_info("Received a listening socket (fd:%d)", fd);
745 if (fd == http_socket)
746 r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
747 else if (fd == https_socket)
748 r = setup_microhttpd_server(s, fd, key, cert, trust);
750 r = add_raw_socket(s, fd);
751 } else if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
752 log_info("Received a connection socket (fd:%d)", fd);
754 r = add_source(s, fd, NULL);
756 log_error("Unknown socket passed on fd:%d", fd);
762 log_error("Failed to register socket (fd:%d): %s",
767 output_name = "socket";
771 _cleanup_free_ char *url = NULL;
772 _cleanup_strv_free_ char **urlv = strv_new(arg_url, "/entries", NULL);
775 url = strv_join(urlv, "");
780 log_info("Spawning getter %s...", url);
781 fd = spawn_getter(arg_getter, url);
783 log_info("Spawning curl %s...", url);
784 fd = spawn_curl(url);
789 r = add_source(s, fd, arg_url);
793 output_name = arg_url;
796 if (arg_listen_raw) {
797 log_info("Listening on a socket...");
798 r = setup_raw_socket(s, arg_listen_raw);
802 output_name = arg_listen_raw;
805 if (arg_listen_http) {
806 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
810 output_name = arg_listen_http;
813 if (arg_listen_https) {
814 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
818 output_name = arg_listen_https;
821 STRV_FOREACH(file, arg_files) {
822 if (streq(*file, "-")) {
823 log_info("Reading standard input...");
826 output_name = "stdin";
828 log_info("Reading file %s...", *file);
830 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
832 log_error("Failed to open %s: %m", *file);
838 r = add_source(s, fd, output_name);
843 if (s->active == 0) {
844 log_error("Zarro sources specified");
848 if (!!n + !!arg_url + !!arg_listen_raw + !!arg_files)
849 output_name = "multiple";
851 r = writer_init(&s->writer);
855 r = open_output(&s->writer, output_name);
859 static int server_destroy(RemoteServer *s) {
864 r = writer_close(&s->writer);
866 while ((d = hashmap_steal_first(s->daemons))) {
867 MHD_stop_daemon(d->daemon);
868 sd_event_source_unref(d->event);
872 hashmap_free(s->daemons);
874 assert(s->sources_size == 0 || s->sources);
875 for (i = 0; i < s->sources_size; i++)
880 sd_event_source_unref(s->sigterm_event);
881 sd_event_source_unref(s->sigint_event);
882 sd_event_source_unref(s->listen_event);
883 sd_event_unref(s->events);
885 /* fds that we're listening on remain open... */
890 /**********************************************************************
891 **********************************************************************
892 **********************************************************************/
894 static int dispatch_raw_source_event(sd_event_source *event,
899 RemoteServer *s = userdata;
900 RemoteSource *source;
903 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
904 source = s->sources[fd];
905 assert(source->fd == fd);
907 r = process_source(source, &s->writer, arg_compress, arg_seal);
908 if (source->state == STATE_EOF) {
909 log_info("EOF reached with source fd:%d (%s)",
910 source->fd, source->name);
911 if (source_non_empty(source))
912 log_warning("EOF reached with incomplete data");
913 remove_source(s, source->fd);
914 log_info("%zd active source remaining", s->active);
915 } else if (r == -E2BIG) {
916 log_error("Entry too big, skipped");
923 static int accept_connection(const char* type, int fd, SocketAddress *addr) {
926 log_debug("Accepting new %s connection on fd:%d", type, fd);
927 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
929 log_error("accept() on fd:%d failed: %m", fd);
933 switch(socket_address_family(addr)) {
936 char* _cleanup_free_ a = NULL;
938 r = socket_address_print(addr, &a);
940 log_error("socket_address_print(): %s", strerror(-r));
945 log_info("Accepted %s %s connection from %s",
947 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
953 log_error("Rejected %s connection with unsupported family %d",
954 type, socket_address_family(addr));
961 static int dispatch_raw_connection_event(sd_event_source *event,
965 RemoteServer *s = userdata;
967 SocketAddress addr = {
968 .size = sizeof(union sockaddr_union),
972 fd2 = accept_connection("raw", fd, &addr);
976 return add_source(s, fd2, NULL);
979 /**********************************************************************
980 **********************************************************************
981 **********************************************************************/
983 static int parse_config(void) {
984 const ConfigTableItem items[] = {
985 { "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
986 { "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
987 { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
991 r = config_parse(NULL, PKGSYSCONFDIR "/journal-remote.conf", NULL,
993 config_item_table_lookup, items,
996 log_error("Failed to parse configuration file: %s", strerror(-r));
1001 static void help(void) {
1002 printf("%s [OPTIONS...] {FILE|-}...\n\n"
1003 "Write external journal events to a journal file.\n\n"
1005 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
1006 " --getter=COMMAND Read events from the output of COMMAND\n"
1007 " --listen-raw=ADDR Listen for connections at ADDR\n"
1008 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
1009 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
1010 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
1011 " --[no-]compress Use XZ-compression in the output journal (default: yes)\n"
1012 " --[no-]seal Use Event sealing in the output journal (default: no)\n"
1013 " --key=FILENAME Specify key in PEM format (default:\n"
1014 " \"" KEY_FILE "\")\n"
1015 " --cert=FILENAME Specify certificate in PEM format (default:\n"
1016 " \"" CERT_FILE "\")\n"
1017 " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
1018 " \"" TRUST_FILE "\")\n"
1019 " --gnutls-log=CATEGORY...\n"
1020 " Specify a list of gnutls logging categories\n"
1021 " -h --help Show this help and exit\n"
1022 " --version Print version string and exit\n"
1024 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
1025 , program_invocation_short_name);
1028 static int parse_argv(int argc, char *argv[]) {
1030 ARG_VERSION = 0x100,
1046 static const struct option options[] = {
1047 { "help", no_argument, NULL, 'h' },
1048 { "version", no_argument, NULL, ARG_VERSION },
1049 { "url", required_argument, NULL, ARG_URL },
1050 { "getter", required_argument, NULL, ARG_GETTER },
1051 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
1052 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1053 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1054 { "output", required_argument, NULL, 'o' },
1055 { "compress", no_argument, NULL, ARG_COMPRESS },
1056 { "no-compress", no_argument, NULL, ARG_NO_COMPRESS },
1057 { "seal", no_argument, NULL, ARG_SEAL },
1058 { "no-seal", no_argument, NULL, ARG_NO_SEAL },
1059 { "key", required_argument, NULL, ARG_KEY },
1060 { "cert", required_argument, NULL, ARG_CERT },
1061 { "trust", required_argument, NULL, ARG_TRUST },
1062 { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG },
1071 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1075 return 0 /* done */;
1078 puts(PACKAGE_STRING);
1079 puts(SYSTEMD_FEATURES);
1080 return 0 /* done */;
1084 log_error("cannot currently set more than one --url");
1093 log_error("cannot currently use --getter more than once");
1097 arg_getter = optarg;
1100 case ARG_LISTEN_RAW:
1101 if (arg_listen_raw) {
1102 log_error("cannot currently use --listen-raw more than once");
1106 arg_listen_raw = optarg;
1109 case ARG_LISTEN_HTTP:
1110 if (arg_listen_http || http_socket >= 0) {
1111 log_error("cannot currently use --listen-http more than once");
1119 arg_listen_http = optarg;
1122 case ARG_LISTEN_HTTPS:
1123 if (arg_listen_https || https_socket >= 0) {
1124 log_error("cannot currently use --listen-https more than once");
1132 arg_listen_https = optarg;
1138 log_error("Key file specified twice");
1142 arg_key = strdup(optarg);
1150 log_error("Certificate file specified twice");
1154 arg_cert = strdup(optarg);
1161 if (arg_trust || arg_trust_all) {
1162 log_error("Confusing trusted CA configuration");
1166 if (streq(optarg, "all"))
1167 arg_trust_all = true;
1170 arg_trust = strdup(optarg);
1174 log_error("Option --trust is not available.");
1183 log_error("cannot use --output/-o more than once");
1187 arg_output = optarg;
1191 arg_compress = true;
1193 case ARG_NO_COMPRESS:
1194 arg_compress = false;
1203 case ARG_GNUTLS_LOG: {
1208 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
1211 cat = strndup(word, size);
1215 if (strv_consume(&arg_gnutls_log, cat) < 0)
1220 log_error("Option --gnutls-log is not available.");
1229 log_error("Unknown option code %c", c);
1234 arg_files = argv + optind;
1236 return 1 /* work to do */;
1239 static int load_certificates(char **key, char **cert, char **trust) {
1242 r = read_full_file(arg_key ?: KEY_FILE, key, NULL);
1244 log_error("Failed to read key from file '%s': %s",
1245 arg_key ?: KEY_FILE, strerror(-r));
1249 r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
1251 log_error("Failed to read certificate from file '%s': %s",
1252 arg_cert ?: CERT_FILE, strerror(-r));
1257 log_info("Certificate checking disabled.");
1259 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
1261 log_error("Failed to read CA certificate file '%s': %s",
1262 arg_trust ?: TRUST_FILE, strerror(-r));
1270 static int setup_gnutls_logger(char **categories) {
1271 if (!arg_listen_http && !arg_listen_https)
1279 gnutls_global_set_log_function(log_func_gnutls);
1282 STRV_FOREACH(cat, categories) {
1283 r = log_enable_gnutls_category(*cat);
1288 log_reset_gnutls_level();
1295 int main(int argc, char **argv) {
1296 RemoteServer s = {};
1298 _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
1300 log_show_color(true);
1301 log_parse_environment();
1305 return EXIT_FAILURE;
1307 r = parse_argv(argc, argv);
1309 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1311 r = setup_gnutls_logger(arg_gnutls_log);
1313 return EXIT_FAILURE;
1315 if (load_certificates(&key, &cert, &trust) < 0)
1316 return EXIT_FAILURE;
1318 if (remoteserver_init(&s, key, cert, trust) < 0)
1319 return EXIT_FAILURE;
1321 sd_event_set_watchdog(s.events, true);
1323 log_debug("%s running as pid "PID_FMT,
1324 program_invocation_short_name, getpid());
1327 "STATUS=Processing requests...");
1330 r = sd_event_get_state(s.events);
1333 if (r == SD_EVENT_FINISHED)
1336 r = sd_event_run(s.events, -1);
1338 log_error("Failed to run event loop: %s", strerror(-r));
1343 log_info("Finishing after writing %" PRIu64 " entries", s.writer.seqnum);
1344 r2 = server_destroy(&s);
1346 sd_notify(false, "STATUS=Shutting down...");
1352 return r >= 0 && r2 >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;