journalctl: make pcre2 a dlopen() dependency
authorLennart Poettering <lennart@poettering.net>
Wed, 24 Jun 2020 12:56:28 +0000 (14:56 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 21 Jul 2020 08:37:54 +0000 (10:37 +0200)
Let's make use of the library if it is installed, but otherwise just
generate a nice error and provide all other functionality.

meson.build
src/journal/journalctl.c
src/journal/meson.build
src/journal/pcre2-dlopen.c [new file with mode: 0644]
src/journal/pcre2-dlopen.h [new file with mode: 0644]

index 020a7e5..170e3da 100644 (file)
@@ -1798,8 +1798,8 @@ public_programs += executable(
                         libqrencode,
                         libxz,
                         liblz4,
-                        libpcre2,
-                        libzstd],
+                        libzstd,
+                        libdl],
         install_rpath : rootlibexecdir,
         install : true,
         install_dir : rootbindir)
index 8d4897b..a957ac1 100644 (file)
@@ -56,6 +56,7 @@
 #include "pager.h"
 #include "parse-util.h"
 #include "path-util.h"
+#include "pcre2-dlopen.h"
 #include "pretty-print.h"
 #include "rlimit-util.h"
 #include "set.h"
@@ -160,20 +161,20 @@ typedef struct BootId {
 } BootId;
 
 #if HAVE_PCRE2
-DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pcre2_code_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, sym_pcre2_match_data_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, sym_pcre2_code_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);
+        p = sym_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);
+                r = sym_pcre2_get_error_message(errorcode, buf, sizeof buf);
 
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Bad pattern \"%s\": %s", pattern,
@@ -183,7 +184,6 @@ static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out
         *out = p;
         return 0;
 }
-
 #endif
 
 static int add_matches_for_device(sd_journal *j, const char *devpath) {
@@ -1087,14 +1087,18 @@ static int parse_argv(int argc, char *argv[]) {
         if (arg_pattern) {
                 unsigned flags;
 
+                r = dlopen_pcre2();
+                if (r < 0)
+                        return r;
+
                 if (arg_case_sensitive >= 0)
                         flags = !arg_case_sensitive * PCRE2_CASELESS;
                 else {
-                        _cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL;
+                        _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
                         bool has_case;
-                        _cleanup_(pcre2_code_freep) pcre2_code *cs = NULL;
+                        _cleanup_(sym_pcre2_code_freep) pcre2_code *cs = NULL;
 
-                        md = pcre2_match_data_create(1, NULL);
+                        md = sym_pcre2_match_data_create(1, NULL);
                         if (!md)
                                 return log_oom();
 
@@ -1102,7 +1106,7 @@ static int parse_argv(int argc, char *argv[]) {
                         if (r < 0)
                                 return r;
 
-                        r = pcre2_match(cs, (PCRE2_SPTR8) arg_pattern, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL);
+                        r = sym_pcre2_match(cs, (PCRE2_SPTR8) arg_pattern, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL);
                         has_case = r >= 0;
 
                         flags = !has_case * PCRE2_CASELESS;
@@ -2630,12 +2634,12 @@ int main(int argc, char *argv[]) {
 
 #if HAVE_PCRE2
                         if (arg_compiled_pattern) {
-                                _cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL;
+                                _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
                                 const void *message;
                                 size_t len;
                                 PCRE2_SIZE *ovec;
 
-                                md = pcre2_match_data_create(1, NULL);
+                                md = sym_pcre2_match_data_create(1, NULL);
                                 if (!md)
                                         return log_oom();
 
@@ -2652,13 +2656,13 @@ int main(int argc, char *argv[]) {
 
                                 assert_se(message = startswith(message, "MESSAGE="));
 
-                                r = pcre2_match(arg_compiled_pattern,
-                                                message,
-                                                len - strlen("MESSAGE="),
-                                                0,      /* start at offset 0 in the subject */
-                                                0,      /* default options */
-                                                md,
-                                                NULL);
+                                r = sym_pcre2_match(arg_compiled_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;
@@ -2667,14 +2671,14 @@ int main(int argc, char *argv[]) {
                                         unsigned char buf[LINE_MAX];
                                         int r2;
 
-                                        r2 = pcre2_get_error_message(r, buf, sizeof buf);
+                                        r2 = sym_pcre2_get_error_message(r, buf, sizeof buf);
                                         log_error("Pattern matching failed: %s",
                                                   r2 < 0 ? "unknown error" : (char*) buf);
                                         r = -EINVAL;
                                         goto finish;
                                 }
 
-                                ovec = pcre2_get_ovector_pointer(md);
+                                ovec = sym_pcre2_get_ovector_pointer(md);
                                 highlight[0] = ovec[0];
                                 highlight[1] = ovec[1];
                         }
@@ -2766,7 +2770,7 @@ finish:
 
 #if HAVE_PCRE2
         if (arg_compiled_pattern) {
-                pcre2_code_free(arg_compiled_pattern);
+                sym_pcre2_code_free(arg_compiled_pattern);
 
                 /* --grep was used, no error was thrown, but the pattern didn't
                  * match anything. Let's mimic grep's behavior here and return
index 5796f77..3a590bd 100644 (file)
@@ -101,7 +101,11 @@ journald_gperf_c = custom_target(
 
 systemd_cat_sources = files('cat.c')
 
-journalctl_sources = files('journalctl.c')
+journalctl_sources = files('''
+        journalctl.c
+        pcre2-dlopen.c
+        pcre2-dlopen.h
+'''.split())
 
 if conf.get('HAVE_QRENCODE') == 1
         journalctl_sources += files('journal-qrcode.c',
diff --git a/src/journal/pcre2-dlopen.c b/src/journal/pcre2-dlopen.c
new file mode 100644 (file)
index 0000000..1e1108c
--- /dev/null
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "alloc-util.h"
+#include "dlfcn-util.h"
+#include "pcre2-dlopen.h"
+
+#if HAVE_PCRE2
+static void *pcre2_dl = NULL;
+
+pcre2_match_data* (*sym_pcre2_match_data_create)(uint32_t, pcre2_general_context *);
+void (*sym_pcre2_match_data_free)(pcre2_match_data *);
+void (*sym_pcre2_code_free)(pcre2_code *);
+pcre2_code* (*sym_pcre2_compile)(PCRE2_SPTR, PCRE2_SIZE, uint32_t, int *, PCRE2_SIZE *, pcre2_compile_context *);
+int (*sym_pcre2_get_error_message)(int, PCRE2_UCHAR *, PCRE2_SIZE);
+int (*sym_pcre2_match)(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, pcre2_match_data *, pcre2_match_context *);
+PCRE2_SIZE* (*sym_pcre2_get_ovector_pointer)(pcre2_match_data *);
+
+int dlopen_pcre2(void) {
+        _cleanup_(dlclosep) void *dl = NULL;
+        int r;
+
+        if (pcre2_dl)
+                return 0; /* Already loaded */
+
+        dl = dlopen("libpcre2-8.so.0", RTLD_LAZY);
+        if (!dl)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                       "PCRE2 support is not installed: %s", dlerror());
+
+        r = dlsym_many_and_warn(
+                        dl,
+                        LOG_ERR,
+                        &sym_pcre2_match_data_create, "pcre2_match_data_create_8",
+                        &sym_pcre2_match_data_free, "pcre2_match_data_free_8",
+                        &sym_pcre2_code_free, "pcre2_code_free_8",
+                        &sym_pcre2_compile, "pcre2_compile_8",
+                        &sym_pcre2_get_error_message, "pcre2_get_error_message_8",
+                        &sym_pcre2_match, "pcre2_match_8",
+                        &sym_pcre2_get_ovector_pointer, "pcre2_get_ovector_pointer_8",
+                        NULL);
+        if (r < 0)
+                return r;
+
+        /* Note that we never release the reference here, because there's no real reason to, after all this
+         * was traditionally a regular shared library dependency which lives forever too. */
+        pcre2_dl = TAKE_PTR(dl);
+
+        return 1;
+}
+
+#else
+
+int dlopen_pcre2(void) {
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                               "PCRE2 support is not compiled in.");
+}
+#endif
diff --git a/src/journal/pcre2-dlopen.h b/src/journal/pcre2-dlopen.h
new file mode 100644 (file)
index 0000000..e7cb0a5
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if HAVE_PCRE2
+
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
+
+extern pcre2_match_data* (*sym_pcre2_match_data_create)(uint32_t, pcre2_general_context *);
+extern void (*sym_pcre2_match_data_free)(pcre2_match_data *);
+extern void (*sym_pcre2_code_free)(pcre2_code *);
+extern pcre2_code* (*sym_pcre2_compile)(PCRE2_SPTR, PCRE2_SIZE, uint32_t, int *, PCRE2_SIZE *, pcre2_compile_context *);
+extern int (*sym_pcre2_get_error_message)(int, PCRE2_UCHAR *, PCRE2_SIZE);
+extern int (*sym_pcre2_match)(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, pcre2_match_data *, pcre2_match_context *);
+extern PCRE2_SIZE* (*sym_pcre2_get_ovector_pointer)(pcre2_match_data *);
+#endif
+
+int dlopen_pcre2(void);