journal-remote.c revision 70f1b2ddc6b94d3fa5539eb8503887b465f7fcc7
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt This file is part of systemd.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt Copyright 2012 Zbigniew Jędrzejewski-Szmek
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt systemd is free software; you can redistribute it and/or modify it
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt under the terms of the GNU Lesser General Public License as published by
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt the Free Software Foundation; either version 2.1 of the License, or
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt (at your option) any later version.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt systemd is distributed in the hope that it will be useful, but
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt WITHOUT ANY WARRANTY; without even the implied warranty of
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt Lesser General Public License for more details.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt You should have received a copy of the GNU Lesser General Public License
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt along with systemd; If not, see <http://www.gnu.org/licenses/>.
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt#define REMOTE_JOURNAL_PATH "/var/log/journal/remote"
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-remote.pem"
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-remote.pem"
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt#define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersenstatic int arg_compress = true;
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersenstatic int arg_seal = false;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic JournalWriteSplitMode arg_split_mode = JOURNAL_WRITE_SPLIT_HOST;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic bool arg_trust_all = false;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt/**********************************************************************
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt **********************************************************************
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt **********************************************************************/
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic int spawn_child(const char* child, char** argv) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt /* In the child */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt /* Make sure the child goes away when the parent dies */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt /* Check whether our parent died before we were able
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * to set the death signal */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_error("Failed to exec child %s: %m", child);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_warning("Failed to close write end of pipe: %m");
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt "--show-error",
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic int spawn_getter(const char *getter, const char *url) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (r < 0) {
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt log_error("Failed to split getter option: %s", strerror(-r));
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r < 0) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt log_error("Failed to create command line: %s", strerror(-r));
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_error("Failed to spawn getter %s: %m", getter);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktstatic int open_output(Writer *w, const char* host) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt output = arg_output ?: REMOTE_JOURNAL_PATH "/remote.journal";
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt log_error("Failed to open output journal %s: %s",
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt log_info("Opened output file %s", w->journal->path);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt/**********************************************************************
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt **********************************************************************
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt **********************************************************************/
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic int init_writer_hashmap(RemoteServer *s) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt assert(arg_split_mode >= 0 && arg_split_mode < (int) ELEMENTSOF(hash_ops));
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt s->writers = hashmap_new(hash_ops[arg_split_mode]);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int get_writer(RemoteServer *s, const char *host,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt const void *key;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt r = hashmap_put(s->writers, w->hashmap_key ?: key, w);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt/**********************************************************************
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt **********************************************************************
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt **********************************************************************/
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt/* This should go away as soon as µhttpd allows state to be passed around. */
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic int dispatch_raw_source_event(sd_event_source *event,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int dispatch_blocking_source_event(sd_event_source *event,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int dispatch_raw_connection_event(sd_event_source *event,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int dispatch_http_event(sd_event_source *event,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r < 0) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt log_warning("Failed to get writer for source %s: %s",
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt s->sources[fd] = source_new(fd, false, name, writer);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int remove_source(RemoteServer *s, int fd) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt assert(fd >= 0 && fd < (ssize_t) s->sources_size);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt /* this closes fd too */
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flyktstatic int add_source(RemoteServer *s, int fd, char* name, bool own_name) {
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt if (r < 0) {
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt log_error("Failed to create source for fd:%d (%s): %s",
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt log_debug("Falling back to sd_event_add_defer for fd:%d (%s)", fd, name);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt r = sd_event_add_defer(s->events, &source->event,
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt sd_event_source_set_enabled(source->event, SD_EVENT_ON);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt if (r < 0) {
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt log_error("Failed to register event source for fd:%d: %s",
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int add_raw_socket(RemoteServer *s, int fd) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = sd_event_add_io(s->events, &s->listen_event,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int setup_raw_socket(RemoteServer *s, const char *address) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt/**********************************************************************
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt **********************************************************************
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt **********************************************************************/
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic RemoteSource *request_meta(void **connection_cls, int fd, char *hostname) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_warning("Failed to get writer for source %s: %s",
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt source = source_new(fd, true, hostname, writer);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_debug("Added RemoteSource as connection metadata %p", source);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_debug("Cleaning up connection metadata %p", s);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt bool finished = false;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_debug("request_handler_upload: connection %p, %zu bytes",
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_debug("Received %zu bytes", *upload_data_size);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = push_data(source, upload_data, *upload_data_size);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt while (true) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = process_source(source, arg_compress, arg_seal);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt else if (r < 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_warning("Failed to process data for connection %p", connection);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "Entry is too large, maximum is %u bytes.\n",
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* The upload is finished */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_warning("Premature EOFbyte. %zu bytes lost.", remaining);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return mhd_respondf(connection, MHD_HTTP_EXPECTATION_FAILED,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "Premature EOF. %zu bytes of trailing data not processed.",
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt const char *url,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_debug("Handling a connection %s %s %s", method, url, version);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "Unsupported method.\n");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "Not found.\n");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt header = MHD_lookup_connection_value(connection,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (!header || !streq(header, "application/vnd.fdo.journal"))
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt " is required.\n");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_error("MHD_get_connection_info failed: cannot get remote fd");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "Cannot check remote address");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = check_permissions(connection, &code, &hostname);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (r < 0) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt "Cannot check remote hostname");
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt if (!request_meta(connection_cls, fd, hostname))
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flyktstatic int setup_microhttpd_server(RemoteServer *s,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt const char *key,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt const char *cert,
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt const char *trust) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r < 0) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_error("Failed to add event callback: %s", strerror(-r));
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = hashmap_ensure_allocated(&s->daemons, &uint64_hash_ops);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_error("Failed to add daemon to hashmap: %s", strerror(-r));
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktstatic int setup_microhttpd_socket(RemoteServer *s,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt const char *key,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt const char *cert,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt const char *trust) {
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return setup_microhttpd_server(s, fd, key, cert, trust);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktstatic int dispatch_http_event(sd_event_source *event,
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen // XXX: unregister daemon
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen/**********************************************************************
a276e6d68606861b552140cbcc003f4af10626fcTom Gundersen **********************************************************************
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt **********************************************************************/
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktstatic int dispatch_sigterm(sd_event_source *event,
assert(s);
assert(s);
int fd, r;
if (fd < 0)
return -EINVAL;
return fd;
const char* key,
const char* cert,
const char* trust) {
int r, n, fd;
char **file;
assert(s);
return -EINVAL;
setup_signals(s);
server = s;
r = init_writer_hashmap(s);
n = sd_listen_fds(true);
strerror(-n));
return -EBADFD;
char *hostname;
return -EINVAL;
if (arg_url) {
if (arg_getter) {
if (fd < 0)
return fd;
hostname =
if (arg_listen_raw) {
if (arg_listen_http) {
if (arg_listen_https) {
const char *output_name;
if (fd < 0) {
return -errno;
if (s->active == 0) {
return -EINVAL;
size_t i;
MHDDaemonWrapper *d;
free(d);
for (i = 0; i < s->sources_size; i++)
remove_source(s, i);
int fd,
void *userdata) {
if (remaining > 0)
} else if (r == -E2BIG) {
} else if (r == -EAGAIN) {
void *userdata) {
int fd2, r;
if (fd2 < 0) {
return -errno;
case AF_INET:
case AF_INET6: {
type,
*hostname = b;
return fd2;
return -EINVAL;
int fd,
void *userdata) {
int fd2, r;
char *hostname;
if (fd2 < 0)
return fd2;
static int parse_config(void) {
false, false, true, NULL);
static void help(void) {
help();
case ARG_VERSION:
case ARG_URL:
if (arg_url) {
return -EINVAL;
case ARG_GETTER:
if (arg_getter) {
return -EINVAL;
case ARG_LISTEN_RAW:
if (arg_listen_raw) {
return -EINVAL;
case ARG_LISTEN_HTTP:
return -EINVAL;
http_socket = r;
case ARG_LISTEN_HTTPS:
return -EINVAL;
https_socket = r;
case ARG_KEY:
if (arg_key) {
return -EINVAL;
if (!arg_key)
return log_oom();
case ARG_CERT:
if (arg_cert) {
return -EINVAL;
if (!arg_cert)
return log_oom();
case ARG_TRUST:
return -EINVAL;
arg_trust_all = true;
#ifdef HAVE_GNUTLS
if (!arg_trust)
return log_oom();
return -EINVAL;
if (arg_output) {
return -EINVAL;
case ARG_SPLIT_MODE:
return -EINVAL;
case ARG_COMPRESS:
if (optarg) {
return -EINVAL;
arg_compress = !!r;
arg_compress = true;
case ARG_SEAL:
if (optarg) {
return -EINVAL;
arg_seal = !!r;
arg_seal = true;
case ARG_GNUTLS_LOG: {
#ifdef HAVE_GNUTLS
char *cat;
if (!cat)
return log_oom();
return log_oom();
return -EINVAL;
return -EINVAL;
|| sd_listen_fds(false) > 0;
return -EINVAL;
if (type_a) {
if (!arg_output) {
return -EINVAL;
return -EINVAL;
return -EINVAL;
if (arg_trust_all)
#ifdef HAVE_GNUTLS
char **cat;
if (categories)
RemoteServer s = {};
log_show_color(true);
r = parse_config();
return EXIT_FAILURE;
return EXIT_FAILURE;
return EXIT_FAILURE;
return EXIT_FAILURE;
sd_notify(false,
while (s.active) {
if (r == SD_EVENT_FINISHED)
sd_notifyf(false,
server_destroy(&s);