]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-internal.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-internal.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "alloc-util.h"
4 #include "bus-internal.h"
5 #include "bus-message.h"
6 #include "hexdecoct.h"
7 #include "string-util.h"
8
9 bool object_path_is_valid(const char *p) {
10 const char *q;
11 bool slash;
12
13 if (!p)
14 return false;
15
16 if (p[0] != '/')
17 return false;
18
19 if (p[1] == 0)
20 return true;
21
22 for (slash = true, q = p+1; *q; q++)
23 if (*q == '/') {
24 if (slash)
25 return false;
26
27 slash = true;
28 } else {
29 bool good;
30
31 good =
32 (*q >= 'a' && *q <= 'z') ||
33 (*q >= 'A' && *q <= 'Z') ||
34 (*q >= '0' && *q <= '9') ||
35 *q == '_';
36
37 if (!good)
38 return false;
39
40 slash = false;
41 }
42
43 if (slash)
44 return false;
45
46 return (q - p) <= BUS_PATH_SIZE_MAX;
47 }
48
49 char* object_path_startswith(const char *a, const char *b) {
50 const char *p;
51
52 if (!object_path_is_valid(a) ||
53 !object_path_is_valid(b))
54 return NULL;
55
56 if (streq(b, "/"))
57 return (char*) a + 1;
58
59 p = startswith(a, b);
60 if (!p)
61 return NULL;
62
63 if (*p == 0)
64 return (char*) p;
65
66 if (*p == '/')
67 return (char*) p + 1;
68
69 return NULL;
70 }
71
72 bool interface_name_is_valid(const char *p) {
73 const char *q;
74 bool dot, found_dot = false;
75
76 if (isempty(p))
77 return false;
78
79 for (dot = true, q = p; *q; q++)
80 if (*q == '.') {
81 if (dot)
82 return false;
83
84 found_dot = dot = true;
85 } else {
86 bool good;
87
88 good =
89 (*q >= 'a' && *q <= 'z') ||
90 (*q >= 'A' && *q <= 'Z') ||
91 (!dot && *q >= '0' && *q <= '9') ||
92 *q == '_';
93
94 if (!good)
95 return false;
96
97 dot = false;
98 }
99
100 if (q - p > 255)
101 return false;
102
103 if (dot)
104 return false;
105
106 if (!found_dot)
107 return false;
108
109 return true;
110 }
111
112 bool service_name_is_valid(const char *p) {
113 const char *q;
114 bool dot, found_dot = false, unique;
115
116 if (isempty(p))
117 return false;
118
119 unique = p[0] == ':';
120
121 for (dot = true, q = unique ? p+1 : p; *q; q++)
122 if (*q == '.') {
123 if (dot)
124 return false;
125
126 found_dot = dot = true;
127 } else {
128 bool good;
129
130 good =
131 (*q >= 'a' && *q <= 'z') ||
132 (*q >= 'A' && *q <= 'Z') ||
133 ((!dot || unique) && *q >= '0' && *q <= '9') ||
134 IN_SET(*q, '_', '-');
135
136 if (!good)
137 return false;
138
139 dot = false;
140 }
141
142 if (q - p > 255)
143 return false;
144
145 if (dot)
146 return false;
147
148 if (!found_dot)
149 return false;
150
151 return true;
152 }
153
154 bool member_name_is_valid(const char *p) {
155 const char *q;
156
157 if (isempty(p))
158 return false;
159
160 for (q = p; *q; q++) {
161 bool good;
162
163 good =
164 (*q >= 'a' && *q <= 'z') ||
165 (*q >= 'A' && *q <= 'Z') ||
166 (*q >= '0' && *q <= '9') ||
167 *q == '_';
168
169 if (!good)
170 return false;
171 }
172
173 if (q - p > 255)
174 return false;
175
176 return true;
177 }
178
179 /*
180 * Complex pattern match
181 * This checks whether @a is a 'complex-prefix' of @b, or @b is a
182 * 'complex-prefix' of @a, based on strings that consist of labels with @c as
183 * spearator. This function returns true if:
184 * - both strings are equal
185 * - either is a prefix of the other and ends with @c
186 * The second rule makes sure that either string needs to be fully included in
187 * the other, and the string which is considered the prefix needs to end with a
188 * separator.
189 */
190 static bool complex_pattern_check(char c, const char *a, const char *b) {
191 bool separator = false;
192
193 if (!a && !b)
194 return true;
195
196 if (!a || !b)
197 return false;
198
199 for (;;) {
200 if (*a != *b)
201 return (separator && (*a == 0 || *b == 0));
202
203 if (*a == 0)
204 return true;
205
206 separator = *a == c;
207
208 a++, b++;
209 }
210 }
211
212 bool namespace_complex_pattern(const char *pattern, const char *value) {
213 return complex_pattern_check('.', pattern, value);
214 }
215
216 bool path_complex_pattern(const char *pattern, const char *value) {
217 return complex_pattern_check('/', pattern, value);
218 }
219
220 /*
221 * Simple pattern match
222 * This checks whether @a is a 'simple-prefix' of @b, based on strings that
223 * consist of labels with @c as separator. This function returns true, if:
224 * - if @a and @b are equal
225 * - if @a is a prefix of @b, and the first following character in @b (or the
226 * last character in @a) is @c
227 * The second rule basically makes sure that if @a is a prefix of @b, then @b
228 * must follow with a new label separated by @c. It cannot extend the label.
229 */
230 static bool simple_pattern_check(char c, const char *a, const char *b) {
231 bool separator = false;
232
233 if (!a && !b)
234 return true;
235
236 if (!a || !b)
237 return false;
238
239 for (;;) {
240 if (*a != *b)
241 return *a == 0 && (*b == c || separator);
242
243 if (*a == 0)
244 return true;
245
246 separator = *a == c;
247
248 a++, b++;
249 }
250 }
251
252 bool namespace_simple_pattern(const char *pattern, const char *value) {
253 return simple_pattern_check('.', pattern, value);
254 }
255
256 bool path_simple_pattern(const char *pattern, const char *value) {
257 return simple_pattern_check('/', pattern, value);
258 }
259
260 int bus_message_type_from_string(const char *s, uint8_t *u) {
261 if (streq(s, "signal"))
262 *u = SD_BUS_MESSAGE_SIGNAL;
263 else if (streq(s, "method_call"))
264 *u = SD_BUS_MESSAGE_METHOD_CALL;
265 else if (streq(s, "error"))
266 *u = SD_BUS_MESSAGE_METHOD_ERROR;
267 else if (streq(s, "method_return"))
268 *u = SD_BUS_MESSAGE_METHOD_RETURN;
269 else
270 return -EINVAL;
271
272 return 0;
273 }
274
275 const char *bus_message_type_to_string(uint8_t u) {
276 if (u == SD_BUS_MESSAGE_SIGNAL)
277 return "signal";
278 else if (u == SD_BUS_MESSAGE_METHOD_CALL)
279 return "method_call";
280 else if (u == SD_BUS_MESSAGE_METHOD_ERROR)
281 return "error";
282 else if (u == SD_BUS_MESSAGE_METHOD_RETURN)
283 return "method_return";
284 else
285 return NULL;
286 }
287
288 char *bus_address_escape(const char *v) {
289 const char *a;
290 char *r, *b;
291
292 r = new(char, strlen(v)*3+1);
293 if (!r)
294 return NULL;
295
296 for (a = v, b = r; *a; a++) {
297
298 if ((*a >= '0' && *a <= '9') ||
299 (*a >= 'a' && *a <= 'z') ||
300 (*a >= 'A' && *a <= 'Z') ||
301 strchr("_-/.", *a))
302 *(b++) = *a;
303 else {
304 *(b++) = '%';
305 *(b++) = hexchar(*a >> 4);
306 *(b++) = hexchar(*a & 0xF);
307 }
308 }
309
310 *b = 0;
311 return r;
312 }
313
314 int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) {
315 assert(m);
316
317 if (r < 0) {
318 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
319 sd_bus_reply_method_errno(m, r, error);
320
321 } else if (sd_bus_error_is_set(error)) {
322 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
323 sd_bus_reply_method_error(m, error);
324 } else
325 return r;
326
327 log_debug("Failed to process message type=%s sender=%s destination=%s path=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " signature=%s error-name=%s error-message=%s: %s",
328 bus_message_type_to_string(m->header->type),
329 strna(sd_bus_message_get_sender(m)),
330 strna(sd_bus_message_get_destination(m)),
331 strna(sd_bus_message_get_path(m)),
332 strna(sd_bus_message_get_interface(m)),
333 strna(sd_bus_message_get_member(m)),
334 BUS_MESSAGE_COOKIE(m),
335 m->reply_cookie,
336 strna(m->root_container.signature),
337 strna(m->error.name),
338 strna(m->error.message),
339 bus_error_message(error, r));
340
341 return 1;
342 }