]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
journalctl: regexp matching
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 12 Jan 2018 06:55:45 +0000 (07:55 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Sat, 27 Jan 2018 12:40:57 +0000 (13:40 +0100)
man/journalctl.xml
meson.build
src/journal/journalctl.c

index 257ff5a8168d3a16dd3592f18bf6b7e1417986b4..91cd4714d5fa091ade74ffdbd1f93c10091864e4 100644 (file)
         priorities.</para></listitem>
       </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>
index 686ec6ab8462128150168d449ed24a7f0634cb43..1ecc6b088ba193f069ba5ced212aacf69beba465 100644 (file)
@@ -1430,7 +1430,8 @@ exe = executable('journalctl',
                  dependencies : [threads,
                                  libqrencode,
                                  libxz,
-                                 liblz4],
+                                 liblz4,
+                                 libpcre2],
                  install_rpath : rootlibexecdir,
                  install : true,
                  install_dir : rootbindir)
index 7078f11b0dd766b333da5ece7e7cd2cd674425fb..44c7c7aec776e63cef32917ab5382c8864bd2c72 100644 (file)
 #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"
 
 
 #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;
 }