journalctl: New option --cursor-file

The option cursor-file takes a filename as argument. If the file exists and
contains a valid cursor, this is used to start the output after this position.
At the end, the last cursor gets written to the file.

This allows for an easy implementation of a timer that regularly looks in the
journal for some messages.

    journalctl --cursor-file err-cursor -b -p err
    journalctl --cursor-file audit-cursor -t audit --grep DENIED

Or you might want to walk the journal in steps of 10 messages:

    journalctl --cursor-file ./curs -n10 --since=today -t systemd
This commit is contained in:
Jörg Sommer 2019-02-12 00:19:13 +01:00 committed by Lennart Poettering
parent 200fb167a2
commit d9e15cbd18
4 changed files with 67 additions and 12 deletions

View file

@ -618,6 +618,17 @@
journal specified by the passed cursor.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--cursor-file=<replaceable>FILE</replaceable></option></term>
<listitem><para>If <replaceable>FILE</replaceable> exists and contains a
cursor, start showing entries <emphasis>after</emphasis> this location.
Otherwise the show entries according the other given options. At the end,
write the cursor of the last entry to <replaceable>FILE</replaceable>. Use
this option to continually read the journal by sequentially calling
<command>journalctl</command>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--after-cursor=</option></term>

View file

@ -48,7 +48,7 @@ _journalctl() {
-M --machine -o --output -u --unit --user-unit -p --priority
--root --case-sensitive'
[ARGUNKNOWN]='-c --cursor --interval -n --lines -S --since -U --until
--after-cursor --verify-key -g --grep
--after-cursor --cursor-file --verify-key -g --grep
--vacuum-size --vacuum-time --vacuum-files --output-fields'
)

View file

@ -104,6 +104,7 @@ _arguments -s \
{-p+,--priority=}'[Show only messages within the specified priority range]:priority:_journalctl_field_values PRIORITY' \
{-t+,--identifier=}'[Show only messages with the specified syslog identifier]:identifier:_journalctl_field_values SYSLOG_IDENTIFIER' \
{-c+,--cursor=}'[Start showing entries from the specified cursor]:cursors:_journalctl_field_values __CURSORS' \
'--cursor-file=[Show entries using cursor store in file]:file:_files' \
'--after-cursor=[Start showing entries from after the specified cursor]:cursors:_journalctl_field_values __CURSORS' \
'--since=[Start showing entries on or newer than the specified date]:YYYY-MM-DD HH\:MM\:SS' \
'--until=[Stop showing entries on or older than the specified date]:YYYY-MM-DD HH\:MM\:SS' \

View file

@ -119,6 +119,7 @@ static int arg_boot_offset = 0;
static bool arg_dmesg = false;
static bool arg_no_hostname = false;
static const char *arg_cursor = NULL;
static const char *arg_cursor_file = NULL;
static const char *arg_after_cursor = NULL;
static bool arg_show_cursor = false;
static const char *arg_directory = NULL;
@ -315,6 +316,7 @@ static int help(void) {
" -c --cursor=CURSOR Show entries starting at the specified cursor\n"
" --after-cursor=CURSOR Show entries after the specified cursor\n"
" --show-cursor Print the cursor after all the entries\n"
" --cursor-file=FILE Show entries after cursor in FILE and update FILE\n"
" -b --boot[=ID] Show current boot or the specified boot\n"
" --list-boots Show terse information about recorded boots\n"
" -k --dmesg Show kernel message log from the current boot\n"
@ -396,6 +398,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VERIFY_KEY,
ARG_DISK_USAGE,
ARG_AFTER_CURSOR,
ARG_CURSOR_FILE,
ARG_SHOW_CURSOR,
ARG_USER_UNIT,
ARG_LIST_CATALOG,
@ -450,6 +453,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
{ "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
{ "cursor", required_argument, NULL, 'c' },
{ "cursor-file", required_argument, NULL, ARG_CURSOR_FILE },
{ "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
{ "show-cursor", no_argument, NULL, ARG_SHOW_CURSOR },
{ "since", required_argument, NULL, 'S' },
@ -661,6 +665,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_cursor = optarg;
break;
case ARG_CURSOR_FILE:
arg_cursor_file = optarg;
break;
case ARG_AFTER_CURSOR:
arg_after_cursor = optarg;
break;
@ -2077,6 +2085,7 @@ static int wait_for_change(sd_journal *j, int poll_fd) {
int main(int argc, char *argv[]) {
bool previous_boot_id_valid = false, first_line = true, ellipsized = false, need_seek = false;
bool use_cursor = false, after_cursor = false;
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
sd_id128_t previous_boot_id;
int n_shown = 0, r, poll_fd = -1;
@ -2420,19 +2429,41 @@ int main(int argc, char *argv[]) {
}
}
if (arg_cursor || arg_after_cursor) {
r = sd_journal_seek_cursor(j, arg_cursor ?: arg_after_cursor);
if (r < 0) {
log_error_errno(r, "Failed to seek to cursor: %m");
goto finish;
if (arg_cursor || arg_after_cursor || arg_cursor_file) {
_cleanup_free_ char *cursor_from_file = NULL;
const char *cursor = arg_cursor ?: arg_after_cursor;
if (arg_cursor_file) {
r = read_one_line_file(arg_cursor_file, &cursor_from_file);
if (r < 0 && r != -ENOENT) {
log_error_errno(r, "Failed to read cursor file %s: %m", arg_cursor_file);
goto finish;
}
if (r > 0) {
cursor = cursor_from_file;
after_cursor = true;
}
} else
after_cursor = !!arg_after_cursor;
if (cursor) {
r = sd_journal_seek_cursor(j, cursor);
if (r < 0) {
log_error_errno(r, "Failed to seek to cursor: %m");
goto finish;
}
use_cursor = true;
}
}
if (use_cursor) {
if (!arg_reverse)
r = sd_journal_next_skip(j, 1 + !!arg_after_cursor);
r = sd_journal_next_skip(j, 1 + after_cursor);
else
r = sd_journal_previous_skip(j, 1 + !!arg_after_cursor);
r = sd_journal_previous_skip(j, 1 + after_cursor);
if (arg_after_cursor && r < 2) {
if (after_cursor && r < 2) {
/* We couldn't find the next entry after the cursor. */
if (arg_follow)
need_seek = true;
@ -2661,14 +2692,26 @@ int main(int argc, char *argv[]) {
if (n_shown == 0 && !arg_quiet)
printf("-- No entries --\n");
if (arg_show_cursor) {
if (arg_show_cursor || arg_cursor_file) {
_cleanup_free_ char *cursor = NULL;
r = sd_journal_get_cursor(j, &cursor);
if (r < 0 && r != -EADDRNOTAVAIL)
log_error_errno(r, "Failed to get cursor: %m");
else if (r >= 0)
printf("-- cursor: %s\n", cursor);
else if (r >= 0) {
if (arg_show_cursor)
printf("-- cursor: %s\n", cursor);
if (arg_cursor_file) {
r = write_string_file(arg_cursor_file, cursor,
WRITE_STRING_FILE_CREATE |
WRITE_STRING_FILE_ATOMIC);
if (r < 0)
log_error_errno(r,
"Failed to write new cursor to %s: %m",
arg_cursor_file);
}
}
}
break;