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