]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/pcre2-util.c
machined: return recognizable error when we try to register the same machine name...
[thirdparty/systemd.git] / src / shared / pcre2-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "dlfcn-util.h"
4 #include "log.h"
5 #include "pcre2-util.h"
6
7 #if HAVE_PCRE2
8 static void *pcre2_dl = NULL;
9
10 DLSYM_FUNCTION(pcre2_match_data_create);
11 DLSYM_FUNCTION(pcre2_match_data_free);
12 DLSYM_FUNCTION(pcre2_code_free);
13 DLSYM_FUNCTION(pcre2_compile);
14 DLSYM_FUNCTION(pcre2_get_error_message);
15 DLSYM_FUNCTION(pcre2_match);
16 DLSYM_FUNCTION(pcre2_get_ovector_pointer);
17
18 DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
19 pcre2_code_hash_ops_free,
20 pcre2_code,
21 (void (*)(const pcre2_code *, struct siphash*))trivial_hash_func,
22 (int (*)(const pcre2_code *, const pcre2_code*))trivial_compare_func,
23 sym_pcre2_code_free);
24 #else
25 const struct hash_ops pcre2_code_hash_ops_free = {};
26 #endif
27
28 int dlopen_pcre2(void) {
29 #if HAVE_PCRE2
30 /* So here's something weird: PCRE2 actually renames the symbols exported by the library via C
31 * macros, so that the exported symbols carry a suffix "_8" but when used from C the suffix is
32 * gone. In the argument list below we ignore this mangling. Surprisingly (at least to me), we
33 * actually get away with that. That's because DLSYM_ARG() useses STRINGIFY() to generate a string
34 * version of the symbol name, and that resolves the macro mapping implicitly already, so that the
35 * string actually contains the "_8" suffix already due to that and we don't have to append it
36 * manually anymore. C is weird. 🤯 */
37
38 return dlopen_many_sym_or_warn(
39 &pcre2_dl, "libpcre2-8.so.0", LOG_ERR,
40 DLSYM_ARG(pcre2_match_data_create),
41 DLSYM_ARG(pcre2_match_data_free),
42 DLSYM_ARG(pcre2_code_free),
43 DLSYM_ARG(pcre2_compile),
44 DLSYM_ARG(pcre2_get_error_message),
45 DLSYM_ARG(pcre2_match),
46 DLSYM_ARG(pcre2_get_ovector_pointer));
47 #else
48 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "PCRE2 support is not compiled in.");
49 #endif
50 }
51
52 int pattern_compile_and_log(const char *pattern, PatternCompileCase case_, pcre2_code **ret) {
53 #if HAVE_PCRE2
54 PCRE2_SIZE erroroffset;
55 _cleanup_(sym_pcre2_code_freep) pcre2_code *p = NULL;
56 unsigned flags = 0;
57 int errorcode, r;
58
59 assert(pattern);
60
61 r = dlopen_pcre2();
62 if (r < 0)
63 return r;
64
65 if (case_ == PATTERN_COMPILE_CASE_INSENSITIVE)
66 flags = PCRE2_CASELESS;
67 else if (case_ == PATTERN_COMPILE_CASE_AUTO) {
68 _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
69 bool has_case;
70 _cleanup_(sym_pcre2_code_freep) pcre2_code *cs = NULL;
71
72 md = sym_pcre2_match_data_create(1, NULL);
73 if (!md)
74 return log_oom();
75
76 r = pattern_compile_and_log("[[:upper:]]", PATTERN_COMPILE_CASE_SENSITIVE, &cs);
77 if (r < 0)
78 return r;
79
80 r = sym_pcre2_match(cs, (PCRE2_SPTR8) pattern, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL);
81 has_case = r >= 0;
82
83 flags = !has_case * PCRE2_CASELESS;
84 }
85
86 log_debug("Doing case %s matching based on %s",
87 flags & PCRE2_CASELESS ? "insensitive" : "sensitive",
88 case_ != PATTERN_COMPILE_CASE_AUTO ? "request" : "pattern casing");
89
90 p = sym_pcre2_compile((PCRE2_SPTR8) pattern,
91 PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
92 if (!p) {
93 unsigned char buf[LINE_MAX];
94
95 r = sym_pcre2_get_error_message(errorcode, buf, sizeof buf);
96
97 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
98 "Bad pattern \"%s\": %s", pattern,
99 r < 0 ? "unknown error" : (char *)buf);
100 }
101
102 if (ret)
103 *ret = TAKE_PTR(p);
104
105 return 0;
106 #else
107 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "PCRE2 support is not compiled in.");
108 #endif
109 }
110
111 int pattern_matches_and_log(pcre2_code *compiled_pattern, const char *message, size_t size, size_t *ret_ovec) {
112 #if HAVE_PCRE2
113 _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
114 int r;
115
116 assert(compiled_pattern);
117 assert(message);
118 /* pattern_compile_and_log() must be called before this function is called and that function already
119 * dlopens pcre2 so we can assert on it being available here. */
120 assert(pcre2_dl);
121
122 md = sym_pcre2_match_data_create(1, NULL);
123 if (!md)
124 return log_oom();
125
126 r = sym_pcre2_match(compiled_pattern,
127 (const unsigned char *)message,
128 size,
129 0, /* start at offset 0 in the subject */
130 0, /* default options */
131 md,
132 NULL);
133 if (r == PCRE2_ERROR_NOMATCH)
134 return false;
135 if (r < 0) {
136 unsigned char buf[LINE_MAX];
137
138 r = sym_pcre2_get_error_message(r, buf, sizeof(buf));
139 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Pattern matching failed: %s",
140 r < 0 ? "unknown error" : (char*) buf);
141 }
142
143 if (ret_ovec) {
144 ret_ovec[0] = sym_pcre2_get_ovector_pointer(md)[0];
145 ret_ovec[1] = sym_pcre2_get_ovector_pointer(md)[1];
146 }
147
148 return true;
149 #else
150 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "PCRE2 support is not compiled in.");
151 #endif
152 }
153
154 void *pattern_free(pcre2_code *p) {
155 #if HAVE_PCRE2
156 if (!p)
157 return NULL;
158
159 assert(pcre2_dl);
160 sym_pcre2_code_free(p);
161 return NULL;
162 #else
163 assert(p == NULL);
164 return NULL;
165 #endif
166 }