#include <time.h>
#include <getopt.h>
#include <signal.h>
+#include <poll.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
+#include <sys/inotify.h>
#include <linux/fs.h>
#ifdef HAVE_ACL
#include "acl-util.h"
#endif
-#include "systemd/sd-journal.h"
+#include "sd-journal.h"
+#include "sd-bus.h"
#include "log.h"
#include "logs-show.h"
#include "fsprg.h"
#include "unit-name.h"
#include "catalog.h"
+#include "mkdir.h"
+#include "bus-util.h"
+#include "bus-error.h"
#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
+enum {
+ /* Special values for arg_lines */
+ ARG_LINES_DEFAULT = -2,
+ ARG_LINES_ALL = -1,
+};
+
static OutputMode arg_output = OUTPUT_SHORT;
+static bool arg_utc = false;
static bool arg_pager_end = false;
static bool arg_follow = false;
static bool arg_full = true;
static bool arg_all = false;
static bool arg_no_pager = false;
-static int arg_lines = -1;
+static int arg_lines = ARG_LINES_DEFAULT;
static bool arg_no_tail = false;
static bool arg_quiet = false;
static bool arg_merge = false;
ACTION_DUMP_CATALOG,
ACTION_UPDATE_CATALOG,
ACTION_LIST_BOOTS,
+ ACTION_FLUSH,
} arg_action = ACTION_SHOW;
typedef struct boot_id_t {
pager_open(arg_pager_end);
}
+static char *format_timestamp_maybe_utc(char *buf, size_t l, usec_t t) {
+
+ if (arg_utc)
+ return format_timestamp_utc(buf, l, t);
+
+ return format_timestamp(buf, l, t);
+}
+
static int parse_boot_descriptor(const char *x, sd_id128_t *boot_id, int *offset) {
sd_id128_t id = SD_ID128_NULL;
int off = 0, r;
" -o --output=STRING Change journal output mode (short, short-iso,\n"
" short-precise, short-monotonic, verbose,\n"
" export, json, json-pretty, json-sse, cat)\n"
+ " --utc Express time in Coordinated Universal Time (UTC)\n"
" -x --catalog Add message explanations where available\n"
" --no-full Ellipsize fields\n"
" -a --all Show all fields, including long and unprintable\n"
" --list-catalog Show message IDs of all entries in the message catalog\n"
" --dump-catalog Show entries in the message catalog\n"
" --update-catalog Update the message catalog database\n"
+ " --flush Flush all journal data from /run into /var\n"
#ifdef HAVE_GCRYPT
" --setup-keys Generate a new FSS key pair\n"
" --verify Verify journal file consistency\n"
ARG_DUMP_CATALOG,
ARG_UPDATE_CATALOG,
ARG_FORCE,
+ ARG_UTC,
+ ARG_FLUSH,
};
static const struct option options[] = {
{ "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG },
{ "reverse", no_argument, NULL, 'r' },
{ "machine", required_argument, NULL, 'M' },
+ { "utc", no_argument, NULL, ARG_UTC },
+ { "flush", no_argument, NULL, ARG_FLUSH },
{}
};
case 'e':
arg_pager_end = true;
- if (arg_lines < 0)
+ if (arg_lines == ARG_LINES_DEFAULT)
arg_lines = 1000;
break;
case 'n':
if (optarg) {
- r = safe_atoi(optarg, &arg_lines);
- if (r < 0 || arg_lines < 0) {
- log_error("Failed to parse lines '%s'", optarg);
- return -EINVAL;
+ if (streq(optarg, "all"))
+ arg_lines = ARG_LINES_ALL;
+ else {
+ r = safe_atoi(optarg, &arg_lines);
+ if (r < 0 || arg_lines < 0) {
+ log_error("Failed to parse lines '%s'", optarg);
+ return -EINVAL;
+ }
}
} else {
- int n;
+ arg_lines = 10;
/* Hmm, no argument? Maybe the next
* word on the command line is
* supposed to be the argument? Let's
* see if there is one, and is
- * parsable as a positive
- * integer... */
-
- if (optind < argc &&
- safe_atoi(argv[optind], &n) >= 0 &&
- n >= 0) {
-
- arg_lines = n;
- optind++;
- } else
- arg_lines = 10;
+ * parsable. */
+ if (optind < argc) {
+ int n;
+ if (streq(argv[optind], "all")) {
+ arg_lines = ARG_LINES_ALL;
+ optind++;
+ } else if (safe_atoi(argv[optind], &n) >= 0 && n >= 0) {
+ arg_lines = n;
+ optind++;
+ }
+ }
}
break;
arg_reverse = true;
break;
+ case ARG_UTC:
+ arg_utc = true;
+ break;
+
+ case ARG_FLUSH:
+ arg_action = ACTION_FLUSH;
+ break;
+
case '?':
return -EINVAL;
assert_not_reached("Unhandled option");
}
- if (arg_follow && !arg_no_tail && arg_lines < 0)
+ if (arg_follow && !arg_no_tail && arg_lines == ARG_LINES_DEFAULT)
arg_lines = 10;
if (!!arg_directory + !!arg_file + !!arg_machine > 1) {
if (r < 0)
return r;
+ pager_open_if_enabled();
+
SD_JOURNAL_FOREACH_UNIQUE(j, data, length) {
- if (length < strlen("_BOOT_ID="))
- continue;
+ assert(startswith(data, "_BOOT_ID="));
if (!GREEDY_REALLOC(all_ids, allocated, count + 1))
return log_oom();
printf("% *i " SD_ID128_FORMAT_STR " %s—%s\n",
w, i - count + 1,
SD_ID128_FORMAT_VAL(id->id),
- format_timestamp(a, sizeof(a), id->first),
- format_timestamp(b, sizeof(b), id->last));
+ format_timestamp_maybe_utc(a, sizeof(a), id->first),
+ format_timestamp_maybe_utc(b, sizeof(b), id->last));
}
return 0;
const char *field;
int r;
- found = set_new(string_hash_func, string_compare_func);
+ found = set_new(&string_hash_ops);
if (!found)
return log_oom();
if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
if (validated > 0) {
log_info("=> Validated from %s to %s, final %s entries not sealed.",
- format_timestamp(a, sizeof(a), first),
- format_timestamp(b, sizeof(b), validated),
+ format_timestamp_maybe_utc(a, sizeof(a), first),
+ format_timestamp_maybe_utc(b, sizeof(b), validated),
format_timespan(c, sizeof(c), last > validated ? last - validated : 0, 0));
} else if (last > 0)
log_info("=> No sealing yet, %s of entries not sealed.",
return r;
}
+static int flush_to_var(void) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_close_unref_ sd_bus *bus = NULL;
+ _cleanup_close_ int watch_fd = -1;
+ int r;
+
+ /* Quick exit */
+ if (access("/run/systemd/journal/flushed", F_OK) >= 0)
+ return 0;
+
+ /* OK, let's actually do the full logic, send SIGUSR1 to the
+ * daemon and set up inotify to wait for the flushed file to appear */
+ r = bus_open_system_systemd(&bus);
+ if (r < 0) {
+ log_error("Failed to get D-Bus connection: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "KillUnit",
+ &error,
+ NULL,
+ "ssi", "systemd-journald.service", "main", SIGUSR1);
+ if (r < 0) {
+ log_error("Failed to kill journal service: %s", bus_error_message(&error, r));
+ return r;
+ }
+
+ mkdir_p("/run/systemd/journal", 0755);
+
+ watch_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
+ if (watch_fd < 0) {
+ log_error("Failed to create inotify watch: %m");
+ return -errno;
+ }
+
+ r = inotify_add_watch(watch_fd, "/run/systemd/journal", IN_CREATE|IN_DONT_FOLLOW|IN_ONLYDIR);
+ if (r < 0) {
+ log_error("Failed to watch journal directory: %m");
+ return -errno;
+ }
+
+ for (;;) {
+ if (access("/run/systemd/journal/flushed", F_OK) >= 0)
+ break;
+
+ if (errno != ENOENT) {
+ log_error("Failed to check for existance of /run/systemd/journal/flushed: %m");
+ return -errno;
+ }
+
+ r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY);
+ if (r < 0) {
+ log_error("Failed to wait for event: %s", strerror(-r));
+ return r;
+ }
+
+ r = flush_fd(watch_fd);
+ if (r < 0) {
+ log_error("Failed to flush inotify events: %s", strerror(-r));
+ return r;
+ }
+ }
+
+ return 0;
+}
+
int main(int argc, char *argv[]) {
int r;
_cleanup_journal_close_ sd_journal *j = NULL;
goto finish;
}
+ if (arg_action == ACTION_FLUSH) {
+ r = flush_to_var();
+ goto finish;
+ }
+
if (arg_action == ACTION_SETUP_KEYS) {
r = setup_keys();
goto finish;
if (r > 0) {
if (arg_follow)
printf("-- Logs begin at %s. --\n",
- format_timestamp(start_buf, sizeof(start_buf), start));
+ format_timestamp_maybe_utc(start_buf, sizeof(start_buf), start));
else
printf("-- Logs begin at %s, end at %s. --\n",
- format_timestamp(start_buf, sizeof(start_buf), start),
- format_timestamp(end_buf, sizeof(end_buf), end));
+ format_timestamp_maybe_utc(start_buf, sizeof(start_buf), start),
+ format_timestamp_maybe_utc(end_buf, sizeof(end_buf), end));
}
}
goto finish;
}
- if (!arg_merge) {
+ if (!arg_merge && !arg_quiet) {
sd_id128_t boot_id;
r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
arg_all * OUTPUT_SHOW_ALL |
arg_full * OUTPUT_FULL_WIDTH |
on_tty() * OUTPUT_COLOR |
- arg_catalog * OUTPUT_CATALOG;
+ arg_catalog * OUTPUT_CATALOG |
+ arg_utc * OUTPUT_UTC;
r = output_journal(stdout, j, arg_output, 0, flags, &ellipsized);
need_seek = true;