]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-error.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-error.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <stdarg.h>
5 #include <stdbool.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8
9 #include "sd-bus.h"
10
11 #include "alloc-util.h"
12 #include "bus-error.h"
13 #include "errno-list.h"
14 #include "errno-util.h"
15 #include "string-util.h"
16 #include "strv.h"
17 #include "util.h"
18
19 BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = {
20 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Failed", EACCES),
21 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoMemory", ENOMEM),
22 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ServiceUnknown", EHOSTUNREACH),
23 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NameHasNoOwner", ENXIO),
24 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoReply", ETIMEDOUT),
25 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.IOError", EIO),
26 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL),
27 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", EOPNOTSUPP),
28 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS),
29 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AccessDenied", EACCES),
30 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AuthFailed", EACCES),
31 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InteractiveAuthorizationRequired", EACCES),
32 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoServer", EHOSTDOWN),
33 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Timeout", ETIMEDOUT),
34 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoNetwork", ENONET),
35 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AddressInUse", EADDRINUSE),
36 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Disconnected", ECONNRESET),
37 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidArgs", EINVAL),
38 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileNotFound", ENOENT),
39 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileExists", EEXIST),
40 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownMethod", EBADR),
41 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownObject", EBADR),
42 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownInterface", EBADR),
43 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownProperty", EBADR),
44 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.PropertyReadOnly", EROFS),
45 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnixProcessIdUnknown", ESRCH),
46 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidSignature", EINVAL),
47 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InconsistentMessage", EBADMSG),
48 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.TimedOut", ETIMEDOUT),
49 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleInvalid", EINVAL),
50 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidFileContent", EINVAL),
51 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleNotFound", ENOENT),
52 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown", ESRCH),
53 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ObjectPathInUse", EBUSY),
54 SD_BUS_ERROR_MAP_END
55 };
56
57 /* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section */
58 extern const sd_bus_error_map __start_SYSTEMD_BUS_ERROR_MAP[];
59 extern const sd_bus_error_map __stop_SYSTEMD_BUS_ERROR_MAP[];
60
61 /* Additional maps registered with sd_bus_error_add_map() are in this
62 * NULL terminated array */
63 static const sd_bus_error_map **additional_error_maps = NULL;
64
65 static int bus_error_name_to_errno(const char *name) {
66 const sd_bus_error_map **map, *m;
67 const char *p;
68 int r;
69
70 if (!name)
71 return EINVAL;
72
73 p = startswith(name, "System.Error.");
74 if (p) {
75 r = errno_from_name(p);
76 if (r < 0)
77 return EIO;
78
79 return r;
80 }
81
82 if (additional_error_maps)
83 for (map = additional_error_maps; *map; map++)
84 for (m = *map;; m++) {
85 /* For additional error maps the end marker is actually the end marker */
86 if (m->code == BUS_ERROR_MAP_END_MARKER)
87 break;
88
89 if (streq(m->name, name))
90 return m->code;
91 }
92
93 m = ALIGN_TO_PTR(__start_SYSTEMD_BUS_ERROR_MAP, sizeof(void*));
94 while (m < __stop_SYSTEMD_BUS_ERROR_MAP) {
95 /* For magic ELF error maps, the end marker might
96 * appear in the middle of things, since multiple maps
97 * might appear in the same section. Hence, let's skip
98 * over it, but realign the pointer to the next 8 byte
99 * boundary, which is the selected alignment for the
100 * arrays. */
101 if (m->code == BUS_ERROR_MAP_END_MARKER) {
102 m = ALIGN_TO_PTR(m + 1, sizeof(void*));
103 continue;
104 }
105
106 if (streq(m->name, name))
107 return m->code;
108
109 m++;
110 }
111
112 return EIO;
113 }
114
115 static sd_bus_error errno_to_bus_error_const(int error) {
116
117 if (error < 0)
118 error = -error;
119
120 switch (error) {
121
122 case ENOMEM:
123 return BUS_ERROR_OOM;
124
125 case EPERM:
126 case EACCES:
127 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED, "Access denied");
128
129 case EINVAL:
130 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument");
131
132 case ESRCH:
133 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process");
134
135 case ENOENT:
136 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found");
137
138 case EEXIST:
139 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "File exists");
140
141 case ETIMEDOUT:
142 case ETIME:
143 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT, "Timed out");
144
145 case EIO:
146 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR, "Input/output error");
147
148 case ENETRESET:
149 case ECONNABORTED:
150 case ECONNRESET:
151 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected");
152
153 case EOPNOTSUPP:
154 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported");
155
156 case EADDRNOTAVAIL:
157 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS, "Address not available");
158
159 case ENOBUFS:
160 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded");
161
162 case EADDRINUSE:
163 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use");
164
165 case EBADMSG:
166 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message");
167 }
168
169 return SD_BUS_ERROR_NULL;
170 }
171
172 static int errno_to_bus_error_name_new(int error, char **ret) {
173 const char *name;
174 char *n;
175
176 if (error < 0)
177 error = -error;
178
179 name = errno_to_name(error);
180 if (!name)
181 return 0;
182
183 n = strjoin("System.Error.", name);
184 if (!n)
185 return -ENOMEM;
186
187 *ret = n;
188 return 1;
189 }
190
191 bool bus_error_is_dirty(sd_bus_error *e) {
192 if (!e)
193 return false;
194
195 return e->name || e->message || e->_need_free != 0;
196 }
197
198 _public_ void sd_bus_error_free(sd_bus_error *e) {
199 if (!e)
200 return;
201
202 if (e->_need_free > 0) {
203 free((void*) e->name);
204 free((void*) e->message);
205 }
206
207 *e = SD_BUS_ERROR_NULL;
208 }
209
210 _public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
211
212 if (!name)
213 return 0;
214 if (!e)
215 goto finish;
216
217 assert_return(!bus_error_is_dirty(e), -EINVAL);
218
219 e->name = strdup(name);
220 if (!e->name) {
221 *e = BUS_ERROR_OOM;
222 return -ENOMEM;
223 }
224
225 if (message)
226 e->message = strdup(message);
227
228 e->_need_free = 1;
229
230 finish:
231 return -bus_error_name_to_errno(name);
232 }
233
234 int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
235
236 if (!name)
237 return 0;
238
239 if (e) {
240 assert_return(!bus_error_is_dirty(e), -EINVAL);
241
242 e->name = strdup(name);
243 if (!e->name) {
244 *e = BUS_ERROR_OOM;
245 return -ENOMEM;
246 }
247
248 /* If we hit OOM on formatting the pretty message, we ignore
249 * this, since we at least managed to write the error name */
250 if (format)
251 (void) vasprintf((char**) &e->message, format, ap);
252
253 e->_need_free = 1;
254 }
255
256 return -bus_error_name_to_errno(name);
257 }
258
259 _public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
260
261 if (format) {
262 int r;
263 va_list ap;
264
265 va_start(ap, format);
266 r = bus_error_setfv(e, name, format, ap);
267 va_end(ap);
268
269 return r;
270 }
271
272 return sd_bus_error_set(e, name, NULL);
273 }
274
275 _public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
276
277 if (!sd_bus_error_is_set(e))
278 return 0;
279 if (!dest)
280 goto finish;
281
282 assert_return(!bus_error_is_dirty(dest), -EINVAL);
283
284 /*
285 * _need_free < 0 indicates that the error is temporarily const, needs deep copying
286 * _need_free == 0 indicates that the error is perpetually const, needs no deep copying
287 * _need_free > 0 indicates that the error is fully dynamic, needs deep copying
288 */
289
290 if (e->_need_free == 0)
291 *dest = *e;
292 else {
293 dest->name = strdup(e->name);
294 if (!dest->name) {
295 *dest = BUS_ERROR_OOM;
296 return -ENOMEM;
297 }
298
299 if (e->message)
300 dest->message = strdup(e->message);
301
302 dest->_need_free = 1;
303 }
304
305 finish:
306 return -bus_error_name_to_errno(e->name);
307 }
308
309 _public_ int sd_bus_error_move(sd_bus_error *dest, sd_bus_error *e) {
310 int r;
311
312 if (!sd_bus_error_is_set(e)) {
313
314 if (dest)
315 *dest = SD_BUS_ERROR_NULL;
316
317 return 0;
318 }
319
320 r = -bus_error_name_to_errno(e->name);
321
322 if (dest) {
323 *dest = *e;
324 *e = SD_BUS_ERROR_NULL;
325 } else
326 sd_bus_error_free(e);
327
328 return r;
329 }
330
331 _public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
332 if (!name)
333 return 0;
334 if (!e)
335 goto finish;
336
337 assert_return(!bus_error_is_dirty(e), -EINVAL);
338
339 *e = SD_BUS_ERROR_MAKE_CONST(name, message);
340
341 finish:
342 return -bus_error_name_to_errno(name);
343 }
344
345 _public_ int sd_bus_error_is_set(const sd_bus_error *e) {
346 if (!e)
347 return 0;
348
349 return !!e->name;
350 }
351
352 _public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
353 if (!e)
354 return 0;
355
356 return streq_ptr(e->name, name);
357 }
358
359 _public_ int sd_bus_error_has_names_sentinel(const sd_bus_error *e, ...) {
360 if (!e || !e->name)
361 return 0;
362
363 va_list ap;
364 const char *p;
365
366 va_start(ap, e);
367 while ((p = va_arg(ap, const char *)))
368 if (streq(p, e->name))
369 break;
370 va_end(ap);
371 return !!p;
372 }
373
374 _public_ int sd_bus_error_get_errno(const sd_bus_error* e) {
375 if (!e || !e->name)
376 return 0;
377
378 return bus_error_name_to_errno(e->name);
379 }
380
381 static void bus_error_strerror(sd_bus_error *e, int error) {
382 size_t k = 64;
383 char *m;
384
385 assert(e);
386
387 for (;;) {
388 char *x;
389
390 m = new(char, k);
391 if (!m)
392 return;
393
394 errno = 0;
395 x = strerror_r(error, m, k);
396 if (errno == ERANGE || strlen(x) >= k - 1) {
397 free(m);
398 k *= 2;
399 continue;
400 }
401
402 if (errno) {
403 free(m);
404 return;
405 }
406
407 if (x == m) {
408 if (e->_need_free > 0) {
409 /* Error is already dynamic, let's just update the message */
410 free((char*) e->message);
411 e->message = x;
412
413 } else {
414 char *t;
415 /* Error was const so far, let's make it dynamic, if we can */
416
417 t = strdup(e->name);
418 if (!t) {
419 free(m);
420 return;
421 }
422
423 e->_need_free = 1;
424 e->name = t;
425 e->message = x;
426 }
427 } else {
428 free(m);
429
430 if (e->_need_free > 0) {
431 char *t;
432
433 /* Error is dynamic, let's hence make the message also dynamic */
434 t = strdup(x);
435 if (!t)
436 return;
437
438 free((char*) e->message);
439 e->message = t;
440 } else {
441 /* Error is const, hence we can just override */
442 e->message = x;
443 }
444 }
445
446 return;
447 }
448 }
449
450 _public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) {
451
452 if (error < 0)
453 error = -error;
454
455 if (!e)
456 return -error;
457 if (error == 0)
458 return -error;
459
460 assert_return(!bus_error_is_dirty(e), -EINVAL);
461
462 /* First, try a const translation */
463 *e = errno_to_bus_error_const(error);
464
465 if (!sd_bus_error_is_set(e)) {
466 int k;
467
468 /* If that didn't work, try a dynamic one. */
469
470 k = errno_to_bus_error_name_new(error, (char**) &e->name);
471 if (k > 0)
472 e->_need_free = 1;
473 else if (k < 0) {
474 *e = BUS_ERROR_OOM;
475 return -error;
476 } else
477 *e = BUS_ERROR_FAILED;
478 }
479
480 /* Now, fill in the message from strerror() if we can */
481 bus_error_strerror(e, error);
482 return -error;
483 }
484
485 _public_ int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
486 PROTECT_ERRNO;
487
488 if (error < 0)
489 error = -error;
490
491 if (!e)
492 return -error;
493 if (error == 0)
494 return 0;
495
496 assert_return(!bus_error_is_dirty(e), -EINVAL);
497
498 /* First, try a const translation */
499 *e = errno_to_bus_error_const(error);
500
501 if (!sd_bus_error_is_set(e)) {
502 int k;
503
504 /* If that didn't work, try a dynamic one */
505
506 k = errno_to_bus_error_name_new(error, (char**) &e->name);
507 if (k > 0)
508 e->_need_free = 1;
509 else if (k < 0) {
510 *e = BUS_ERROR_OOM;
511 return -ENOMEM;
512 } else
513 *e = BUS_ERROR_FAILED;
514 }
515
516 if (format) {
517 _cleanup_free_ char *m = NULL;
518
519 /* Then, let's try to fill in the supplied message */
520
521 errno = error; /* Make sure that %m resolves to the specified error */
522 if (vasprintf(&m, format, ap) < 0)
523 goto fail;
524
525 if (e->_need_free <= 0) {
526 char *t;
527
528 t = strdup(e->name);
529 if (!t)
530 goto fail;
531
532 e->_need_free = 1;
533 e->name = t;
534 }
535
536 e->message = TAKE_PTR(m);
537 return -error;
538 }
539
540 fail:
541 /* If that didn't work, use strerror() for the message */
542 bus_error_strerror(e, error);
543 return -error;
544 }
545
546 _public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
547 int r;
548
549 if (error < 0)
550 error = -error;
551
552 if (!e)
553 return -error;
554 if (error == 0)
555 return 0;
556
557 assert_return(!bus_error_is_dirty(e), -EINVAL);
558
559 if (format) {
560 va_list ap;
561
562 va_start(ap, format);
563 r = sd_bus_error_set_errnofv(e, error, format, ap);
564 va_end(ap);
565
566 return r;
567 }
568
569 return sd_bus_error_set_errno(e, error);
570 }
571
572 const char *bus_error_message(const sd_bus_error *e, int error) {
573
574 if (e) {
575 /* Sometimes, the D-Bus server is a little bit too verbose with
576 * its error messages, so let's override them here */
577 if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED))
578 return "Access denied";
579
580 if (e->message)
581 return e->message;
582 }
583
584 return strerror_safe(abs(error));
585 }
586
587 static bool map_ok(const sd_bus_error_map *map) {
588 for (; map->code != BUS_ERROR_MAP_END_MARKER; map++)
589 if (!map->name || map->code <=0)
590 return false;
591 return true;
592 }
593
594 _public_ int sd_bus_error_add_map(const sd_bus_error_map *map) {
595 const sd_bus_error_map **maps = NULL;
596 unsigned n = 0;
597
598 assert_return(map, -EINVAL);
599 assert_return(map_ok(map), -EINVAL);
600
601 if (additional_error_maps)
602 for (; additional_error_maps[n] != NULL; n++)
603 if (additional_error_maps[n] == map)
604 return 0;
605
606 maps = reallocarray(additional_error_maps, n + 2, sizeof(struct sd_bus_error_map*));
607 if (!maps)
608 return -ENOMEM;
609
610 maps[n] = map;
611 maps[n+1] = NULL;
612
613 additional_error_maps = maps;
614 return 1;
615 }