journal-remote.c revision 15a5e95075a7f6007dd97b2a165c8ed16fe683df
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani This file is part of systemd.
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani Copyright 2012 Zbigniew Jędrzejewski-Szmek
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani systemd is free software; you can redistribute it and/or modify it
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani under the terms of the GNU Lesser General Public License as published by
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani the Free Software Foundation; either version 2.1 of the License, or
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani (at your option) any later version.
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani systemd is distributed in the hope that it will be useful, but
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani WITHOUT ANY WARRANTY; without even the implied warranty of
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani Lesser General Public License for more details.
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani You should have received a copy of the GNU Lesser General Public License
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani along with systemd; If not, see <http://www.gnu.org/licenses/>.
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani#define REMOTE_JOURNAL_PATH "/var/log/journal/remote"
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-remote.pem"
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-remote.pem"
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani#define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
2212d76d08f3bc34c683aed1a6736325b841625cBeniamino Galvanistatic int arg_compress = true;
2212d76d08f3bc34c683aed1a6736325b841625cBeniamino Galvanistatic int arg_seal = false;
2212d76d08f3bc34c683aed1a6736325b841625cBeniamino Galvanistatic int http_socket = -1, https_socket = -1;
2212d76d08f3bc34c683aed1a6736325b841625cBeniamino Galvanistatic JournalWriteSplitMode arg_split_mode = JOURNAL_WRITE_SPLIT_HOST;
2212d76d08f3bc34c683aed1a6736325b841625cBeniamino Galvanistatic bool arg_trust_all = false;
2212d76d08f3bc34c683aed1a6736325b841625cBeniamino Galvani/**********************************************************************
2212d76d08f3bc34c683aed1a6736325b841625cBeniamino Galvani **********************************************************************
2212d76d08f3bc34c683aed1a6736325b841625cBeniamino Galvani **********************************************************************/
2212d76d08f3bc34c683aed1a6736325b841625cBeniamino Galvanistatic int spawn_child(const char* child, char** argv) {
2212d76d08f3bc34c683aed1a6736325b841625cBeniamino Galvani return log_error_errno(errno, "Failed to create pager pipe: %m");
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani r = log_error_errno(errno, "Failed to fork: %m");
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani /* In the child */
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani log_error_errno(errno, "Failed to dup pipe to stdout: %m");
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani /* Make sure the child goes away when the parent dies */
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani /* Check whether our parent died before we were able
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani * to set the death signal */
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani log_error_errno(errno, "Failed to exec child %s: %m", child);
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani log_warning_errno(errno, "Failed to close write end of pipe: %m");
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani "--show-error",
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani log_error_errno(errno, "Failed to spawn curl: %m");
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahanistatic int spawn_getter(const char *getter, const char *url) {
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani r = strv_split_extract(&words, getter, WHITESPACE, EXTRACT_QUOTES);
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani return log_error_errno(r, "Failed to split getter option: %m");
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani return log_error_errno(r, "Failed to create command line: %m");
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani log_error_errno(errno, "Failed to spawn getter %s: %m", getter);
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahanistatic int open_output(Writer *w, const char* host) {
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani output = arg_output ?: REMOTE_JOURNAL_PATH "/remote.journal";
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani r = asprintf(&_output, "%s/remote-%s.journal",
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani log_error_errno(r, "Failed to open output journal %s: %m",
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani log_debug("Opened output file %s", w->journal->path);
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani/**********************************************************************
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani **********************************************************************
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani **********************************************************************/
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahanistatic int init_writer_hashmap(RemoteServer *s) {
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani [JOURNAL_WRITE_SPLIT_HOST] = &string_hash_ops,
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani assert(arg_split_mode >= 0 && arg_split_mode < (int) ELEMENTSOF(hash_ops));
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani s->writers = hashmap_new(hash_ops[arg_split_mode]);
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahanistatic int get_writer(RemoteServer *s, const char *host,
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani const void *key;
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST) {
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani r = hashmap_put(s->writers, w->hashmap_key ?: key, w);
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani/**********************************************************************
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani **********************************************************************
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani **********************************************************************/
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani/* This should go away as soon as µhttpd allows state to be passed around. */
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahanistatic int dispatch_raw_source_event(sd_event_source *event,
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahanistatic int dispatch_raw_source_until_block(sd_event_source *event,
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahanistatic int dispatch_blocking_source_event(sd_event_source *event,
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahanistatic int dispatch_raw_connection_event(sd_event_source *event,
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahanistatic int dispatch_http_event(sd_event_source *event,
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani /* This takes ownership of name, but only on success. */
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani return log_warning_errno(r, "Failed to get writer for source %s: %m",
ad1ad5c8e36ea795034fcdac660b15d7c141d55bSusant Sahani s->sources[fd] = source_new(fd, false, name, writer);
11e8357164694d8307dea5bcae017c98c5f125abBeniamino Galvanistatic int remove_source(RemoteServer *s, int fd) {
11e8357164694d8307dea5bcae017c98c5f125abBeniamino Galvani assert(fd >= 0 && fd < (ssize_t) s->sources_size);
11e8357164694d8307dea5bcae017c98c5f125abBeniamino Galvani /* this closes fd too */
11e8357164694d8307dea5bcae017c98c5f125abBeniamino Galvanistatic int add_source(RemoteServer *s, int fd, char* name, bool own_name) {
11e8357164694d8307dea5bcae017c98c5f125abBeniamino Galvani /* This takes ownership of name, even on failure, if own_name is true. */
11e8357164694d8307dea5bcae017c98c5f125abBeniamino Galvani r = get_source_for_fd(s, fd, name, &source);
11e8357164694d8307dea5bcae017c98c5f125abBeniamino Galvani log_error_errno(r, "Failed to create source for fd:%d (%s): %m",
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani r = sd_event_add_io(s->events, &source->event,
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani /* Add additional source for buffer processing. It will be
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani * enabled later. */
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani r = sd_event_add_defer(s->events, &source->buffer_event,
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani sd_event_source_set_enabled(source->buffer_event, SD_EVENT_OFF);
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani } else if (r == -EPERM) {
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani log_debug("Falling back to sd_event_add_defer for fd:%d (%s)", fd, name);
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani r = sd_event_add_defer(s->events, &source->event,
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani sd_event_source_set_enabled(source->event, SD_EVENT_ON);
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani log_error_errno(r, "Failed to register event source for fd:%d: %m",
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani r = sd_event_source_set_description(source->event, name);
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani log_error_errno(r, "Failed to set source name for fd:%d: %m", fd);
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvanistatic int add_raw_socket(RemoteServer *s, int fd) {
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani char name[sizeof("raw-socket-")-1 + DECIMAL_STR_MAX(int) + 1];
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani r = sd_event_add_io(s->events, &s->listen_event,
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani r = sd_event_source_set_description(s->listen_event, name);
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvanistatic int setup_raw_socket(RemoteServer *s, const char *address) {
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani/**********************************************************************
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani **********************************************************************
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani **********************************************************************/
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvanistatic int request_meta(void **connection_cls, int fd, char *hostname) {
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani r = get_writer(server, hostname, &writer);
b57003ecc3fe92c8074cbc72ee2b7d9cda742271Beniamino Galvani return log_warning_errno(r, "Failed to get writer for source %s: %m",
11e8357164694d8307dea5bcae017c98c5f125abBeniamino Galvani source = source_new(fd, true, hostname, writer);
11e8357164694d8307dea5bcae017c98c5f125abBeniamino Galvani log_debug("Added RemoteSource as connection metadata %p", source);
11e8357164694d8307dea5bcae017c98c5f125abBeniamino Galvani log_debug("Cleaning up connection metadata %p", s);
if (*upload_data_size) {
*upload_data_size = 0;
finished = true;
if (r == -EAGAIN)
if (r == -E2BIG)
if (!finished)
return MHD_YES;
if (remaining > 0) {
static int request_handler(
void *cls,
const char *url,
const char *method,
const char *version,
const char *upload_data,
void **connection_cls) {
const char *header;
if (*connection_cls)
"Content-Type: application/vnd.fdo.journal"
if (!ci) {
return code;
if (r == -ENOMEM)
strerror(-r));
return MHD_YES;
int fd,
const char *key,
const char *cert,
const char *trust) {
{ MHD_OPTION_END},
{ MHD_OPTION_END},
{ MHD_OPTION_END},
{ MHD_OPTION_END}};
int flags =
int r, epoll_fd;
MHDDaemonWrapper *d;
if (key) {
if (trust)
return log_oom();
if (!d->daemon) {
r = -EINVAL;
goto error;
if (!info) {
r = -EOPNOTSUPP;
goto error;
if (epoll_fd < 0) {
r = -EUCLEAN;
goto error;
dispatch_http_event, d);
goto error;
goto error;
log_oom();
goto error;
goto error;
s->active ++;
free(d);
const char *address,
const char *key,
const char *cert,
const char *trust) {
int fd;
if (fd < 0)
return fd;
int fd,
void *userdata) {
assert(d);
if (r == MHD_NO) {
return -EINVAL;
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);
return -EBADFD;
char *hostname;
return -EINVAL;
fd);
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)
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,
RemoteServer *s) {
if (remaining > 0)
} else if (r == -E2BIG) {
} else if (r == -EAGAIN) {
void *userdata) {
int fd,
void *userdata) {
void *userdata) {
int fd2, r;
if (fd2 < 0)
case AF_INET:
case AF_INET6: {
type,
*hostname = b;
return fd2;
return -EINVAL;
int fd,
void *userdata) {
int fd2;
if (fd2 < 0)
return fd2;
static int parse_config(void) {
false, NULL);
static void help(void) {
help();
case ARG_VERSION:
return 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)
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);