]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/journal-remote/journal-remote.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / journal-remote / journal-remote.c
index 35a1e55f9ee2e98e68c1fee305b11d20ec078168..01a65437087198d0d8c3a8133f641707bfbcae37 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
 #include <sys/prctl.h>
 #include <sys/socket.h>
 #include <unistd.h>
-
-#ifdef HAVE_GNUTLS
-#include <gnutls/gnutls.h>
-#endif
+#include <stdint.h>
 
 #include "sd-daemon.h"
 
@@ -70,7 +68,7 @@ static int arg_seal = false;
 static int http_socket = -1, https_socket = -1;
 static char** arg_gnutls_log = NULL;
 
-static JournalWriteSplitMode arg_split_mode = JOURNAL_WRITE_SPLIT_HOST;
+static JournalWriteSplitMode arg_split_mode = _JOURNAL_WRITE_SPLIT_INVALID;
 static char* arg_output = NULL;
 
 static char *arg_key = NULL;
@@ -90,7 +88,7 @@ static int spawn_child(const char* child, char** argv) {
         if (pipe(fd) < 0)
                 return log_error_errno(errno, "Failed to create pager pipe: %m");
 
-        parent_pid = getpid();
+        parent_pid = getpid_cached();
 
         child_pid = fork();
         if (child_pid < 0) {
@@ -131,6 +129,10 @@ static int spawn_child(const char* child, char** argv) {
         if (r < 0)
                 log_warning_errno(errno, "Failed to close write end of pipe: %m");
 
+        r = fd_nonblock(fd[0], true);
+        if (r < 0)
+                log_warning_errno(errno, "Failed to set child pipe to non-blocking: %m");
+
         return fd[0];
 }
 
@@ -299,6 +301,9 @@ static int dispatch_raw_connection_event(sd_event_source *event,
                                          int fd,
                                          uint32_t revents,
                                          void *userdata);
+static int null_timer_event_handler(sd_event_source *s,
+                                uint64_t usec,
+                                void *userdata);
 static int dispatch_http_event(sd_event_source *event,
                                int fd,
                                uint32_t revents,
@@ -512,7 +517,8 @@ static int process_http_upload(
         if (*upload_data_size) {
                 log_trace("Received %zu bytes", *upload_data_size);
 
-                r = push_data(source, upload_data, *upload_data_size);
+                r = journal_importer_push_data(&source->importer,
+                                               upload_data, *upload_data_size);
                 if (r < 0)
                         return mhd_respond_oom(connection);
 
@@ -528,13 +534,12 @@ static int process_http_upload(
                         log_warning("Failed to process data for connection %p", connection);
                         if (r == -E2BIG)
                                 return mhd_respondf(connection,
-                                                    MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
-                                                    "Entry is too large, maximum is %u bytes.\n",
-                                                    DATA_SIZE_MAX);
+                                                    r, MHD_HTTP_PAYLOAD_TOO_LARGE,
+                                                    "Entry is too large, maximum is " STRINGIFY(DATA_SIZE_MAX) " bytes.");
                         else
                                 return mhd_respondf(connection,
-                                                    MHD_HTTP_UNPROCESSABLE_ENTITY,
-                                                    "Processing failed: %s.", strerror(-r));
+                                                    r, MHD_HTTP_UNPROCESSABLE_ENTITY,
+                                                    "Processing failed: %m.");
                 }
         }
 
@@ -543,15 +548,16 @@ static int process_http_upload(
 
         /* The upload is finished */
 
-        remaining = source_non_empty(source);
+        remaining = journal_importer_bytes_remaining(&source->importer);
         if (remaining > 0) {
-                log_warning("Premature EOFbyte. %zu bytes lost.", remaining);
-                return mhd_respondf(connection, MHD_HTTP_EXPECTATION_FAILED,
+                log_warning("Premature EOF byte. %zu bytes lost.", remaining);
+                return mhd_respondf(connection,
+                                    0, MHD_HTTP_EXPECTATION_FAILED,
                                     "Premature EOF. %zu bytes of trailing data not processed.",
                                     remaining);
         }
 
-        return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
+        return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.");
 };
 
 static int request_handler(
@@ -581,19 +587,16 @@ static int request_handler(
                                            *connection_cls);
 
         if (!streq(method, "POST"))
-                return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE,
-                                   "Unsupported method.\n");
+                return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, "Unsupported method.");
 
         if (!streq(url, "/upload"))
-                return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
-                                   "Not found.\n");
+                return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.");
 
         header = MHD_lookup_connection_value(connection,
                                              MHD_HEADER_KIND, "Content-Type");
         if (!header || !streq(header, "application/vnd.fdo.journal"))
                 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
-                                   "Content-Type: application/vnd.fdo.journal"
-                                   " is required.\n");
+                                   "Content-Type: application/vnd.fdo.journal is required.");
 
         {
                 const union MHD_ConnectionInfo *ci;
@@ -603,7 +606,7 @@ static int request_handler(
                 if (!ci) {
                         log_error("MHD_get_connection_info failed: cannot get remote fd");
                         return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                           "Cannot check remote address");
+                                           "Cannot check remote address.");
                 }
 
                 fd = ci->connect_fd;
@@ -618,7 +621,7 @@ static int request_handler(
                 r = getpeername_pretty(fd, false, &hostname);
                 if (r < 0)
                         return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                           "Cannot check remote hostname");
+                                           "Cannot check remote hostname.");
         }
 
         assert(hostname);
@@ -627,8 +630,7 @@ static int request_handler(
         if (r == -ENOMEM)
                 return respond_oom(connection);
         else if (r < 0)
-                return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                   strerror(-r));
+                return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "%m");
 
         hostname = NULL;
         return MHD_YES;
@@ -647,14 +649,14 @@ static int setup_microhttpd_server(RemoteServer *s,
                 { MHD_OPTION_END},
                 { MHD_OPTION_END},
                 { MHD_OPTION_END},
+                { MHD_OPTION_END},
                 { MHD_OPTION_END}};
         int opts_pos = 4;
         int flags =
                 MHD_USE_DEBUG |
                 MHD_USE_DUAL_STACK |
-                MHD_USE_EPOLL_LINUX_ONLY |
-                MHD_USE_PEDANTIC_CHECKS |
-                MHD_USE_PIPE_FOR_SHUTDOWN;
+                MHD_USE_EPOLL |
+                MHD_USE_ITC;
 
         const union MHD_DaemonInfo *info;
         int r, epoll_fd;
@@ -666,6 +668,20 @@ static int setup_microhttpd_server(RemoteServer *s,
         if (r < 0)
                 return log_error_errno(r, "Failed to make fd:%d nonblocking: %m", fd);
 
+/* MHD_OPTION_STRICT_FOR_CLIENT is introduced in microhttpd 0.9.54,
+ * and MHD_USE_PEDANTIC_CHECKS will be deprecated in future.
+ * If MHD_USE_PEDANTIC_CHECKS is '#define'd, then it is deprecated
+ * and we should use MHD_OPTION_STRICT_FOR_CLIENT. On the other hand,
+ * if MHD_USE_PEDANTIC_CHECKS is not '#define'd, then it is not
+ * deprecated yet and there exists an enum element with the same name.
+ * So we can safely use it. */
+#ifdef MHD_USE_PEDANTIC_CHECKS
+        opts[opts_pos++] = (struct MHD_OptionItem)
+                {MHD_OPTION_STRICT_FOR_CLIENT, 1};
+#else
+        flags |= MHD_USE_PEDANTIC_CHECKS;
+#endif
+
         if (key) {
                 assert(cert);
 
@@ -674,7 +690,7 @@ static int setup_microhttpd_server(RemoteServer *s,
                 opts[opts_pos++] = (struct MHD_OptionItem)
                         {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert};
 
-                flags |= MHD_USE_SSL;
+                flags |= MHD_USE_TLS;
 
                 if (trust)
                         opts[opts_pos++] = (struct MHD_OptionItem)
@@ -716,7 +732,7 @@ static int setup_microhttpd_server(RemoteServer *s,
                 goto error;
         }
 
-        r = sd_event_add_io(s->events, &d->event,
+        r = sd_event_add_io(s->events, &d->io_event,
                             epoll_fd, EPOLLIN,
                             dispatch_http_event, d);
         if (r < 0) {
@@ -724,7 +740,21 @@ static int setup_microhttpd_server(RemoteServer *s,
                 goto error;
         }
 
-        r = sd_event_source_set_description(d->event, "epoll-fd");
+        r = sd_event_source_set_description(d->io_event, "io_event");
+        if (r < 0) {
+                log_error_errno(r, "Failed to set source name: %m");
+                goto error;
+        }
+
+        r = sd_event_add_time(s->events, &d->timer_event,
+                              CLOCK_MONOTONIC, UINT64_MAX, 0,
+                              null_timer_event_handler, d);
+        if (r < 0) {
+                log_error_errno(r, "Failed to add timer_event: %m");
+                goto error;
+        }
+
+        r = sd_event_source_set_description(d->timer_event, "timer_event");
         if (r < 0) {
                 log_error_errno(r, "Failed to set source name: %m");
                 goto error;
@@ -766,12 +796,19 @@ static int setup_microhttpd_socket(RemoteServer *s,
         return setup_microhttpd_server(s, fd, key, cert, trust);
 }
 
+static int null_timer_event_handler(sd_event_source *timer_event,
+                                    uint64_t usec,
+                                    void *userdata) {
+        return dispatch_http_event(timer_event, 0, 0, userdata);
+}
+
 static int dispatch_http_event(sd_event_source *event,
                                int fd,
                                uint32_t revents,
                                void *userdata) {
         MHDDaemonWrapper *d = userdata;
         int r;
+        MHD_UNSIGNED_LONG_LONG timeout = ULONG_LONG_MAX;
 
         assert(d);
 
@@ -781,6 +818,18 @@ static int dispatch_http_event(sd_event_source *event,
                 // XXX: unregister daemon
                 return -EINVAL;
         }
+        if (MHD_get_timeout(d->daemon, &timeout) == MHD_NO)
+                timeout = ULONG_LONG_MAX;
+
+        r = sd_event_source_set_time(d->timer_event, timeout);
+        if (r < 0) {
+                log_warning_errno(r, "Unable to set event loop timeout: %m, this may result in indefinite blocking!");
+                return 1;
+        }
+
+        r = sd_event_source_set_enabled(d->timer_event, SD_EVENT_ON);
+        if (r < 0)
+                log_warning_errno(r, "Unable to enable timer_event: %m, this may result in indefinite blocking!");
 
         return 1; /* work to do */
 }
@@ -1000,7 +1049,8 @@ static void server_destroy(RemoteServer *s) {
 
         while ((d = hashmap_steal_first(s->daemons))) {
                 MHD_stop_daemon(d->daemon);
-                sd_event_source_unref(d->event);
+                sd_event_source_unref(d->io_event);
+                sd_event_source_unref(d->timer_event);
                 free(d);
         }
 
@@ -1040,19 +1090,19 @@ static int handle_raw_source(sd_event_source *event,
 
         assert(fd >= 0 && fd < (ssize_t) s->sources_size);
         source = s->sources[fd];
-        assert(source->fd == fd);
+        assert(source->importer.fd == fd);
 
         r = process_source(source, arg_compress, arg_seal);
-        if (source->state == STATE_EOF) {
+        if (journal_importer_eof(&source->importer)) {
                 size_t remaining;
 
-                log_debug("EOF reached with source fd:%d (%s)",
-                          source->fd, source->name);
+                log_debug("EOF reached with source %s (fd=%d)",
+                          source->importer.name, source->importer.fd);
 
-                remaining = source_non_empty(source);
+                remaining = journal_importer_bytes_remaining(&source->importer);
                 if (remaining > 0)
                         log_notice("Premature EOF. %zu bytes lost.", remaining);
-                remove_source(s, source->fd);
+                remove_source(s, source->importer.fd);
                 log_debug("%zu active sources remaining", s->active);
                 return 0;
         } else if (r == -E2BIG) {
@@ -1076,7 +1126,7 @@ static int dispatch_raw_source_until_block(sd_event_source *event,
         /* Make sure event stays around even if source is destroyed */
         sd_event_source_ref(event);
 
-        r = handle_raw_source(event, source->fd, EPOLLIN, server);
+        r = handle_raw_source(event, source->importer.fd, EPOLLIN, server);
         if (r != 1)
                 /* No more data for now */
                 sd_event_source_set_enabled(event, SD_EVENT_OFF);
@@ -1109,7 +1159,7 @@ static int dispatch_blocking_source_event(sd_event_source *event,
                                           void *userdata) {
         RemoteSource *source = userdata;
 
-        return handle_raw_source(event, source->fd, EPOLLIN, server);
+        return handle_raw_source(event, source->importer.fd, EPOLLIN, server);
 }
 
 static int accept_connection(const char* type, int fd,
@@ -1202,10 +1252,10 @@ static int parse_config(void) {
                 { "Remote",  "TrustedCertificateFile", config_parse_path,             0, &arg_trust      },
                 {}};
 
-        return config_parse_many(PKGSYSCONFDIR "/journal-remote.conf",
-                                 CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"),
-                                 "Remote\0", config_item_table_lookup, items,
-                                 false, NULL);
+        return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-remote.conf",
+                                        CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"),
+                                        "Remote\0", config_item_table_lookup, items,
+                                        CONFIG_PARSE_WARN, NULL);
 }
 
 static void help(void) {
@@ -1373,7 +1423,7 @@ static int parse_argv(int argc, char *argv[]) {
                         if (streq(optarg, "all"))
                                 arg_trust_all = true;
                         else {
-#ifdef HAVE_GNUTLS
+#if HAVE_GNUTLS
                                 arg_trust = strdup(optarg);
                                 if (!arg_trust)
                                         return log_oom();
@@ -1431,7 +1481,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_GNUTLS_LOG: {
-#ifdef HAVE_GNUTLS
+#if HAVE_GNUTLS
                         const char* p = optarg;
                         for (;;) {
                                 _cleanup_free_ char *word = NULL;
@@ -1481,13 +1531,26 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
                 }
 
+                if (!IN_SET(arg_split_mode, JOURNAL_WRITE_SPLIT_NONE, _JOURNAL_WRITE_SPLIT_INVALID)) {
+                        log_error("For active sources, only --split-mode=none is allowed.");
+                        return -EINVAL;
+                }
+
                 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
         }
 
-        if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE
-            && arg_output && is_dir(arg_output, true) > 0) {
-                log_error("For SplitMode=none, output must be a file.");
-                return -EINVAL;
+        if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID)
+                arg_split_mode = JOURNAL_WRITE_SPLIT_HOST;
+
+        if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE && arg_output) {
+                if (is_dir(arg_output, true) > 0) {
+                        log_error("For SplitMode=none, output must be a file.");
+                        return -EINVAL;
+                }
+                if (!endswith(arg_output, ".journal")) {
+                        log_error("For SplitMode=none, output file name must end with .journal.");
+                        return -EINVAL;
+                }
         }
 
         if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
@@ -1564,10 +1627,10 @@ int main(int argc, char **argv) {
         if (r < 0)
                 log_error_errno(r, "Failed to enable watchdog: %m");
         else
-                log_debug("Watchdog is %s.", r > 0 ? "enabled" : "disabled");
+                log_debug("Watchdog is %sd.", enable_disable(r > 0));
 
         log_debug("%s running as pid "PID_FMT,
-                  program_invocation_short_name, getpid());
+                  program_invocation_short_name, getpid_cached());
         sd_notify(false,
                   "READY=1\n"
                   "STATUS=Processing requests...");