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 "journal-remote-parse.h"
46 #include "journal-remote-write.h"
48 #define REMOTE_JOURNAL_PATH "/var/log/journal/" SD_ID128_FORMAT_STR "/remote-%s.journal"
50 static char* arg_output = NULL;
51 static char* arg_url = NULL;
52 static char* arg_getter = NULL;
53 static bool arg_stdin = false;
54 static char* arg_listen_raw = NULL;
55 static int arg_compress = true;
56 static int arg_seal = false;
58 /**********************************************************************
59 **********************************************************************
60 **********************************************************************/
62 static int spawn_child(const char* child, char** argv) {
64 pid_t parent_pid, child_pid;
68 log_error("Failed to create pager pipe: %m");
72 parent_pid = getpid();
77 log_error("Failed to fork: %m");
84 r = dup2(fd[1], STDOUT_FILENO);
86 log_error("Failed to dup pipe to stdout: %m");
92 log_warning("Failed to close pipe fds: %m");
94 /* Make sure the child goes away when the parent dies */
95 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
98 /* Check whether our parent died before we were able
99 * to set the death signal */
100 if (getppid() != parent_pid)
104 log_error("Failed to exec child %s: %m", child);
110 log_warning("Failed to close write end of pipe: %m");
115 static int spawn_curl(char* url) {
116 char **argv = STRV_MAKE("curl",
117 "-HAccept: application/vnd.fdo.journal",
123 r = spawn_child("curl", argv);
125 log_error("Failed to spawn curl: %m");
129 static int spawn_getter(char *getter, char *url) {
131 char _cleanup_strv_free_ **words = NULL, **words2 = NULL;
134 words = strv_split_quoted(getter);
138 r = spawn_child(words[0], words);
140 log_error("Failed to spawn getter %s: %m", getter);
145 static int open_output(Writer *s, const char* url) {
146 char _cleanup_free_ *name, *output = NULL;
155 for(c = name; *c; c++) {
156 if (*c == '/' || *c == ':' || *c == ' ')
158 else if (*c == '?') {
166 r = sd_id128_get_machine(&machine);
168 log_error("failed to determine machine ID128: %s", strerror(-r));
172 r = asprintf(&output, REMOTE_JOURNAL_PATH,
173 SD_ID128_FORMAT_VAL(machine), name);
177 r = is_dir(arg_output);
179 r = asprintf(&output,
180 "%s/remote-%s.journal", arg_output, name);
184 output = strdup(arg_output);
190 r = journal_file_open_reliably(output,
191 O_RDWR|O_CREAT, 0640,
192 arg_compress, arg_seal,
197 log_error("Failed to open output journal %s: %s",
198 arg_output, strerror(-r));
200 log_info("Opened output file %s", s->journal->path);
204 typedef struct RemoteServer {
205 RemoteSource **sources;
206 ssize_t sources_size;
210 sd_event_source *sigterm_event, *sigint_event, *listen_event;
215 static int dispatch_raw_source_event(sd_event_source *event,
219 static int dispatch_raw_connection_event(sd_event_source *event,
224 static int get_source_for_fd(RemoteServer *s, int fd, RemoteSource **source) {
228 if (!GREEDY_REALLOC0_T(s->sources, s->sources_size, fd + 1))
231 if (s->sources[fd] == NULL) {
232 s->sources[fd] = new0(RemoteSource, 1);
235 s->sources[fd]->fd = -1;
239 *source = s->sources[fd];
243 static int remove_source(RemoteServer *s, int fd) {
244 RemoteSource *source;
248 assert(fd < s->sources_size);
250 source = s->sources[fd];
253 s->sources[fd] = NULL;
262 static int add_source(RemoteServer *s, int fd, const char* name) {
263 RemoteSource *source = NULL;
271 realname = strdup(name);
275 r = asprintf(&realname, "fd:%d", fd);
280 log_debug("Creating source for fd:%d (%s)", fd, name);
282 r = get_source_for_fd(s, fd, &source);
284 log_error("Failed to create source for fd:%d (%s)", fd, name);
288 assert(source->fd < 0);
291 r = sd_event_add_io(s->events, &source->event,
292 fd, EPOLLIN, dispatch_raw_source_event, s);
294 log_error("Failed to register event source for fd:%d: %s",
299 return 1; /* work to do */
302 remove_source(s, fd);
306 static int setup_raw_socket(RemoteServer *s, const char *address) {
309 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
313 r = sd_event_add_io(s->events, &s->listen_event, fd, EPOLLIN,
314 dispatch_raw_connection_event, s);
324 /**********************************************************************
325 **********************************************************************
326 **********************************************************************/
328 static int dispatch_sigterm(sd_event_source *event,
329 const struct signalfd_siginfo *si,
331 RemoteServer *s = userdata;
335 log_received_signal(LOG_INFO, si);
337 sd_event_exit(s->events, 0);
341 static int setup_signals(RemoteServer *s) {
347 assert_se(sigemptyset(&mask) == 0);
348 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
349 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
351 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
355 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
362 static int remoteserver_init(RemoteServer *s) {
364 const char *output_name = NULL;
368 sd_event_default(&s->events);
372 n = sd_listen_fds(true);
374 log_error("Failed to read listening file descriptors from environment: %s",
378 log_info("Received %d descriptors", n);
380 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
381 if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
382 assert_not_reached("not implemented");
383 } else if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
384 log_info("Received a connection socket (fd:%d)", fd);
386 r = add_source(s, fd, NULL);
387 output_name = "socket";
389 log_error("Unknown socket passed on fd:%d", fd);
395 char _cleanup_free_ *url = NULL;
396 char _cleanup_strv_free_ **urlv = strv_new(arg_url, "/entries", NULL);
399 url = strv_join(urlv, "");
404 log_info("Spawning getter %s...", url);
405 fd = spawn_getter(arg_getter, url);
407 log_info("Spawning curl %s...", url);
408 fd = spawn_curl(url);
413 r = add_source(s, fd, arg_url);
417 output_name = arg_url;
420 if (arg_listen_raw) {
421 log_info("Listening on a socket...");
422 r = setup_raw_socket(s, arg_listen_raw);
426 output_name = arg_listen_raw;
430 log_info("Reading standard input...");
431 r = add_source(s, STDIN_FILENO, "stdin");
435 output_name = "stdin";
438 if (s->active == 0) {
439 log_error("Zarro sources specified");
443 if (!!n + !!arg_url + !!arg_listen_raw + !!arg_stdin > 1)
444 output_name = "multiple";
446 r = writer_init(&s->writer);
450 r = open_output(&s->writer, output_name);
454 static int server_destroy(RemoteServer *s) {
458 r = writer_close(&s->writer);
460 assert(s->sources_size == 0 || s->sources);
461 for(i = 0; i < s->sources_size; i++)
466 sd_event_source_unref(s->sigterm_event);
467 sd_event_source_unref(s->sigint_event);
468 sd_event_source_unref(s->listen_event);
469 sd_event_unref(s->events);
471 /* fds that we're listening on remain open... */
476 /**********************************************************************
477 **********************************************************************
478 **********************************************************************/
480 static int dispatch_raw_source_event(sd_event_source *event,
485 RemoteServer *s = userdata;
486 RemoteSource *source;
489 assert(fd < s->sources_size);
490 source = s->sources[fd];
491 assert(source->fd == fd);
493 r = process_source(source, &s->writer, arg_compress, arg_seal);
494 if (source->state == STATE_EOF) {
495 log_info("EOF reached with source fd:%d (%s)",
496 source->fd, source->name);
497 if (source_non_empty(source))
498 log_warning("EOF reached with incomplete data");
499 remove_source(s, source->fd);
500 log_info("%zd active source remaining", s->active);
501 } else if (r == -E2BIG) {
502 log_error("Entry too big, skipped");
509 static int dispatch_raw_connection_event(sd_event_source *event,
513 RemoteServer *s = userdata;
515 SocketAddress addr = {
516 .size = sizeof(union sockaddr_union),
521 log_debug("Accepting new connection on fd:%d", fd);
522 fd2 = accept4(fd, &addr.sockaddr.sa, &addr.size, SOCK_NONBLOCK|SOCK_CLOEXEC);
524 log_error("accept() on fd:%d failed: %m", fd);
528 switch(socket_address_family(&addr)) {
531 char* _cleanup_free_ a = NULL;
533 r = socket_address_print(&addr, &a);
535 log_error("socket_address_print(): %s", strerror(-r));
540 log_info("Accepted %s connection from %s",
541 socket_address_family(&addr) == AF_INET ? "IP" : "IPv6",
546 log_error("Connection with unsupported family %d",
547 socket_address_family(&addr));
552 r = add_source(s, fd2, NULL);
554 log_error("failed to create source from fd:%d: %s", fd2, strerror(-r));
560 /**********************************************************************
561 **********************************************************************
562 **********************************************************************/
564 static int help(void) {
565 printf("%s [OPTIONS...]\n\n"
566 "Write external journal events to a journal file.\n\n"
568 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
569 " --getter=COMMAND Read events from the output of COMMAND\n"
570 " --listen-raw=ADDR Listen for connections at ADDR\n"
571 " --stdin Read events from standard input\n"
572 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
573 " --[no-]compress Use XZ-compression in the output journal (default: yes)\n"
574 " --[no-]seal Use Event sealing in the output journal (default: no)\n"
575 " -h --help Show this help and exit\n"
576 " --version Print version string and exit\n"
578 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
579 , program_invocation_short_name);
584 static int parse_argv(int argc, char *argv[]) {
597 static const struct option options[] = {
598 { "help", no_argument, NULL, 'h' },
599 { "version", no_argument, NULL, ARG_VERSION },
600 { "url", required_argument, NULL, ARG_URL },
601 { "getter", required_argument, NULL, ARG_GETTER },
602 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
603 { "stdin", no_argument, NULL, ARG_STDIN },
604 { "output", required_argument, NULL, 'o' },
605 { "compress", no_argument, NULL, ARG_COMPRESS },
606 { "no-compress", no_argument, NULL, ARG_NO_COMPRESS },
607 { "seal", no_argument, NULL, ARG_SEAL },
608 { "no-seal", no_argument, NULL, ARG_NO_SEAL },
617 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
624 puts(PACKAGE_STRING);
625 puts(SYSTEMD_FEATURES);
630 log_error("cannot currently set more than one --url");
639 log_error("cannot currently use --getter more than once");
647 if (arg_listen_raw) {
648 log_error("cannot currently use --listen-raw more than once");
652 arg_listen_raw = optarg;
661 log_error("cannot use --output/-o more than once");
671 case ARG_NO_COMPRESS:
672 arg_compress = false;
685 log_error("Unknown option code %c", c);
690 log_error("This program takes no positional arguments");
694 return 1 /* work to do */;
697 int main(int argc, char **argv) {
701 log_set_max_level(LOG_DEBUG);
702 log_show_color(true);
703 log_parse_environment();
705 r = parse_argv(argc, argv);
707 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
709 if (remoteserver_init(&s) < 0)
712 log_debug("%s running as pid %lu",
713 program_invocation_short_name, (unsigned long) getpid());
716 "STATUS=Processing requests...");
719 r = sd_event_get_state(s.events);
722 if (r == SD_EVENT_FINISHED)
725 r = sd_event_run(s.events, -1);
727 log_error("Failed to run event loop: %s", strerror(-r));
732 log_info("Finishing after writing %" PRIu64 " entries", s.writer.seqnum);
733 r2 = server_destroy(&s);
735 sd_notify(false, "STATUS=Shutting down...");
737 return r >= 0 && r2 >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;