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