#include "sd-daemon.h"
#include "sd-event.h"
+#include "sd-varlink.h"
#include "ansi-color.h"
#include "fileio.h"
#include "journalctl-filter.h"
#include "journalctl-show.h"
#include "journalctl-util.h"
-#include "log.h"
+#include "journalctl-varlink.h"
#include "logs-show.h"
#include "terminal-util.h"
sd_id128_t previous_boot_id;
sd_id128_t previous_boot_id_output;
dual_timestamp previous_ts_output;
+ sd_event *event;
+ sd_varlink *synchronize_varlink;
} Context;
static void context_done(Context *c) {
assert(c);
+ c->synchronize_varlink = sd_varlink_flush_close_unref(c->synchronize_varlink);
+ c->event = sd_event_unref(c->event);
sd_journal_close(c->journal);
}
return n_shown;
}
-static int show_and_fflush(Context *c, sd_event_source *s) {
+static int show_and_fflush(Context *c) {
int r;
assert(c);
- assert(s);
r = show(c);
if (r < 0)
- return sd_event_exit(sd_event_source_get_event(s), r);
+ return sd_event_exit(c->event, r);
fflush(stdout);
return 0;
r = sd_journal_process(c->journal);
if (r < 0) {
log_error_errno(r, "Failed to process journal events: %m");
- return sd_event_exit(sd_event_source_get_event(s), r);
+ return sd_event_exit(c->event, r);
}
- return show_and_fflush(c, s);
+ return show_and_fflush(c);
}
static int on_first_event(sd_event_source *s, void *userdata) {
assert(s);
- r = show_and_fflush(c, s);
+ r = show_and_fflush(c);
if (r < 0)
return r;
return 0;
}
+static int on_synchronize_reply(
+ sd_varlink *vl,
+ sd_json_variant *parameters,
+ const char *error_id,
+ sd_varlink_reply_flags_t flags,
+ void *userdata) {
+
+ Context *c = ASSERT_PTR(userdata);
+ int r;
+
+ assert(vl);
+
+ if (error_id)
+ log_warning("Failed to synchronize on Journal, ignoring: %s", error_id);
+
+ r = show_and_fflush(c);
+ if (r < 0)
+ return r;
+
+ return sd_event_exit(c->event, EXIT_SUCCESS);
+}
+
static int on_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *vl = NULL;
+ Context *c = ASSERT_PTR(userdata);
+ int r;
+
assert(s);
assert(si);
assert(IN_SET(si->ssi_signo, SIGTERM, SIGINT));
- return sd_event_exit(sd_event_source_get_event(s), si->ssi_signo);
+ if (!arg_synchronize_on_exit)
+ goto finish;
+
+ if (c->synchronize_varlink) /* Already pending? Then exit immediately, so that user can cancel the sync */
+ return sd_event_exit(c->event, EXIT_SUCCESS);
+
+ r = varlink_connect_journal(&vl);
+ if (r < 0) {
+ log_error_errno(r, "Failed to connect to Journal Varlink IPC interface, ignoring: %m");
+ goto finish;
+ }
+
+ /* Set a low priority on the idle event handler, so that we show any log messages first */
+ r = sd_varlink_attach_event(vl, c->event, SD_EVENT_PRIORITY_IDLE);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to attach Varlink connectio to event loop: %m");
+ goto finish;
+ }
+
+ r = sd_varlink_bind_reply(vl, on_synchronize_reply);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to bind synchronization reply: %m");
+ goto finish;
+ }
+
+ (void) sd_varlink_set_userdata(vl, c);
+
+ r = sd_varlink_invoke(vl, "io.systemd.Journal.Synchronize", /* parameters= */ NULL);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to issue synchronization request: %m");
+ goto finish;
+ }
+
+ c->synchronize_varlink = TAKE_PTR(vl);
+ return 0;
+
+finish:
+ return sd_event_exit(c->event, si->ssi_signo);
}
-static int setup_event(Context *c, int fd, sd_event **ret) {
- _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+static int setup_event(Context *c, int fd) {
int r;
assert(arg_follow);
assert(c);
assert(fd >= 0);
- assert(ret);
+ assert(!c->event);
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
r = sd_event_default(&e);
if (r < 0)
return log_error_errno(r, "Failed to allocate sd_event object: %m");
- (void) sd_event_add_signal(e, NULL, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, on_signal, NULL);
- (void) sd_event_add_signal(e, NULL, SIGINT | SD_EVENT_SIGNAL_PROCMASK, on_signal, NULL);
+ (void) sd_event_add_signal(e, /* ret_event_source= */ NULL, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, on_signal, c);
+ (void) sd_event_add_signal(e, /* ret_event_source= */ NULL, SIGINT | SD_EVENT_SIGNAL_PROCMASK, on_signal, c);
r = sd_event_add_io(e, NULL, fd, EPOLLIN, &on_journal_event, c);
if (r < 0)
return log_error_errno(r, "Failed to add defer event source: %m");
}
- *ret = TAKE_PTR(e);
+ c->event = TAKE_PTR(e);
return 0;
}
}
if (arg_follow) {
- _cleanup_(sd_event_unrefp) sd_event *e = NULL;
int sig;
assert(poll_fd >= 0);
- r = setup_event(&c, poll_fd, &e);
+ r = setup_event(&c, poll_fd);
if (r < 0)
return r;
- r = sd_event_loop(e);
+ r = sd_event_loop(c.event);
if (r < 0)
return r;
sig = r;
pcre2_code *arg_compiled_pattern = NULL;
PatternCompileCase arg_case = PATTERN_COMPILE_CASE_AUTO;
ImagePolicy *arg_image_policy = NULL;
+bool arg_synchronize_on_exit = false;
STATIC_DESTRUCTOR_REGISTER(arg_cursor, freep);
STATIC_DESTRUCTOR_REGISTER(arg_cursor_file, freep);
" --no-tail Show all lines, even in follow mode\n"
" --truncate-newline Truncate entries by first newline character\n"
" -q --quiet Do not show info messages and privilege warning\n"
+ " --synchronize-on-exit=BOOL\n"
+ " Wait for Journal synchronization before exiting\n"
"\n%3$sPager Control Options:%4$s\n"
" --no-pager Do not pipe output into a pager\n"
" -e --pager-end Immediately jump to the end in the pager\n"
ARG_OUTPUT_FIELDS,
ARG_NAMESPACE,
ARG_LIST_NAMESPACES,
+ ARG_SYNCHRONIZE_ON_EXIT,
};
static const struct option options[] = {
{ "output-fields", required_argument, NULL, ARG_OUTPUT_FIELDS },
{ "namespace", required_argument, NULL, ARG_NAMESPACE },
{ "list-namespaces", no_argument, NULL, ARG_LIST_NAMESPACES },
+ { "synchronize-on-exit", required_argument, NULL, ARG_SYNCHRONIZE_ON_EXIT },
{}
};
break;
}
+
+ case ARG_SYNCHRONIZE_ON_EXIT:
+ r = parse_boolean_argument("--synchronize-on-exit", optarg, &arg_synchronize_on_exit);
+ if (r < 0)
+ return r;
+
+ break;
+
case '?':
return -EINVAL;