journal-remote.c revision 22259a00fdb54dee818eeb1019421e2c516a330d
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen This file is part of systemd.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen Copyright 2012 Zbigniew Jędrzejewski-Szmek
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen systemd is free software; you can redistribute it and/or modify it
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen under the terms of the GNU Lesser General Public License as published by
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen (at your option) any later version.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen systemd is distributed in the hope that it will be useful, but
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen Lesser General Public License for more details.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen You should have received a copy of the GNU Lesser General Public License
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#define REMOTE_JOURNAL_PATH "/var/log/journal/remote"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-remote.pem"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-remote.pem"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int arg_compress = true;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int arg_seal = false;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int http_socket = -1, https_socket = -1;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic JournalWriteSplitMode arg_split_mode = JOURNAL_WRITE_SPLIT_HOST;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic bool arg_trust_all = false;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/**********************************************************************
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **********************************************************************
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **********************************************************************/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int spawn_child(const char* child, char** argv) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* In the child */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to dup pipe to stdout: %m");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Make sure the child goes away when the parent dies */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Check whether our parent died before we were able
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * to set the death signal */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to exec child %s: %m", child);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_warning("Failed to close write end of pipe: %m");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "--show-error",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int spawn_getter(const char *getter, const char *url) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to split getter option: %s", strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to create command line: %s", strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to spawn getter %s: %m", getter);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int open_output(Writer *w, const char* host) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen output = arg_output ?: REMOTE_JOURNAL_PATH "/remote.journal";
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = asprintf(&_output, "%s/remote-%s.journal",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to open output journal %s: %s",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_info("Opened output file %s", w->journal->path);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/**********************************************************************
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **********************************************************************
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **********************************************************************/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int init_writer_hashmap(RemoteServer *s) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen [JOURNAL_WRITE_SPLIT_HOST] = &string_hash_ops,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(arg_split_mode >= 0 && arg_split_mode < (int) ELEMENTSOF(hash_ops));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen s->writers = hashmap_new(hash_ops[arg_split_mode]);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int get_writer(RemoteServer *s, const char *host,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const void *key;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = hashmap_put(s->writers, w->hashmap_key ?: key, w);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/**********************************************************************
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **********************************************************************
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **********************************************************************/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/* This should go away as soon as µhttpd allows state to be passed around. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int dispatch_raw_source_event(sd_event_source *event,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int dispatch_raw_connection_event(sd_event_source *event,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int dispatch_http_event(sd_event_source *event,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_warning("Failed to get writer for source %s: %s",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen s->sources[fd] = source_new(fd, false, name, writer);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int remove_source(RemoteServer *s, int fd) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(fd >= 0 && fd < (ssize_t) s->sources_size);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* this closes fd too */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int add_source(RemoteServer *s, int fd, char* name, bool own_name) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to create source for fd:%d (%s): %s",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_event_add_io(s->events, &source->event,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to register event source for fd:%d: %s",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int add_raw_socket(RemoteServer *s, int fd) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_event_add_io(s->events, &s->listen_event,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int setup_raw_socket(RemoteServer *s, const char *address) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/**********************************************************************
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **********************************************************************
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **********************************************************************/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic RemoteSource *request_meta(void **connection_cls, int fd, char *hostname) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_warning("Failed to get writer for source %s: %s",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen source = source_new(fd, true, hostname, writer);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("Added RemoteSource as connection metadata %p", source);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("Cleaning up connection metadata %p", s);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("request_handler_upload: connection %p, %zu bytes",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("Received %zu bytes", *upload_data_size);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = push_data(source, upload_data, *upload_data_size);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen while (true) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = process_source(source, arg_compress, arg_seal);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen else if (r < 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_warning("Failed to process data for connection %p", connection);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "Entry is too large, maximum is %u bytes.\n",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* The upload is finished */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_warning("Premature EOFbyte. %zu bytes lost.", remaining);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return mhd_respondf(connection, MHD_HTTP_EXPECTATION_FAILED,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "Premature EOF. %zu bytes of trailing data not processed.",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *url,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("Handling a connection %s %s %s", method, url, version);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "Unsupported method.\n");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "Not found.\n");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen header = MHD_lookup_connection_value(connection,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!header || !streq(header, "application/vnd.fdo.journal"))
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " is required.\n");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("MHD_get_connection_info failed: cannot get remote fd");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "Cannot check remote address");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = check_permissions(connection, &code, &hostname);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "Cannot check remote hostname");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!request_meta(connection_cls, fd, hostname))
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int setup_microhttpd_server(RemoteServer *s,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *key,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *trust) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("µhttp returned NULL daemon info");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to add event callback: %s", strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = hashmap_ensure_allocated(&s->daemons, &uint64_hash_ops);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to add daemon to hashmap: %s", strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int setup_microhttpd_socket(RemoteServer *s,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *key,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *trust) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return setup_microhttpd_server(s, fd, key, cert, trust);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int dispatch_http_event(sd_event_source *event,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen // XXX: unregister daemon
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/**********************************************************************
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **********************************************************************
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **********************************************************************/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int dispatch_sigterm(sd_event_source *event,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char* key,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char* trust) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if ((arg_listen_raw || arg_listen_http) && trust) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Option --trust makes all non-HTTPS connections untrusted.");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to allocate event loop: %s", strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to read listening file descriptors from environment: %s",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Received fewer sockets than expected");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_info("Received a listening socket (fd:%d)", fd);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = setup_microhttpd_server(s, fd, key, cert, trust);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen } else if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to retrieve remote name: %s", strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_info("Received a connection socket (fd:%d) from %s", fd, hostname);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Unknown socket passed on fd:%d", fd);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to register socket (fd:%d): %s",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = add_source(s, fd, (char*) hostname, false);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = add_source(s, fd, (char*) output_name, false);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (s->active == 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* In this case we know what the writer will be
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen called, so we can create it and verify that we can
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen create output as expected. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen while ((d = hashmap_steal_first(s->daemons))) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen for (i = 0; i < s->sources_size; i++)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* fds that we're listening on remain open... */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/**********************************************************************
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **********************************************************************
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **********************************************************************/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int dispatch_raw_source_event(sd_event_source *event,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(fd >= 0 && fd < (ssize_t) s->sources_size);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = process_source(source, arg_compress, arg_seal);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_info("EOF reached with source fd:%d (%s)",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_warning("Premature EOF. %zu bytes lost.", remaining);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_info("%zd active sources remaining", s->active);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen } else if (r == -E2BIG) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen } else if (r == -EAGAIN) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen } else if (r < 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_info("Closing connection: %s", strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int accept_connection(const char* type, int fd,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("Accepting new %s connection on fd:%d", type, fd);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("accept() on fd:%d failed: %m", fd);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("socket_address_print(): %s", strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Rejected %s connection with unsupported family %d",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int dispatch_raw_connection_event(sd_event_source *event,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fd2 = accept_connection("raw", fd, &addr, &hostname);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/**********************************************************************
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **********************************************************************
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **********************************************************************/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom GundersenDEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "Failed to parse split mode setting");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int parse_config(void) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "Remote", "SplitMode", config_parse_write_split_mode, 0, &arg_split_mode },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return config_parse(NULL, PKGSYSCONFDIR "/journal-remote.conf", NULL,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen false, false, true, NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic void help(void) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "Write external journal events to journal file(s).\n\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " -h --help Show this help\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " --version Show package version\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " --url=URL Read events from systemd-journal-gatewayd at URL\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " --getter=COMMAND Read events from the output of COMMAND\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " --listen-raw=ADDR Listen for connections at ADDR\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " --compress[=BOOL] Use XZ-compression in the output journal (default: yes)\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " --seal[=BOOL] Use Event sealing in the output journal (default: no)\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " --key=FILENAME Specify key in PEM format (default:\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " --cert=FILENAME Specify certificate in PEM format (default:\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " --gnutls-log=CATEGORY...\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen " Specify a list of gnutls logging categories\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int parse_argv(int argc, char *argv[]) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "version", no_argument, NULL, ARG_VERSION },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "getter", required_argument, NULL, ARG_GETTER },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "split-mode", required_argument, NULL, ARG_SPLIT_MODE },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "compress", optional_argument, NULL, ARG_COMPRESS },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "seal", optional_argument, NULL, ARG_SEAL },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "cert", required_argument, NULL, ARG_CERT },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "trust", required_argument, NULL, ARG_TRUST },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG },
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0 /* done */;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0 /* done */;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("cannot currently set more than one --url");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("cannot currently use --getter more than once");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("cannot currently use --listen-raw more than once");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("cannot currently use --listen-http more than once");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("cannot currently use --listen-https more than once");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Certificate file specified twice");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Confusing trusted CA configuration");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Option --trust is not available.");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("cannot use --output/-o more than once");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen arg_split_mode = journal_write_split_mode_from_string(optarg);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to parse --compress= parameter.");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to parse --seal= parameter.");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Option --gnutls-log is not available.");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen type_a = arg_getter || !strv_isempty(arg_files);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Cannot use file input or --getter with "
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "--arg-listen-... or socket activation.");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Option --output must be specified with file input or --getter.");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen && arg_output && is_dir(arg_output, true) > 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("For SplitMode=none, output must be a file.");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen && arg_output && is_dir(arg_output, true) <= 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("For SplitMode=host, output must be a directory.");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen journal_write_split_mode_to_string(arg_split_mode),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int load_certificates(char **key, char **cert, char **trust) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to read key from file '%s': %s",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to read certificate from file '%s': %s",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to read CA certificate file '%s': %s",
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int setup_gnutls_logger(char **categories) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen gnutls_global_set_log_function(log_func_gnutls);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (load_certificates(&key, &cert, &trust) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (remoteserver_init(&s, key, cert, trust) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "STATUS=Processing requests...");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to run event loop: %s", strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "STOPPING=1\n"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_info("Finishing after writing %" PRIu64 " entries", s.event_count);