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;
229 ssize_t sources_size;
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_T(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;
280 assert(fd < s->sources_size);
282 source = s->sources[fd];
285 s->sources[fd] = NULL;
294 static int add_source(RemoteServer *s, int fd, const char* name) {
295 RemoteSource *source = NULL;
303 realname = strdup(name);
307 r = asprintf(&realname, "fd:%d", fd);
312 log_debug("Creating source for fd:%d (%s)", fd, name);
314 r = get_source_for_fd(s, fd, &source);
316 log_error("Failed to create source for fd:%d (%s)", fd, name);
320 assert(source->fd < 0);
323 r = sd_event_add_io(s->events, &source->event,
324 fd, EPOLLIN, dispatch_raw_source_event, s);
326 log_error("Failed to register event source for fd:%d: %s",
331 return 1; /* work to do */
334 remove_source(s, fd);
338 static int add_raw_socket(RemoteServer *s, int fd) {
341 r = sd_event_add_io(s->events, &s->listen_event, fd, EPOLLIN,
342 dispatch_raw_connection_event, s);
352 static int setup_raw_socket(RemoteServer *s, const char *address) {
355 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
359 return add_raw_socket(s, fd);
362 /**********************************************************************
363 **********************************************************************
364 **********************************************************************/
366 static RemoteSource *request_meta(void **connection_cls) {
367 RemoteSource *source;
369 assert(connection_cls);
371 return *connection_cls;
373 source = new0(RemoteSource, 1);
378 log_debug("Added RemoteSource as connection metadata %p", source);
380 *connection_cls = source;
384 static void request_meta_free(void *cls,
385 struct MHD_Connection *connection,
386 void **connection_cls,
387 enum MHD_RequestTerminationCode toe) {
390 assert(connection_cls);
393 log_debug("Cleaning up connection metadata %p", s);
395 *connection_cls = NULL;
398 static int process_http_upload(
399 struct MHD_Connection *connection,
400 const char *upload_data,
401 size_t *upload_data_size,
402 RemoteSource *source) {
404 bool finished = false;
409 log_debug("request_handler_upload: connection %p, %zu bytes",
410 connection, *upload_data_size);
412 if (*upload_data_size) {
413 log_info("Received %zu bytes", *upload_data_size);
415 r = push_data(source, upload_data, *upload_data_size);
417 log_error("Failed to store received data of size %zu: %s",
418 *upload_data_size, strerror(-r));
419 return mhd_respond_oom(connection);
421 *upload_data_size = 0;
426 r = process_source(source, &server->writer, arg_compress, arg_seal);
428 log_warning("Entry too big, skipped");
429 else if (r == -EAGAIN || r == -EWOULDBLOCK)
432 log_warning("Failed to process data for connection %p", connection);
433 return mhd_respondf(connection, MHD_HTTP_UNPROCESSABLE_ENTITY,
434 "Processing failed: %s", strerror(-r));
441 /* The upload is finished */
443 if (source_non_empty(source)) {
444 log_warning("EOF reached with incomplete data");
445 return mhd_respond(connection, MHD_HTTP_EXPECTATION_FAILED,
446 "Trailing data not processed.");
449 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
452 static int request_handler(
454 struct MHD_Connection *connection,
458 const char *upload_data,
459 size_t *upload_data_size,
460 void **connection_cls) {
466 assert(connection_cls);
470 log_debug("Handling a connection %s %s %s", method, url, version);
473 return process_http_upload(connection,
474 upload_data, upload_data_size,
477 if (!streq(method, "POST"))
478 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
479 "Unsupported method.\n");
481 if (!streq(url, "/upload"))
482 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
485 header = MHD_lookup_connection_value(connection,
486 MHD_HEADER_KIND, "Content-Type");
487 if (!header || !streq(header, "application/vnd.fdo.journal"))
488 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
489 "Content-Type: application/vnd.fdo.journal"
493 r = check_permissions(connection, &code);
498 if (!request_meta(connection_cls))
499 return respond_oom(connection);
503 static int setup_microhttpd_server(RemoteServer *s, int fd, bool https) {
504 struct MHD_OptionItem opts[] = {
505 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
506 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
507 { MHD_OPTION_LISTEN_SOCKET, fd},
515 MHD_USE_PEDANTIC_CHECKS |
516 MHD_USE_EPOLL_LINUX_ONLY |
519 const union MHD_DaemonInfo *info;
525 r = fd_nonblock(fd, true);
527 log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
532 opts[opts_pos++] = (struct MHD_OptionItem)
533 {MHD_OPTION_HTTPS_MEM_KEY, 0, key_pem};
534 opts[opts_pos++] = (struct MHD_OptionItem)
535 {MHD_OPTION_HTTPS_MEM_CERT, 0, cert_pem};
537 flags |= MHD_USE_SSL;
540 opts[opts_pos++] = (struct MHD_OptionItem)
541 {MHD_OPTION_HTTPS_MEM_TRUST, 0, trust_pem};
544 d = new(MHDDaemonWrapper, 1);
548 d->fd = (uint64_t) fd;
550 d->daemon = MHD_start_daemon(flags, 0,
552 request_handler, NULL,
553 MHD_OPTION_ARRAY, opts,
556 log_error("Failed to start µhttp daemon");
561 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
562 https ? "HTTPS" : "HTTP", fd, d);
565 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
567 log_error("µhttp returned NULL daemon info");
572 epoll_fd = info->listen_fd;
574 log_error("µhttp epoll fd is invalid");
579 r = sd_event_add_io(s->events, &d->event,
580 epoll_fd, EPOLLIN, dispatch_http_event, d);
582 log_error("Failed to add event callback: %s", strerror(-r));
586 r = hashmap_ensure_allocated(&s->daemons, uint64_hash_func, uint64_compare_func);
592 r = hashmap_put(s->daemons, &d->fd, d);
594 log_error("Failed to add daemon to hashmap: %s", strerror(-r));
602 MHD_stop_daemon(d->daemon);
608 static int setup_microhttpd_socket(RemoteServer *s,
613 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
617 return setup_microhttpd_server(s, fd, https);
620 static int dispatch_http_event(sd_event_source *event,
624 MHDDaemonWrapper *d = userdata;
629 log_info("%s", __func__);
631 r = MHD_run(d->daemon);
633 log_error("MHD_run failed!");
634 // XXX: unregister daemon
638 return 1; /* work to do */
641 /**********************************************************************
642 **********************************************************************
643 **********************************************************************/
645 static int dispatch_sigterm(sd_event_source *event,
646 const struct signalfd_siginfo *si,
648 RemoteServer *s = userdata;
652 log_received_signal(LOG_INFO, si);
654 sd_event_exit(s->events, 0);
658 static int setup_signals(RemoteServer *s) {
664 assert_se(sigemptyset(&mask) == 0);
665 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
666 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
668 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
672 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
679 static int fd_fd(const char *spec) {
682 r = safe_atoi(spec, &fd);
693 static int remoteserver_init(RemoteServer *s) {
695 const char *output_name = NULL;
700 sd_event_default(&s->events);
704 assert(server == NULL);
707 n = sd_listen_fds(true);
709 log_error("Failed to read listening file descriptors from environment: %s",
713 log_info("Received %d descriptors", n);
715 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
716 log_error("Received fewer sockets than expected");
720 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
721 if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
722 log_info("Received a listening socket (fd:%d)", fd);
724 if (fd == http_socket)
725 r = setup_microhttpd_server(s, fd, false);
726 else if (fd == https_socket)
727 r = setup_microhttpd_server(s, fd, true);
729 r = add_raw_socket(s, fd);
730 } else if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
731 log_info("Received a connection socket (fd:%d)", fd);
733 r = add_source(s, fd, NULL);
735 log_error("Unknown socket passed on fd:%d", fd);
741 log_error("Failed to register socket (fd:%d): %s",
746 output_name = "socket";
750 char _cleanup_free_ *url = NULL;
751 char _cleanup_strv_free_ **urlv = strv_new(arg_url, "/entries", NULL);
754 url = strv_join(urlv, "");
759 log_info("Spawning getter %s...", url);
760 fd = spawn_getter(arg_getter, url);
762 log_info("Spawning curl %s...", url);
763 fd = spawn_curl(url);
768 r = add_source(s, fd, arg_url);
772 output_name = arg_url;
775 if (arg_listen_raw) {
776 log_info("Listening on a socket...");
777 r = setup_raw_socket(s, arg_listen_raw);
781 output_name = arg_listen_raw;
784 if (arg_listen_http) {
785 r = setup_microhttpd_socket(s, arg_listen_http, false);
789 output_name = arg_listen_http;
792 if (arg_listen_https) {
793 r = setup_microhttpd_socket(s, arg_listen_https, true);
797 output_name = arg_listen_https;
800 STRV_FOREACH(file, arg_files) {
801 if (streq(*file, "-")) {
802 log_info("Reading standard input...");
805 output_name = "stdin";
807 log_info("Reading file %s...", *file);
809 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
811 log_error("Failed to open %s: %m", *file);
817 r = add_source(s, fd, output_name);
822 if (s->active == 0) {
823 log_error("Zarro sources specified");
827 if (!!n + !!arg_url + !!arg_listen_raw + !!arg_files)
828 output_name = "multiple";
830 r = writer_init(&s->writer);
834 r = open_output(&s->writer, output_name);
838 static int server_destroy(RemoteServer *s) {
843 r = writer_close(&s->writer);
845 while ((d = hashmap_steal_first(s->daemons))) {
846 MHD_stop_daemon(d->daemon);
847 sd_event_source_unref(d->event);
851 hashmap_free(s->daemons);
853 assert(s->sources_size == 0 || s->sources);
854 for (i = 0; i < s->sources_size; i++)
859 sd_event_source_unref(s->sigterm_event);
860 sd_event_source_unref(s->sigint_event);
861 sd_event_source_unref(s->listen_event);
862 sd_event_unref(s->events);
864 /* fds that we're listening on remain open... */
869 /**********************************************************************
870 **********************************************************************
871 **********************************************************************/
873 static int dispatch_raw_source_event(sd_event_source *event,
878 RemoteServer *s = userdata;
879 RemoteSource *source;
882 assert(fd < s->sources_size);
883 source = s->sources[fd];
884 assert(source->fd == fd);
886 r = process_source(source, &s->writer, arg_compress, arg_seal);
887 if (source->state == STATE_EOF) {
888 log_info("EOF reached with source fd:%d (%s)",
889 source->fd, source->name);
890 if (source_non_empty(source))
891 log_warning("EOF reached with incomplete data");
892 remove_source(s, source->fd);
893 log_info("%zd active source remaining", s->active);
894 } else if (r == -E2BIG) {
895 log_error("Entry too big, skipped");
902 static int accept_connection(const char* type, int fd, SocketAddress *addr) {
905 log_debug("Accepting new %s connection on fd:%d", type, fd);
906 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
908 log_error("accept() on fd:%d failed: %m", fd);
912 switch(socket_address_family(addr)) {
915 char* _cleanup_free_ a = NULL;
917 r = socket_address_print(addr, &a);
919 log_error("socket_address_print(): %s", strerror(-r));
924 log_info("Accepted %s %s connection from %s",
926 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
932 log_error("Rejected %s connection with unsupported family %d",
933 type, socket_address_family(addr));
940 static int dispatch_raw_connection_event(sd_event_source *event,
944 RemoteServer *s = userdata;
946 SocketAddress addr = {
947 .size = sizeof(union sockaddr_union),
951 fd2 = accept_connection("raw", fd, &addr);
955 return add_source(s, fd2, NULL);
958 /**********************************************************************
959 **********************************************************************
960 **********************************************************************/
962 static int help(void) {
963 printf("%s [OPTIONS...] {FILE|-}...\n\n"
964 "Write external journal events to a journal file.\n\n"
966 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
967 " --getter=COMMAND Read events from the output of COMMAND\n"
968 " --listen-raw=ADDR Listen for connections at ADDR\n"
969 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
970 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
971 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
972 " --[no-]compress Use XZ-compression in the output journal (default: yes)\n"
973 " --[no-]seal Use Event sealing in the output journal (default: no)\n"
974 " -h --help Show this help and exit\n"
975 " --version Print version string and exit\n"
977 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
978 , program_invocation_short_name);
983 static int parse_argv(int argc, char *argv[]) {
1000 static const struct option options[] = {
1001 { "help", no_argument, NULL, 'h' },
1002 { "version", no_argument, NULL, ARG_VERSION },
1003 { "url", required_argument, NULL, ARG_URL },
1004 { "getter", required_argument, NULL, ARG_GETTER },
1005 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
1006 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1007 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1008 { "output", required_argument, NULL, 'o' },
1009 { "compress", no_argument, NULL, ARG_COMPRESS },
1010 { "no-compress", no_argument, NULL, ARG_NO_COMPRESS },
1011 { "seal", no_argument, NULL, ARG_SEAL },
1012 { "no-seal", no_argument, NULL, ARG_NO_SEAL },
1013 { "key", required_argument, NULL, ARG_KEY },
1014 { "cert", required_argument, NULL, ARG_CERT },
1015 { "trust", required_argument, NULL, ARG_TRUST },
1024 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1028 return 0 /* done */;
1031 puts(PACKAGE_STRING);
1032 puts(SYSTEMD_FEATURES);
1033 return 0 /* done */;
1037 log_error("cannot currently set more than one --url");
1046 log_error("cannot currently use --getter more than once");
1050 arg_getter = optarg;
1053 case ARG_LISTEN_RAW:
1054 if (arg_listen_raw) {
1055 log_error("cannot currently use --listen-raw more than once");
1059 arg_listen_raw = optarg;
1062 case ARG_LISTEN_HTTP:
1063 if (arg_listen_http || http_socket >= 0) {
1064 log_error("cannot currently use --listen-http more than once");
1071 else if (r == -ENOENT)
1072 arg_listen_http = optarg;
1074 log_error("Invalid port/fd specification %s: %s",
1075 optarg, strerror(-r));
1081 case ARG_LISTEN_HTTPS:
1082 if (arg_listen_https || https_socket >= 0) {
1083 log_error("cannot currently use --listen-https more than once");
1090 else if (r == -ENOENT)
1091 arg_listen_https = optarg;
1093 log_error("Invalid port/fd specification %s: %s",
1094 optarg, strerror(-r));
1102 log_error("Key file specified twice");
1105 r = read_full_file(optarg, &key_pem, NULL);
1107 log_error("Failed to read key file: %s", strerror(-r));
1115 log_error("Certificate file specified twice");
1118 r = read_full_file(optarg, &cert_pem, NULL);
1120 log_error("Failed to read certificate file: %s", strerror(-r));
1129 log_error("CA certificate file specified twice");
1132 r = read_full_file(optarg, &trust_pem, NULL);
1134 log_error("Failed to read CA certificate file: %s", strerror(-r));
1140 log_error("Option --trust is not available.");
1145 log_error("cannot use --output/-o more than once");
1149 arg_output = optarg;
1153 arg_compress = true;
1155 case ARG_NO_COMPRESS:
1156 arg_compress = false;
1169 log_error("Unknown option code %c", c);
1173 if (arg_listen_https && !(key_pem && cert_pem)) {
1174 log_error("Options --key and --cert must be used when https sources are specified");
1179 arg_files = argv + optind;
1181 return 1 /* work to do */;
1184 static int setup_gnutls_logger(void) {
1185 if (!arg_listen_http && !arg_listen_https)
1189 gnutls_global_set_log_function(log_func_gnutls);
1190 gnutls_global_set_log_level(GNUTLS_LOG_LEVEL);
1196 int main(int argc, char **argv) {
1197 RemoteServer s = {};
1200 log_set_max_level(LOG_DEBUG);
1201 log_show_color(true);
1202 log_parse_environment();
1204 r = parse_argv(argc, argv);
1206 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1208 r = setup_gnutls_logger();
1210 return EXIT_FAILURE;
1212 if (remoteserver_init(&s) < 0)
1213 return EXIT_FAILURE;
1215 log_debug("%s running as pid %lu",
1216 program_invocation_short_name, (unsigned long) getpid());
1219 "STATUS=Processing requests...");
1222 r = sd_event_get_state(s.events);
1225 if (r == SD_EVENT_FINISHED)
1228 r = sd_event_run(s.events, -1);
1230 log_error("Failed to run event loop: %s", strerror(-r));
1235 log_info("Finishing after writing %" PRIu64 " entries", s.writer.seqnum);
1236 r2 = server_destroy(&s);
1238 sd_notify(false, "STATUS=Shutting down...");
1240 return r >= 0 && r2 >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;