diff options
-rw-r--r-- | man/journalctl.xml | 11 | ||||
-rw-r--r-- | meson.build | 3 | ||||
-rw-r--r-- | src/journal/journalctl.c | 108 |
3 files changed, 121 insertions, 1 deletions
diff --git a/man/journalctl.xml b/man/journalctl.xml index 257ff5a816..91cd4714d5 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -579,6 +579,17 @@ </varlistentry> <varlistentry> + <term><option>-g</option></term> + <term><option>--grep=</option></term> + + <listitem><para>Filter output to entries where the <varname>MESSAGE=</varname> + field matches the specified regular expression. PERL-compatible regular expressions + are used, see + <citerefentry><refentrytitle>pcre2pattern</refentrytitle><manvolnum>3</manvolnum></citerefentry> + for a detailed description of the syntax.</para></listitem> + </varlistentry> + + <varlistentry> <term><option>-c</option></term> <term><option>--cursor=</option></term> diff --git a/meson.build b/meson.build index 686ec6ab84..1ecc6b088b 100644 --- a/meson.build +++ b/meson.build @@ -1430,7 +1430,8 @@ exe = executable('journalctl', dependencies : [threads, libqrencode, libxz, - liblz4], + liblz4, + libpcre2], install_rpath : rootlibexecdir, install : true, install_dir : rootbindir) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 7078f11b0d..44c7c7aec7 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -34,6 +34,11 @@ #include <sys/stat.h> #include <unistd.h> +#if HAVE_PCRE2 +# define PCRE2_CODE_UNIT_WIDTH 8 +# include <pcre2.h> +#endif + #include "sd-bus.h" #include "sd-journal.h" @@ -76,6 +81,33 @@ #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE) +#if HAVE_PCRE2 +DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free); + +static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) { + int errorcode, r; + PCRE2_SIZE erroroffset; + pcre2_code *p; + + p = pcre2_compile((PCRE2_SPTR8) pattern, + PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL); + if (!p) { + unsigned char buf[LINE_MAX]; + + r = pcre2_get_error_message(errorcode, buf, sizeof buf); + + log_error("Bad pattern \"%s\": %s", + pattern, + r < 0 ? "unknown error" : (char*) buf); + return -EINVAL; + } + + *out = p; + return 0; +} + +#endif + enum { /* Special values for arg_lines */ ARG_LINES_DEFAULT = -2, @@ -126,6 +158,10 @@ static uint64_t arg_vacuum_n_files = 0; static usec_t arg_vacuum_time = 0; static char **arg_output_fields = NULL; +#if HAVE_PCRE2 +static pcre2_code *arg_pattern = NULL; +#endif + static enum { ACTION_SHOW, ACTION_NEW_ID128, @@ -295,6 +331,7 @@ static void help(void) { " --user-unit=UNIT Show logs from the specified user unit\n" " -t --identifier=STRING Show entries with the specified syslog identifier\n" " -p --priority=RANGE Show entries with the specified priority\n" + " -g --grep=PATTERN Show entries with MESSSAGE matching PATTERN\n" " -e --pager-end Immediately jump to the end in the pager\n" " -f --follow Follow the journal\n" " -n --lines[=INTEGER] Number of journal entries to show\n" @@ -411,6 +448,7 @@ static int parse_argv(int argc, char *argv[]) { { "header", no_argument, NULL, ARG_HEADER }, { "identifier", required_argument, NULL, 't' }, { "priority", required_argument, NULL, 'p' }, + { "grep", required_argument, NULL, 'g' }, { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS }, { "interval", required_argument, NULL, ARG_INTERVAL }, { "verify", no_argument, NULL, ARG_VERIFY }, @@ -762,6 +800,24 @@ static int parse_argv(int argc, char *argv[]) { break; } +#if HAVE_PCRE2 + case 'g': { + if (arg_pattern) { + pcre2_code_free(arg_pattern); + arg_pattern = NULL; + } + r = pattern_compile(optarg, 0, &arg_pattern); + if (r < 0) + return r; + + break; + } + +#else + case 'g': + return log_error("Compiled without pattern matching support"); +#endif + case 'S': r = parse_timestamp(optarg, &arg_since); if (r < 0) { @@ -2468,6 +2524,53 @@ int main(int argc, char *argv[]) { } } +#if HAVE_PCRE2 + if (arg_pattern) { + _cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL; + const void *message; + size_t len; + + md = pcre2_match_data_create(1, NULL); + if (!md) + return log_oom(); + + r = sd_journal_get_data(j, "MESSAGE", &message, &len); + if (r < 0) { + if (r == -ENOENT) { + need_seek = true; + continue; + } + + log_error_errno(r, "Failed to get MESSAGE field: %m"); + goto finish; + } + + assert_se(message = startswith(message, "MESSAGE=")); + + r = pcre2_match(arg_pattern, + message, + len - strlen("MESSAGE="), + 0, /* start at offset 0 in the subject */ + 0, /* default options */ + md, + NULL); + if (r == PCRE2_ERROR_NOMATCH) { + need_seek = true; + continue; + } + if (r < 0) { + unsigned char buf[LINE_MAX]; + int r2; + + r2 = pcre2_get_error_message(r, buf, sizeof buf); + log_error("Pattern matching failed: %s", + r2 < 0 ? "unknown error" : (char*) buf); + r = -EINVAL; + goto finish; + } + } +#endif + flags = arg_all * OUTPUT_SHOW_ALL | arg_full * OUTPUT_FULL_WIDTH | @@ -2527,5 +2630,10 @@ finish: free(arg_root); free(arg_verify_key); +#if HAVE_PCRE2 + if (arg_pattern) + pcre2_code_free(arg_pattern); +#endif + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } |