1 /* SPDX-License-Identifier: LGPL-2.1+ */
12 #include "alloc-util.h"
13 #include "bus-error.h"
14 #include "errno-list.h"
15 #include "string-util.h"
18 BUS_ERROR_MAP_ELF_REGISTER
const sd_bus_error_map bus_standard_errors
[] = {
19 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Failed", EACCES
),
20 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoMemory", ENOMEM
),
21 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ServiceUnknown", EHOSTUNREACH
),
22 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NameHasNoOwner", ENXIO
),
23 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoReply", ETIMEDOUT
),
24 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.IOError", EIO
),
25 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL
),
26 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", EOPNOTSUPP
),
27 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS
),
28 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AccessDenied", EACCES
),
29 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AuthFailed", EACCES
),
30 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InteractiveAuthorizationRequired", EACCES
),
31 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoServer", EHOSTDOWN
),
32 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Timeout", ETIMEDOUT
),
33 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoNetwork", ENONET
),
34 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AddressInUse", EADDRINUSE
),
35 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Disconnected", ECONNRESET
),
36 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidArgs", EINVAL
),
37 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileNotFound", ENOENT
),
38 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileExists", EEXIST
),
39 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownMethod", EBADR
),
40 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownObject", EBADR
),
41 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownInterface", EBADR
),
42 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownProperty", EBADR
),
43 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.PropertyReadOnly", EROFS
),
44 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnixProcessIdUnknown", ESRCH
),
45 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidSignature", EINVAL
),
46 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InconsistentMessage", EBADMSG
),
47 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.TimedOut", ETIMEDOUT
),
48 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleInvalid", EINVAL
),
49 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidFileContent", EINVAL
),
50 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleNotFound", ENOENT
),
51 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown", ESRCH
),
52 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ObjectPathInUse", EBUSY
),
56 /* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section */
57 extern const sd_bus_error_map __start_SYSTEMD_BUS_ERROR_MAP
[];
58 extern const sd_bus_error_map __stop_SYSTEMD_BUS_ERROR_MAP
[];
60 /* Additional maps registered with sd_bus_error_add_map() are in this
61 * NULL terminated array */
62 static const sd_bus_error_map
**additional_error_maps
= NULL
;
64 static int bus_error_name_to_errno(const char *name
) {
65 const sd_bus_error_map
**map
, *m
;
72 p
= startswith(name
, "System.Error.");
74 r
= errno_from_name(p
);
81 if (additional_error_maps
)
82 for (map
= additional_error_maps
; *map
; map
++)
83 for (m
= *map
;; m
++) {
84 /* For additional error maps the end marker is actually the end marker */
85 if (m
->code
== BUS_ERROR_MAP_END_MARKER
)
88 if (streq(m
->name
, name
))
92 m
= ALIGN_TO_PTR(__start_SYSTEMD_BUS_ERROR_MAP
, sizeof(void*));
93 while (m
< __stop_SYSTEMD_BUS_ERROR_MAP
) {
94 /* For magic ELF error maps, the end marker might
95 * appear in the middle of things, since multiple maps
96 * might appear in the same section. Hence, let's skip
97 * over it, but realign the pointer to the next 8 byte
98 * boundary, which is the selected alignment for the
100 if (m
->code
== BUS_ERROR_MAP_END_MARKER
) {
101 m
= ALIGN_TO_PTR(m
+ 1, sizeof(void*));
105 if (streq(m
->name
, name
))
114 static sd_bus_error
errno_to_bus_error_const(int error
) {
122 return BUS_ERROR_OOM
;
126 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED
, "Access denied");
129 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS
, "Invalid argument");
132 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN
, "No such process");
135 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND
, "File not found");
138 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS
, "File exists");
142 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT
, "Timed out");
145 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR
, "Input/output error");
150 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED
, "Disconnected");
153 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED
, "Not supported");
156 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS
, "Address not available");
159 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED
, "Limits exceeded");
162 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE
, "Address in use");
165 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE
, "Inconsistent message");
168 return SD_BUS_ERROR_NULL
;
171 static int errno_to_bus_error_name_new(int error
, char **ret
) {
178 name
= errno_to_name(error
);
182 n
= strappend("System.Error.", name
);
190 bool bus_error_is_dirty(sd_bus_error
*e
) {
194 return e
->name
|| e
->message
|| e
->_need_free
!= 0;
197 _public_
void sd_bus_error_free(sd_bus_error
*e
) {
201 if (e
->_need_free
> 0) {
202 free((void*) e
->name
);
203 free((void*) e
->message
);
206 *e
= SD_BUS_ERROR_NULL
;
209 _public_
int sd_bus_error_set(sd_bus_error
*e
, const char *name
, const char *message
) {
216 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
218 e
->name
= strdup(name
);
225 e
->message
= strdup(message
);
230 return -bus_error_name_to_errno(name
);
233 int bus_error_setfv(sd_bus_error
*e
, const char *name
, const char *format
, va_list ap
) {
239 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
241 e
->name
= strdup(name
);
247 /* If we hit OOM on formatting the pretty message, we ignore
248 * this, since we at least managed to write the error name */
250 (void) vasprintf((char**) &e
->message
, format
, ap
);
255 return -bus_error_name_to_errno(name
);
258 _public_
int sd_bus_error_setf(sd_bus_error
*e
, const char *name
, const char *format
, ...) {
264 va_start(ap
, format
);
265 r
= bus_error_setfv(e
, name
, format
, ap
);
271 return sd_bus_error_set(e
, name
, NULL
);
274 _public_
int sd_bus_error_copy(sd_bus_error
*dest
, const sd_bus_error
*e
) {
276 if (!sd_bus_error_is_set(e
))
281 assert_return(!bus_error_is_dirty(dest
), -EINVAL
);
284 * _need_free < 0 indicates that the error is temporarily const, needs deep copying
285 * _need_free == 0 indicates that the error is perpetually const, needs no deep copying
286 * _need_free > 0 indicates that the error is fully dynamic, needs deep copying
289 if (e
->_need_free
== 0)
292 dest
->name
= strdup(e
->name
);
294 *dest
= BUS_ERROR_OOM
;
299 dest
->message
= strdup(e
->message
);
301 dest
->_need_free
= 1;
305 return -bus_error_name_to_errno(e
->name
);
308 _public_
int sd_bus_error_move(sd_bus_error
*dest
, sd_bus_error
*e
) {
311 if (!sd_bus_error_is_set(e
)) {
314 *dest
= SD_BUS_ERROR_NULL
;
319 r
= -bus_error_name_to_errno(e
->name
);
323 *e
= SD_BUS_ERROR_NULL
;
325 sd_bus_error_free(e
);
330 _public_
int sd_bus_error_set_const(sd_bus_error
*e
, const char *name
, const char *message
) {
336 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
338 *e
= SD_BUS_ERROR_MAKE_CONST(name
, message
);
341 return -bus_error_name_to_errno(name
);
344 _public_
int sd_bus_error_is_set(const sd_bus_error
*e
) {
351 _public_
int sd_bus_error_has_name(const sd_bus_error
*e
, const char *name
) {
355 return streq_ptr(e
->name
, name
);
358 _public_
int sd_bus_error_get_errno(const sd_bus_error
* e
) {
365 return bus_error_name_to_errno(e
->name
);
368 static void bus_error_strerror(sd_bus_error
*e
, int error
) {
382 x
= strerror_r(error
, m
, k
);
383 if (errno
== ERANGE
|| strlen(x
) >= k
- 1) {
395 if (e
->_need_free
> 0) {
396 /* Error is already dynamic, let's just update the message */
397 free((char*) e
->message
);
402 /* Error was const so far, let's make it dynamic, if we can */
417 if (e
->_need_free
> 0) {
420 /* Error is dynamic, let's hence make the message also dynamic */
425 free((char*) e
->message
);
428 /* Error is const, hence we can just override */
437 _public_
int sd_bus_error_set_errno(sd_bus_error
*e
, int error
) {
447 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
449 /* First, try a const translation */
450 *e
= errno_to_bus_error_const(error
);
452 if (!sd_bus_error_is_set(e
)) {
455 /* If that didn't work, try a dynamic one. */
457 k
= errno_to_bus_error_name_new(error
, (char**) &e
->name
);
464 *e
= BUS_ERROR_FAILED
;
467 /* Now, fill in the message from strerror() if we can */
468 bus_error_strerror(e
, error
);
472 _public_
int sd_bus_error_set_errnofv(sd_bus_error
*e
, int error
, const char *format
, va_list ap
) {
484 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
486 /* First, try a const translation */
487 *e
= errno_to_bus_error_const(error
);
489 if (!sd_bus_error_is_set(e
)) {
492 /* If that didn't work, try a dynamic one */
494 k
= errno_to_bus_error_name_new(error
, (char**) &e
->name
);
501 *e
= BUS_ERROR_FAILED
;
507 /* Then, let's try to fill in the supplied message */
509 errno
= error
; /* Make sure that %m resolves to the specified error */
510 r
= vasprintf(&m
, format
, ap
);
513 if (e
->_need_free
<= 0) {
526 free((char*) e
->message
);
533 /* If that didn't work, use strerror() for the message */
534 bus_error_strerror(e
, error
);
538 _public_
int sd_bus_error_set_errnof(sd_bus_error
*e
, int error
, const char *format
, ...) {
549 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
554 va_start(ap
, format
);
555 r
= sd_bus_error_set_errnofv(e
, error
, format
, ap
);
561 return sd_bus_error_set_errno(e
, error
);
564 const char *bus_error_message(const sd_bus_error
*e
, int error
) {
567 /* Sometimes, the D-Bus server is a little bit too verbose with
568 * its error messages, so let's override them here */
569 if (sd_bus_error_has_name(e
, SD_BUS_ERROR_ACCESS_DENIED
))
570 return "Access denied";
579 return strerror(error
);
582 static bool map_ok(const sd_bus_error_map
*map
) {
583 for (; map
->code
!= BUS_ERROR_MAP_END_MARKER
; map
++)
584 if (!map
->name
|| map
->code
<=0)
589 _public_
int sd_bus_error_add_map(const sd_bus_error_map
*map
) {
590 const sd_bus_error_map
**maps
= NULL
;
593 assert_return(map
, -EINVAL
);
594 assert_return(map_ok(map
), -EINVAL
);
596 if (additional_error_maps
)
597 for (; additional_error_maps
[n
] != NULL
; n
++)
598 if (additional_error_maps
[n
] == map
)
601 maps
= reallocarray(additional_error_maps
, n
+ 2, sizeof(struct sd_bus_error_map
*));
608 additional_error_maps
= maps
;