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 "socket-util.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 static char* arg_output = NULL;
58 static char* arg_url = NULL;
59 static char* arg_getter = NULL;
60 static char* arg_listen_raw = NULL;
61 static char* arg_listen_http = NULL;
62 static char* arg_listen_https = NULL;
63 static char** arg_files = NULL;
64 static int arg_compress = true;
65 static int arg_seal = false;
66 static int http_socket = -1, https_socket = -1;
68 static char *key_pem = NULL;
69 static char *cert_pem = NULL;
70 static char *trust_pem = NULL;
72 /**********************************************************************
73 **********************************************************************
74 **********************************************************************/
76 static int spawn_child(const char* child, char** argv) {
78 pid_t parent_pid, child_pid;
82 log_error("Failed to create pager pipe: %m");
86 parent_pid = getpid();
91 log_error("Failed to fork: %m");
98 r = dup2(fd[1], STDOUT_FILENO);
100 log_error("Failed to dup pipe to stdout: %m");
106 /* Make sure the child goes away when the parent dies */
107 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
110 /* Check whether our parent died before we were able
111 * to set the death signal */
112 if (getppid() != parent_pid)
116 log_error("Failed to exec child %s: %m", child);
122 log_warning("Failed to close write end of pipe: %m");
127 static int spawn_curl(char* url) {
128 char **argv = STRV_MAKE("curl",
129 "-HAccept: application/vnd.fdo.journal",
135 r = spawn_child("curl", argv);
137 log_error("Failed to spawn curl: %m");
141 static int spawn_getter(char *getter, char *url) {
143 char _cleanup_strv_free_ **words = NULL;
146 words = strv_split_quoted(getter);
150 r = spawn_child(words[0], words);
152 log_error("Failed to spawn getter %s: %m", getter);
157 static int open_output(Writer *s, const char* url) {
158 char _cleanup_free_ *name, *output = NULL;
167 for(c = name; *c; c++) {
168 if (*c == '/' || *c == ':' || *c == ' ')
170 else if (*c == '?') {
178 r = sd_id128_get_machine(&machine);
180 log_error("failed to determine machine ID128: %s", strerror(-r));
184 r = asprintf(&output, REMOTE_JOURNAL_PATH,
185 SD_ID128_FORMAT_VAL(machine), name);
189 r = is_dir(arg_output);
191 r = asprintf(&output,
192 "%s/remote-%s.journal", arg_output, name);
196 output = strdup(arg_output);
202 r = journal_file_open_reliably(output,
203 O_RDWR|O_CREAT, 0640,
204 arg_compress, arg_seal,
209 log_error("Failed to open output journal %s: %s",
210 arg_output, strerror(-r));
212 log_info("Opened output file %s", s->journal->path);
216 /**********************************************************************
217 **********************************************************************
218 **********************************************************************/
220 typedef struct MHDDaemonWrapper {
222 struct MHD_Daemon *daemon;
224 sd_event_source *event;
227 typedef struct RemoteServer {
228 RemoteSource **sources;
233 sd_event_source *sigterm_event, *sigint_event, *listen_event;
240 /* This should go away as soon as µhttpd allows state to be passed around. */
241 static RemoteServer *server;
243 static int dispatch_raw_source_event(sd_event_source *event,
247 static int dispatch_raw_connection_event(sd_event_source *event,
251 static int dispatch_http_event(sd_event_source *event,
256 static int get_source_for_fd(RemoteServer *s, int fd, RemoteSource **source) {
260 if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
263 if (s->sources[fd] == NULL) {
264 s->sources[fd] = new0(RemoteSource, 1);
267 s->sources[fd]->fd = -1;
271 *source = s->sources[fd];
275 static int remove_source(RemoteServer *s, int fd) {
276 RemoteSource *source;
279 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
281 source = s->sources[fd];
284 s->sources[fd] = NULL;
293 static int add_source(RemoteServer *s, int fd, const char* name) {
294 RemoteSource *source = NULL;
302 realname = strdup(name);
306 r = asprintf(&realname, "fd:%d", fd);
311 log_debug("Creating source for fd:%d (%s)", fd, name);
313 r = get_source_for_fd(s, fd, &source);
315 log_error("Failed to create source for fd:%d (%s)", fd, name);
319 assert(source->fd < 0);
322 r = sd_event_add_io(s->events, &source->event,
323 fd, EPOLLIN, dispatch_raw_source_event, s);
325 log_error("Failed to register event source for fd:%d: %s",
330 return 1; /* work to do */
333 remove_source(s, fd);
337 static int add_raw_socket(RemoteServer *s, int fd) {
340 r = sd_event_add_io(s->events, &s->listen_event, fd, EPOLLIN,
341 dispatch_raw_connection_event, s);
351 static int setup_raw_socket(RemoteServer *s, const char *address) {
354 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
358 return add_raw_socket(s, fd);
361 /**********************************************************************
362 **********************************************************************
363 **********************************************************************/
365 static RemoteSource *request_meta(void **connection_cls) {
366 RemoteSource *source;
368 assert(connection_cls);
370 return *connection_cls;
372 source = new0(RemoteSource, 1);
377 log_debug("Added RemoteSource as connection metadata %p", source);
379 *connection_cls = source;
383 static void request_meta_free(void *cls,
384 struct MHD_Connection *connection,
385 void **connection_cls,
386 enum MHD_RequestTerminationCode toe) {
389 assert(connection_cls);
392 log_debug("Cleaning up connection metadata %p", s);
394 *connection_cls = NULL;
397 static int process_http_upload(
398 struct MHD_Connection *connection,
399 const char *upload_data,
400 size_t *upload_data_size,
401 RemoteSource *source) {
403 bool finished = false;
408 log_debug("request_handler_upload: connection %p, %zu bytes",
409 connection, *upload_data_size);
411 if (*upload_data_size) {
412 log_info("Received %zu bytes", *upload_data_size);
414 r = push_data(source, upload_data, *upload_data_size);
416 log_error("Failed to store received data of size %zu: %s",
417 *upload_data_size, strerror(-r));
418 return mhd_respond_oom(connection);
420 *upload_data_size = 0;
425 r = process_source(source, &server->writer, arg_compress, arg_seal);
427 log_warning("Entry too big, skipped");
428 else if (r == -EAGAIN || r == -EWOULDBLOCK)
431 log_warning("Failed to process data for connection %p", connection);
432 return mhd_respondf(connection, MHD_HTTP_UNPROCESSABLE_ENTITY,
433 "Processing failed: %s", strerror(-r));
440 /* The upload is finished */
442 if (source_non_empty(source)) {
443 log_warning("EOF reached with incomplete data");
444 return mhd_respond(connection, MHD_HTTP_EXPECTATION_FAILED,
445 "Trailing data not processed.");
448 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
451 static int request_handler(
453 struct MHD_Connection *connection,
457 const char *upload_data,
458 size_t *upload_data_size,
459 void **connection_cls) {
465 assert(connection_cls);
469 log_debug("Handling a connection %s %s %s", method, url, version);
472 return process_http_upload(connection,
473 upload_data, upload_data_size,
476 if (!streq(method, "POST"))
477 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
478 "Unsupported method.\n");
480 if (!streq(url, "/upload"))
481 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
484 header = MHD_lookup_connection_value(connection,
485 MHD_HEADER_KIND, "Content-Type");
486 if (!header || !streq(header, "application/vnd.fdo.journal"))
487 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
488 "Content-Type: application/vnd.fdo.journal"
492 r = check_permissions(connection, &code);
497 if (!request_meta(connection_cls))
498 return respond_oom(connection);
502 static int setup_microhttpd_server(RemoteServer *s, int fd, bool https) {
503 struct MHD_OptionItem opts[] = {
504 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
505 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
506 { MHD_OPTION_LISTEN_SOCKET, fd},
514 MHD_USE_PEDANTIC_CHECKS |
515 MHD_USE_EPOLL_LINUX_ONLY |
518 const union MHD_DaemonInfo *info;
524 r = fd_nonblock(fd, true);
526 log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
531 opts[opts_pos++] = (struct MHD_OptionItem)
532 {MHD_OPTION_HTTPS_MEM_KEY, 0, key_pem};
533 opts[opts_pos++] = (struct MHD_OptionItem)
534 {MHD_OPTION_HTTPS_MEM_CERT, 0, cert_pem};
536 flags |= MHD_USE_SSL;
539 opts[opts_pos++] = (struct MHD_OptionItem)
540 {MHD_OPTION_HTTPS_MEM_TRUST, 0, trust_pem};
543 d = new(MHDDaemonWrapper, 1);
547 d->fd = (uint64_t) fd;
549 d->daemon = MHD_start_daemon(flags, 0,
551 request_handler, NULL,
552 MHD_OPTION_ARRAY, opts,
555 log_error("Failed to start µhttp daemon");
560 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
561 https ? "HTTPS" : "HTTP", fd, d);
564 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
566 log_error("µhttp returned NULL daemon info");
571 epoll_fd = info->listen_fd;
573 log_error("µhttp epoll fd is invalid");
578 r = sd_event_add_io(s->events, &d->event,
579 epoll_fd, EPOLLIN, dispatch_http_event, d);
581 log_error("Failed to add event callback: %s", strerror(-r));
585 r = hashmap_ensure_allocated(&s->daemons, uint64_hash_func, uint64_compare_func);
591 r = hashmap_put(s->daemons, &d->fd, d);
593 log_error("Failed to add daemon to hashmap: %s", strerror(-r));
601 MHD_stop_daemon(d->daemon);
607 static int setup_microhttpd_socket(RemoteServer *s,
612 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
616 return setup_microhttpd_server(s, fd, https);
619 static int dispatch_http_event(sd_event_source *event,
623 MHDDaemonWrapper *d = userdata;
628 log_info("%s", __func__);
630 r = MHD_run(d->daemon);
632 log_error("MHD_run failed!");
633 // XXX: unregister daemon
637 return 1; /* work to do */
640 /**********************************************************************
641 **********************************************************************
642 **********************************************************************/
644 static int dispatch_sigterm(sd_event_source *event,
645 const struct signalfd_siginfo *si,
647 RemoteServer *s = userdata;
651 log_received_signal(LOG_INFO, si);
653 sd_event_exit(s->events, 0);
657 static int setup_signals(RemoteServer *s) {
663 assert_se(sigemptyset(&mask) == 0);
664 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
665 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
667 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
671 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
678 static int fd_fd(const char *spec) {
681 r = safe_atoi(spec, &fd);
692 static int remoteserver_init(RemoteServer *s) {
694 const char *output_name = NULL;
699 sd_event_default(&s->events);
703 assert(server == NULL);
706 n = sd_listen_fds(true);
708 log_error("Failed to read listening file descriptors from environment: %s",
712 log_info("Received %d descriptors", n);
714 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
715 log_error("Received fewer sockets than expected");
719 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
720 if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
721 log_info("Received a listening socket (fd:%d)", fd);
723 if (fd == http_socket)
724 r = setup_microhttpd_server(s, fd, false);
725 else if (fd == https_socket)
726 r = setup_microhttpd_server(s, fd, true);
728 r = add_raw_socket(s, fd);
729 } else if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
730 log_info("Received a connection socket (fd:%d)", fd);
732 r = add_source(s, fd, NULL);
734 log_error("Unknown socket passed on fd:%d", fd);
740 log_error("Failed to register socket (fd:%d): %s",
745 output_name = "socket";
749 char _cleanup_free_ *url = NULL;
750 char _cleanup_strv_free_ **urlv = strv_new(arg_url, "/entries", NULL);
753 url = strv_join(urlv, "");
758 log_info("Spawning getter %s...", url);
759 fd = spawn_getter(arg_getter, url);
761 log_info("Spawning curl %s...", url);
762 fd = spawn_curl(url);
767 r = add_source(s, fd, arg_url);
771 output_name = arg_url;
774 if (arg_listen_raw) {
775 log_info("Listening on a socket...");
776 r = setup_raw_socket(s, arg_listen_raw);
780 output_name = arg_listen_raw;
783 if (arg_listen_http) {
784 r = setup_microhttpd_socket(s, arg_listen_http, false);
788 output_name = arg_listen_http;
791 if (arg_listen_https) {
792 r = setup_microhttpd_socket(s, arg_listen_https, true);
796 output_name = arg_listen_https;
799 STRV_FOREACH(file, arg_files) {
800 if (streq(*file, "-")) {
801 log_info("Reading standard input...");
804 output_name = "stdin";
806 log_info("Reading file %s...", *file);
808 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
810 log_error("Failed to open %s: %m", *file);
816 r = add_source(s, fd, output_name);
821 if (s->active == 0) {
822 log_error("Zarro sources specified");
826 if (!!n + !!arg_url + !!arg_listen_raw + !!arg_files)
827 output_name = "multiple";
829 r = writer_init(&s->writer);
833 r = open_output(&s->writer, output_name);
837 static int server_destroy(RemoteServer *s) {
842 r = writer_close(&s->writer);
844 while ((d = hashmap_steal_first(s->daemons))) {
845 MHD_stop_daemon(d->daemon);
846 sd_event_source_unref(d->event);
850 hashmap_free(s->daemons);
852 assert(s->sources_size == 0 || s->sources);
853 for (i = 0; i < s->sources_size; i++)
858 sd_event_source_unref(s->sigterm_event);
859 sd_event_source_unref(s->sigint_event);
860 sd_event_source_unref(s->listen_event);
861 sd_event_unref(s->events);
863 /* fds that we're listening on remain open... */
868 /**********************************************************************
869 **********************************************************************
870 **********************************************************************/
872 static int dispatch_raw_source_event(sd_event_source *event,
877 RemoteServer *s = userdata;
878 RemoteSource *source;
881 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
882 source = s->sources[fd];
883 assert(source->fd == fd);
885 r = process_source(source, &s->writer, arg_compress, arg_seal);
886 if (source->state == STATE_EOF) {
887 log_info("EOF reached with source fd:%d (%s)",
888 source->fd, source->name);
889 if (source_non_empty(source))
890 log_warning("EOF reached with incomplete data");
891 remove_source(s, source->fd);
892 log_info("%zd active source remaining", s->active);
893 } else if (r == -E2BIG) {
894 log_error("Entry too big, skipped");
901 static int accept_connection(const char* type, int fd, SocketAddress *addr) {
904 log_debug("Accepting new %s connection on fd:%d", type, fd);
905 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
907 log_error("accept() on fd:%d failed: %m", fd);
911 switch(socket_address_family(addr)) {
914 char* _cleanup_free_ a = NULL;
916 r = socket_address_print(addr, &a);
918 log_error("socket_address_print(): %s", strerror(-r));
923 log_info("Accepted %s %s connection from %s",
925 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
931 log_error("Rejected %s connection with unsupported family %d",
932 type, socket_address_family(addr));
939 static int dispatch_raw_connection_event(sd_event_source *event,
943 RemoteServer *s = userdata;
945 SocketAddress addr = {
946 .size = sizeof(union sockaddr_union),
950 fd2 = accept_connection("raw", fd, &addr);
954 return add_source(s, fd2, NULL);
957 /**********************************************************************
958 **********************************************************************
959 **********************************************************************/
961 static int help(void) {
962 printf("%s [OPTIONS...] {FILE|-}...\n\n"
963 "Write external journal events to a journal file.\n\n"
965 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
966 " --getter=COMMAND Read events from the output of COMMAND\n"
967 " --listen-raw=ADDR Listen for connections at ADDR\n"
968 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
969 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
970 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
971 " --[no-]compress Use XZ-compression in the output journal (default: yes)\n"
972 " --[no-]seal Use Event sealing in the output journal (default: no)\n"
973 " -h --help Show this help and exit\n"
974 " --version Print version string and exit\n"
976 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
977 , program_invocation_short_name);
982 static int parse_argv(int argc, char *argv[]) {
999 static const struct option options[] = {
1000 { "help", no_argument, NULL, 'h' },
1001 { "version", no_argument, NULL, ARG_VERSION },
1002 { "url", required_argument, NULL, ARG_URL },
1003 { "getter", required_argument, NULL, ARG_GETTER },
1004 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
1005 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1006 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1007 { "output", required_argument, NULL, 'o' },
1008 { "compress", no_argument, NULL, ARG_COMPRESS },
1009 { "no-compress", no_argument, NULL, ARG_NO_COMPRESS },
1010 { "seal", no_argument, NULL, ARG_SEAL },
1011 { "no-seal", no_argument, NULL, ARG_NO_SEAL },
1012 { "key", required_argument, NULL, ARG_KEY },
1013 { "cert", required_argument, NULL, ARG_CERT },
1014 { "trust", required_argument, NULL, ARG_TRUST },
1023 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1027 return 0 /* done */;
1030 puts(PACKAGE_STRING);
1031 puts(SYSTEMD_FEATURES);
1032 return 0 /* done */;
1036 log_error("cannot currently set more than one --url");
1045 log_error("cannot currently use --getter more than once");
1049 arg_getter = optarg;
1052 case ARG_LISTEN_RAW:
1053 if (arg_listen_raw) {
1054 log_error("cannot currently use --listen-raw more than once");
1058 arg_listen_raw = optarg;
1061 case ARG_LISTEN_HTTP:
1062 if (arg_listen_http || http_socket >= 0) {
1063 log_error("cannot currently use --listen-http more than once");
1070 else if (r == -ENOENT)
1071 arg_listen_http = optarg;
1073 log_error("Invalid port/fd specification %s: %s",
1074 optarg, strerror(-r));
1080 case ARG_LISTEN_HTTPS:
1081 if (arg_listen_https || https_socket >= 0) {
1082 log_error("cannot currently use --listen-https more than once");
1089 else if (r == -ENOENT)
1090 arg_listen_https = optarg;
1092 log_error("Invalid port/fd specification %s: %s",
1093 optarg, strerror(-r));
1101 log_error("Key file specified twice");
1104 r = read_full_file(optarg, &key_pem, NULL);
1106 log_error("Failed to read key file: %s", strerror(-r));
1114 log_error("Certificate file specified twice");
1117 r = read_full_file(optarg, &cert_pem, NULL);
1119 log_error("Failed to read certificate file: %s", strerror(-r));
1128 log_error("CA certificate file specified twice");
1131 r = read_full_file(optarg, &trust_pem, NULL);
1133 log_error("Failed to read CA certificate file: %s", strerror(-r));
1139 log_error("Option --trust is not available.");
1144 log_error("cannot use --output/-o more than once");
1148 arg_output = optarg;
1152 arg_compress = true;
1154 case ARG_NO_COMPRESS:
1155 arg_compress = false;
1168 log_error("Unknown option code %c", c);
1172 if (arg_listen_https && !(key_pem && cert_pem)) {
1173 log_error("Options --key and --cert must be used when https sources are specified");
1178 arg_files = argv + optind;
1180 return 1 /* work to do */;
1183 static int setup_gnutls_logger(void) {
1184 if (!arg_listen_http && !arg_listen_https)
1188 gnutls_global_set_log_function(log_func_gnutls);
1189 gnutls_global_set_log_level(GNUTLS_LOG_LEVEL);
1195 int main(int argc, char **argv) {
1196 RemoteServer s = {};
1199 log_set_max_level(LOG_DEBUG);
1200 log_show_color(true);
1201 log_parse_environment();
1203 r = parse_argv(argc, argv);
1205 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1207 r = setup_gnutls_logger();
1209 return EXIT_FAILURE;
1211 if (remoteserver_init(&s) < 0)
1212 return EXIT_FAILURE;
1214 log_debug("%s running as pid %lu",
1215 program_invocation_short_name, (unsigned long) getpid());
1218 "STATUS=Processing requests...");
1221 r = sd_event_get_state(s.events);
1224 if (r == SD_EVENT_FINISHED)
1227 r = sd_event_run(s.events, -1);
1229 log_error("Failed to run event loop: %s", strerror(-r));
1234 log_info("Finishing after writing %" PRIu64 " entries", s.writer.seqnum);
1235 r2 = server_destroy(&s);
1237 sd_notify(false, "STATUS=Shutting down...");
1239 return r >= 0 && r2 >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;