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 "microhttpd-util.h"
48 #include <gnutls/gnutls.h>
51 #include "journal-remote-parse.h"
52 #include "journal-remote-write.h"
54 #define REMOTE_JOURNAL_PATH "/var/log/journal/" SD_ID128_FORMAT_STR "/remote-%s.journal"
56 static char* arg_output = NULL;
57 static char* arg_url = NULL;
58 static char* arg_getter = NULL;
59 static char* arg_listen_raw = NULL;
60 static char* arg_listen_http = NULL;
61 static char* arg_listen_https = NULL;
62 static char** arg_files = NULL;
63 static int arg_compress = true;
64 static int arg_seal = false;
65 static int http_socket = -1, https_socket = -1;
67 static char *key_pem = NULL;
68 static char *cert_pem = NULL;
69 static char *trust_pem = NULL;
71 /**********************************************************************
72 **********************************************************************
73 **********************************************************************/
75 static int spawn_child(const char* child, char** argv) {
77 pid_t parent_pid, child_pid;
81 log_error("Failed to create pager pipe: %m");
85 parent_pid = getpid();
90 log_error("Failed to fork: %m");
97 r = dup2(fd[1], STDOUT_FILENO);
99 log_error("Failed to dup pipe to stdout: %m");
105 /* Make sure the child goes away when the parent dies */
106 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
109 /* Check whether our parent died before we were able
110 * to set the death signal */
111 if (getppid() != parent_pid)
115 log_error("Failed to exec child %s: %m", child);
121 log_warning("Failed to close write end of pipe: %m");
126 static int spawn_curl(char* url) {
127 char **argv = STRV_MAKE("curl",
128 "-HAccept: application/vnd.fdo.journal",
134 r = spawn_child("curl", argv);
136 log_error("Failed to spawn curl: %m");
140 static int spawn_getter(char *getter, char *url) {
142 char _cleanup_strv_free_ **words = NULL;
145 words = strv_split_quoted(getter);
149 r = spawn_child(words[0], words);
151 log_error("Failed to spawn getter %s: %m", getter);
156 static int open_output(Writer *s, const char* url) {
157 char _cleanup_free_ *name, *output = NULL;
166 for(c = name; *c; c++) {
167 if (*c == '/' || *c == ':' || *c == ' ')
169 else if (*c == '?') {
177 r = sd_id128_get_machine(&machine);
179 log_error("failed to determine machine ID128: %s", strerror(-r));
183 r = asprintf(&output, REMOTE_JOURNAL_PATH,
184 SD_ID128_FORMAT_VAL(machine), name);
188 r = is_dir(arg_output);
190 r = asprintf(&output,
191 "%s/remote-%s.journal", arg_output, name);
195 output = strdup(arg_output);
201 r = journal_file_open_reliably(output,
202 O_RDWR|O_CREAT, 0640,
203 arg_compress, arg_seal,
208 log_error("Failed to open output journal %s: %s",
209 arg_output, strerror(-r));
211 log_info("Opened output file %s", s->journal->path);
215 /**********************************************************************
216 **********************************************************************
217 **********************************************************************/
219 typedef struct MHDDaemonWrapper {
221 struct MHD_Daemon *daemon;
223 sd_event_source *event;
226 typedef struct RemoteServer {
227 RemoteSource **sources;
232 sd_event_source *sigterm_event, *sigint_event, *listen_event;
239 /* This should go away as soon as µhttpd allows state to be passed around. */
240 static RemoteServer *server;
242 static int dispatch_raw_source_event(sd_event_source *event,
246 static int dispatch_raw_connection_event(sd_event_source *event,
250 static int dispatch_http_event(sd_event_source *event,
255 static int get_source_for_fd(RemoteServer *s, int fd, RemoteSource **source) {
259 if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
262 if (s->sources[fd] == NULL) {
263 s->sources[fd] = new0(RemoteSource, 1);
266 s->sources[fd]->fd = -1;
270 *source = s->sources[fd];
274 static int remove_source(RemoteServer *s, int fd) {
275 RemoteSource *source;
278 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
280 source = s->sources[fd];
283 s->sources[fd] = NULL;
292 static int add_source(RemoteServer *s, int fd, const char* name) {
293 RemoteSource *source = NULL;
301 realname = strdup(name);
305 r = asprintf(&realname, "fd:%d", fd);
310 log_debug("Creating source for fd:%d (%s)", fd, name);
312 r = get_source_for_fd(s, fd, &source);
314 log_error("Failed to create source for fd:%d (%s)", fd, name);
318 assert(source->fd < 0);
321 r = sd_event_add_io(s->events, &source->event,
322 fd, EPOLLIN, dispatch_raw_source_event, s);
324 log_error("Failed to register event source for fd:%d: %s",
329 return 1; /* work to do */
332 remove_source(s, fd);
336 static int add_raw_socket(RemoteServer *s, int fd) {
339 r = sd_event_add_io(s->events, &s->listen_event, fd, EPOLLIN,
340 dispatch_raw_connection_event, s);
350 static int setup_raw_socket(RemoteServer *s, const char *address) {
353 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
357 return add_raw_socket(s, fd);
360 /**********************************************************************
361 **********************************************************************
362 **********************************************************************/
364 static RemoteSource *request_meta(void **connection_cls) {
365 RemoteSource *source;
367 assert(connection_cls);
369 return *connection_cls;
371 source = new0(RemoteSource, 1);
376 log_debug("Added RemoteSource as connection metadata %p", source);
378 *connection_cls = source;
382 static void request_meta_free(void *cls,
383 struct MHD_Connection *connection,
384 void **connection_cls,
385 enum MHD_RequestTerminationCode toe) {
388 assert(connection_cls);
391 log_debug("Cleaning up connection metadata %p", s);
393 *connection_cls = NULL;
396 static int process_http_upload(
397 struct MHD_Connection *connection,
398 const char *upload_data,
399 size_t *upload_data_size,
400 RemoteSource *source) {
402 bool finished = false;
407 log_debug("request_handler_upload: connection %p, %zu bytes",
408 connection, *upload_data_size);
410 if (*upload_data_size) {
411 log_info("Received %zu bytes", *upload_data_size);
413 r = push_data(source, upload_data, *upload_data_size);
415 log_error("Failed to store received data of size %zu: %s",
416 *upload_data_size, strerror(-r));
417 return mhd_respond_oom(connection);
419 *upload_data_size = 0;
424 r = process_source(source, &server->writer, arg_compress, arg_seal);
426 log_warning("Entry too big, skipped");
427 else if (r == -EAGAIN || r == -EWOULDBLOCK)
430 log_warning("Failed to process data for connection %p", connection);
431 return mhd_respondf(connection, MHD_HTTP_UNPROCESSABLE_ENTITY,
432 "Processing failed: %s", strerror(-r));
439 /* The upload is finished */
441 if (source_non_empty(source)) {
442 log_warning("EOF reached with incomplete data");
443 return mhd_respond(connection, MHD_HTTP_EXPECTATION_FAILED,
444 "Trailing data not processed.");
447 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
450 static int request_handler(
452 struct MHD_Connection *connection,
456 const char *upload_data,
457 size_t *upload_data_size,
458 void **connection_cls) {
464 assert(connection_cls);
468 log_debug("Handling a connection %s %s %s", method, url, version);
471 return process_http_upload(connection,
472 upload_data, upload_data_size,
475 if (!streq(method, "POST"))
476 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
477 "Unsupported method.\n");
479 if (!streq(url, "/upload"))
480 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
483 header = MHD_lookup_connection_value(connection,
484 MHD_HEADER_KIND, "Content-Type");
485 if (!header || !streq(header, "application/vnd.fdo.journal"))
486 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
487 "Content-Type: application/vnd.fdo.journal"
491 r = check_permissions(connection, &code);
496 if (!request_meta(connection_cls))
497 return respond_oom(connection);
501 static int setup_microhttpd_server(RemoteServer *s, int fd, bool https) {
502 struct MHD_OptionItem opts[] = {
503 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
504 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
505 { MHD_OPTION_LISTEN_SOCKET, fd},
513 MHD_USE_PEDANTIC_CHECKS |
514 MHD_USE_EPOLL_LINUX_ONLY |
517 const union MHD_DaemonInfo *info;
523 r = fd_nonblock(fd, true);
525 log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
530 opts[opts_pos++] = (struct MHD_OptionItem)
531 {MHD_OPTION_HTTPS_MEM_KEY, 0, key_pem};
532 opts[opts_pos++] = (struct MHD_OptionItem)
533 {MHD_OPTION_HTTPS_MEM_CERT, 0, cert_pem};
535 flags |= MHD_USE_SSL;
538 opts[opts_pos++] = (struct MHD_OptionItem)
539 {MHD_OPTION_HTTPS_MEM_TRUST, 0, trust_pem};
542 d = new(MHDDaemonWrapper, 1);
546 d->fd = (uint64_t) fd;
548 d->daemon = MHD_start_daemon(flags, 0,
550 request_handler, NULL,
551 MHD_OPTION_ARRAY, opts,
554 log_error("Failed to start µhttp daemon");
559 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
560 https ? "HTTPS" : "HTTP", fd, d);
563 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
565 log_error("µhttp returned NULL daemon info");
570 epoll_fd = info->listen_fd;
572 log_error("µhttp epoll fd is invalid");
577 r = sd_event_add_io(s->events, &d->event,
578 epoll_fd, EPOLLIN, dispatch_http_event, d);
580 log_error("Failed to add event callback: %s", strerror(-r));
584 r = hashmap_ensure_allocated(&s->daemons, uint64_hash_func, uint64_compare_func);
590 r = hashmap_put(s->daemons, &d->fd, d);
592 log_error("Failed to add daemon to hashmap: %s", strerror(-r));
600 MHD_stop_daemon(d->daemon);
606 static int setup_microhttpd_socket(RemoteServer *s,
611 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
615 return setup_microhttpd_server(s, fd, https);
618 static int dispatch_http_event(sd_event_source *event,
622 MHDDaemonWrapper *d = userdata;
627 log_info("%s", __func__);
629 r = MHD_run(d->daemon);
631 log_error("MHD_run failed!");
632 // XXX: unregister daemon
636 return 1; /* work to do */
639 /**********************************************************************
640 **********************************************************************
641 **********************************************************************/
643 static int dispatch_sigterm(sd_event_source *event,
644 const struct signalfd_siginfo *si,
646 RemoteServer *s = userdata;
650 log_received_signal(LOG_INFO, si);
652 sd_event_exit(s->events, 0);
656 static int setup_signals(RemoteServer *s) {
662 assert_se(sigemptyset(&mask) == 0);
663 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
664 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
666 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
670 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
677 static int fd_fd(const char *spec) {
680 r = safe_atoi(spec, &fd);
691 static int remoteserver_init(RemoteServer *s) {
693 const char *output_name = NULL;
698 sd_event_default(&s->events);
702 assert(server == NULL);
705 n = sd_listen_fds(true);
707 log_error("Failed to read listening file descriptors from environment: %s",
711 log_info("Received %d descriptors", n);
713 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
714 log_error("Received fewer sockets than expected");
718 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
719 if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
720 log_info("Received a listening socket (fd:%d)", fd);
722 if (fd == http_socket)
723 r = setup_microhttpd_server(s, fd, false);
724 else if (fd == https_socket)
725 r = setup_microhttpd_server(s, fd, true);
727 r = add_raw_socket(s, fd);
728 } else if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
729 log_info("Received a connection socket (fd:%d)", fd);
731 r = add_source(s, fd, NULL);
733 log_error("Unknown socket passed on fd:%d", fd);
739 log_error("Failed to register socket (fd:%d): %s",
744 output_name = "socket";
748 char _cleanup_free_ *url = NULL;
749 char _cleanup_strv_free_ **urlv = strv_new(arg_url, "/entries", NULL);
752 url = strv_join(urlv, "");
757 log_info("Spawning getter %s...", url);
758 fd = spawn_getter(arg_getter, url);
760 log_info("Spawning curl %s...", url);
761 fd = spawn_curl(url);
766 r = add_source(s, fd, arg_url);
770 output_name = arg_url;
773 if (arg_listen_raw) {
774 log_info("Listening on a socket...");
775 r = setup_raw_socket(s, arg_listen_raw);
779 output_name = arg_listen_raw;
782 if (arg_listen_http) {
783 r = setup_microhttpd_socket(s, arg_listen_http, false);
787 output_name = arg_listen_http;
790 if (arg_listen_https) {
791 r = setup_microhttpd_socket(s, arg_listen_https, true);
795 output_name = arg_listen_https;
798 STRV_FOREACH(file, arg_files) {
799 if (streq(*file, "-")) {
800 log_info("Reading standard input...");
803 output_name = "stdin";
805 log_info("Reading file %s...", *file);
807 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
809 log_error("Failed to open %s: %m", *file);
815 r = add_source(s, fd, output_name);
820 if (s->active == 0) {
821 log_error("Zarro sources specified");
825 if (!!n + !!arg_url + !!arg_listen_raw + !!arg_files)
826 output_name = "multiple";
828 r = writer_init(&s->writer);
832 r = open_output(&s->writer, output_name);
836 static int server_destroy(RemoteServer *s) {
841 r = writer_close(&s->writer);
843 while ((d = hashmap_steal_first(s->daemons))) {
844 MHD_stop_daemon(d->daemon);
845 sd_event_source_unref(d->event);
849 hashmap_free(s->daemons);
851 assert(s->sources_size == 0 || s->sources);
852 for (i = 0; i < s->sources_size; i++)
857 sd_event_source_unref(s->sigterm_event);
858 sd_event_source_unref(s->sigint_event);
859 sd_event_source_unref(s->listen_event);
860 sd_event_unref(s->events);
862 /* fds that we're listening on remain open... */
867 /**********************************************************************
868 **********************************************************************
869 **********************************************************************/
871 static int dispatch_raw_source_event(sd_event_source *event,
876 RemoteServer *s = userdata;
877 RemoteSource *source;
880 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
881 source = s->sources[fd];
882 assert(source->fd == fd);
884 r = process_source(source, &s->writer, arg_compress, arg_seal);
885 if (source->state == STATE_EOF) {
886 log_info("EOF reached with source fd:%d (%s)",
887 source->fd, source->name);
888 if (source_non_empty(source))
889 log_warning("EOF reached with incomplete data");
890 remove_source(s, source->fd);
891 log_info("%zd active source remaining", s->active);
892 } else if (r == -E2BIG) {
893 log_error("Entry too big, skipped");
900 static int accept_connection(const char* type, int fd, SocketAddress *addr) {
903 log_debug("Accepting new %s connection on fd:%d", type, fd);
904 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
906 log_error("accept() on fd:%d failed: %m", fd);
910 switch(socket_address_family(addr)) {
913 char* _cleanup_free_ a = NULL;
915 r = socket_address_print(addr, &a);
917 log_error("socket_address_print(): %s", strerror(-r));
922 log_info("Accepted %s %s connection from %s",
924 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
930 log_error("Rejected %s connection with unsupported family %d",
931 type, socket_address_family(addr));
938 static int dispatch_raw_connection_event(sd_event_source *event,
942 RemoteServer *s = userdata;
944 SocketAddress addr = {
945 .size = sizeof(union sockaddr_union),
949 fd2 = accept_connection("raw", fd, &addr);
953 return add_source(s, fd2, NULL);
956 /**********************************************************************
957 **********************************************************************
958 **********************************************************************/
960 static int help(void) {
961 printf("%s [OPTIONS...] {FILE|-}...\n\n"
962 "Write external journal events to a journal file.\n\n"
964 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
965 " --getter=COMMAND Read events from the output of COMMAND\n"
966 " --listen-raw=ADDR Listen for connections at ADDR\n"
967 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
968 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
969 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
970 " --[no-]compress Use XZ-compression in the output journal (default: yes)\n"
971 " --[no-]seal Use Event sealing in the output journal (default: no)\n"
972 " -h --help Show this help and exit\n"
973 " --version Print version string and exit\n"
975 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
976 , program_invocation_short_name);
981 static int parse_argv(int argc, char *argv[]) {
998 static const struct option options[] = {
999 { "help", no_argument, NULL, 'h' },
1000 { "version", no_argument, NULL, ARG_VERSION },
1001 { "url", required_argument, NULL, ARG_URL },
1002 { "getter", required_argument, NULL, ARG_GETTER },
1003 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
1004 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1005 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1006 { "output", required_argument, NULL, 'o' },
1007 { "compress", no_argument, NULL, ARG_COMPRESS },
1008 { "no-compress", no_argument, NULL, ARG_NO_COMPRESS },
1009 { "seal", no_argument, NULL, ARG_SEAL },
1010 { "no-seal", no_argument, NULL, ARG_NO_SEAL },
1011 { "key", required_argument, NULL, ARG_KEY },
1012 { "cert", required_argument, NULL, ARG_CERT },
1013 { "trust", required_argument, NULL, ARG_TRUST },
1022 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1026 return 0 /* done */;
1029 puts(PACKAGE_STRING);
1030 puts(SYSTEMD_FEATURES);
1031 return 0 /* done */;
1035 log_error("cannot currently set more than one --url");
1044 log_error("cannot currently use --getter more than once");
1048 arg_getter = optarg;
1051 case ARG_LISTEN_RAW:
1052 if (arg_listen_raw) {
1053 log_error("cannot currently use --listen-raw more than once");
1057 arg_listen_raw = optarg;
1060 case ARG_LISTEN_HTTP:
1061 if (arg_listen_http || http_socket >= 0) {
1062 log_error("cannot currently use --listen-http more than once");
1069 else if (r == -ENOENT)
1070 arg_listen_http = optarg;
1072 log_error("Invalid port/fd specification %s: %s",
1073 optarg, strerror(-r));
1079 case ARG_LISTEN_HTTPS:
1080 if (arg_listen_https || https_socket >= 0) {
1081 log_error("cannot currently use --listen-https more than once");
1088 else if (r == -ENOENT)
1089 arg_listen_https = optarg;
1091 log_error("Invalid port/fd specification %s: %s",
1092 optarg, strerror(-r));
1100 log_error("Key file specified twice");
1103 r = read_full_file(optarg, &key_pem, NULL);
1105 log_error("Failed to read key file: %s", strerror(-r));
1113 log_error("Certificate file specified twice");
1116 r = read_full_file(optarg, &cert_pem, NULL);
1118 log_error("Failed to read certificate file: %s", strerror(-r));
1127 log_error("CA certificate file specified twice");
1130 r = read_full_file(optarg, &trust_pem, NULL);
1132 log_error("Failed to read CA certificate file: %s", strerror(-r));
1138 log_error("Option --trust is not available.");
1143 log_error("cannot use --output/-o more than once");
1147 arg_output = optarg;
1151 arg_compress = true;
1153 case ARG_NO_COMPRESS:
1154 arg_compress = false;
1167 log_error("Unknown option code %c", c);
1171 if (arg_listen_https && !(key_pem && cert_pem)) {
1172 log_error("Options --key and --cert must be used when https sources are specified");
1177 arg_files = argv + optind;
1179 return 1 /* work to do */;
1182 static int setup_gnutls_logger(void) {
1183 if (!arg_listen_http && !arg_listen_https)
1187 gnutls_global_set_log_function(log_func_gnutls);
1188 gnutls_global_set_log_level(GNUTLS_LOG_LEVEL);
1194 int main(int argc, char **argv) {
1195 RemoteServer s = {};
1198 log_set_max_level(LOG_DEBUG);
1199 log_show_color(true);
1200 log_parse_environment();
1202 r = parse_argv(argc, argv);
1204 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1206 r = setup_gnutls_logger();
1208 return EXIT_FAILURE;
1210 if (remoteserver_init(&s) < 0)
1211 return EXIT_FAILURE;
1213 log_debug("%s running as pid "PID_FMT,
1214 program_invocation_short_name, getpid());
1217 "STATUS=Processing requests...");
1220 r = sd_event_get_state(s.events);
1223 if (r == SD_EVENT_FINISHED)
1226 r = sd_event_run(s.events, -1);
1228 log_error("Failed to run event loop: %s", strerror(-r));
1233 log_info("Finishing after writing %" PRIu64 " entries", s.writer.seqnum);
1234 r2 = server_destroy(&s);
1236 sd_notify(false, "STATUS=Shutting down...");
1238 return r >= 0 && r2 >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;