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_BUS_ERROR_MAP
[];
58 extern const sd_bus_error_map __stop_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
= __start_BUS_ERROR_MAP
;
93 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
94 while (m
< __stop_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
101 if (m
->code
== BUS_ERROR_MAP_END_MARKER
) {
106 if (streq(m
->name
, name
))
116 static sd_bus_error
errno_to_bus_error_const(int error
) {
124 return BUS_ERROR_OOM
;
128 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED
, "Access denied");
131 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS
, "Invalid argument");
134 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN
, "No such process");
137 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND
, "File not found");
140 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS
, "File exists");
144 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT
, "Timed out");
147 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR
, "Input/output error");
152 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED
, "Disconnected");
155 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED
, "Not supported");
158 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS
, "Address not available");
161 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED
, "Limits exceeded");
164 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE
, "Address in use");
167 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE
, "Inconsistent message");
170 return SD_BUS_ERROR_NULL
;
173 static int errno_to_bus_error_name_new(int error
, char **ret
) {
180 name
= errno_to_name(error
);
184 n
= strappend("System.Error.", name
);
192 bool bus_error_is_dirty(sd_bus_error
*e
) {
196 return e
->name
|| e
->message
|| e
->_need_free
!= 0;
199 _public_
void sd_bus_error_free(sd_bus_error
*e
) {
203 if (e
->_need_free
> 0) {
204 free((void*) e
->name
);
205 free((void*) e
->message
);
208 *e
= SD_BUS_ERROR_NULL
;
211 _public_
int sd_bus_error_set(sd_bus_error
*e
, const char *name
, const char *message
) {
218 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
220 e
->name
= strdup(name
);
227 e
->message
= strdup(message
);
232 return -bus_error_name_to_errno(name
);
235 int bus_error_setfv(sd_bus_error
*e
, const char *name
, const char *format
, va_list ap
) {
241 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
243 e
->name
= strdup(name
);
249 /* If we hit OOM on formatting the pretty message, we ignore
250 * this, since we at least managed to write the error name */
252 (void) vasprintf((char**) &e
->message
, format
, ap
);
257 return -bus_error_name_to_errno(name
);
260 _public_
int sd_bus_error_setf(sd_bus_error
*e
, const char *name
, const char *format
, ...) {
266 va_start(ap
, format
);
267 r
= bus_error_setfv(e
, name
, format
, ap
);
273 return sd_bus_error_set(e
, name
, NULL
);
276 _public_
int sd_bus_error_copy(sd_bus_error
*dest
, const sd_bus_error
*e
) {
278 if (!sd_bus_error_is_set(e
))
283 assert_return(!bus_error_is_dirty(dest
), -EINVAL
);
286 * _need_free < 0 indicates that the error is temporarily const, needs deep copying
287 * _need_free == 0 indicates that the error is perpetually const, needs no deep copying
288 * _need_free > 0 indicates that the error is fully dynamic, needs deep copying
291 if (e
->_need_free
== 0)
294 dest
->name
= strdup(e
->name
);
296 *dest
= BUS_ERROR_OOM
;
301 dest
->message
= strdup(e
->message
);
303 dest
->_need_free
= 1;
307 return -bus_error_name_to_errno(e
->name
);
310 _public_
int sd_bus_error_move(sd_bus_error
*dest
, sd_bus_error
*e
) {
313 if (!sd_bus_error_is_set(e
)) {
316 *dest
= SD_BUS_ERROR_NULL
;
321 r
= -bus_error_name_to_errno(e
->name
);
325 *e
= SD_BUS_ERROR_NULL
;
327 sd_bus_error_free(e
);
332 _public_
int sd_bus_error_set_const(sd_bus_error
*e
, const char *name
, const char *message
) {
338 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
340 *e
= SD_BUS_ERROR_MAKE_CONST(name
, message
);
343 return -bus_error_name_to_errno(name
);
346 _public_
int sd_bus_error_is_set(const sd_bus_error
*e
) {
353 _public_
int sd_bus_error_has_name(const sd_bus_error
*e
, const char *name
) {
357 return streq_ptr(e
->name
, name
);
360 _public_
int sd_bus_error_get_errno(const sd_bus_error
* e
) {
367 return bus_error_name_to_errno(e
->name
);
370 static void bus_error_strerror(sd_bus_error
*e
, int error
) {
384 x
= strerror_r(error
, m
, k
);
385 if (errno
== ERANGE
|| strlen(x
) >= k
- 1) {
397 if (e
->_need_free
> 0) {
398 /* Error is already dynamic, let's just update the message */
399 free((char*) e
->message
);
404 /* Error was const so far, let's make it dynamic, if we can */
419 if (e
->_need_free
> 0) {
422 /* Error is dynamic, let's hence make the message also dynamic */
427 free((char*) e
->message
);
430 /* Error is const, hence we can just override */
439 _public_
int sd_bus_error_set_errno(sd_bus_error
*e
, int error
) {
449 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
451 /* First, try a const translation */
452 *e
= errno_to_bus_error_const(error
);
454 if (!sd_bus_error_is_set(e
)) {
457 /* If that didn't work, try a dynamic one. */
459 k
= errno_to_bus_error_name_new(error
, (char**) &e
->name
);
466 *e
= BUS_ERROR_FAILED
;
469 /* Now, fill in the message from strerror() if we can */
470 bus_error_strerror(e
, error
);
474 _public_
int sd_bus_error_set_errnofv(sd_bus_error
*e
, int error
, const char *format
, va_list ap
) {
486 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
488 /* First, try a const translation */
489 *e
= errno_to_bus_error_const(error
);
491 if (!sd_bus_error_is_set(e
)) {
494 /* If that didn't work, try a dynamic one */
496 k
= errno_to_bus_error_name_new(error
, (char**) &e
->name
);
503 *e
= BUS_ERROR_FAILED
;
509 /* Then, let's try to fill in the supplied message */
511 errno
= error
; /* Make sure that %m resolves to the specified error */
512 r
= vasprintf(&m
, format
, ap
);
515 if (e
->_need_free
<= 0) {
528 free((char*) e
->message
);
535 /* If that didn't work, use strerror() for the message */
536 bus_error_strerror(e
, error
);
540 _public_
int sd_bus_error_set_errnof(sd_bus_error
*e
, int error
, const char *format
, ...) {
551 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
556 va_start(ap
, format
);
557 r
= sd_bus_error_set_errnofv(e
, error
, format
, ap
);
563 return sd_bus_error_set_errno(e
, error
);
566 const char *bus_error_message(const sd_bus_error
*e
, int error
) {
569 /* Sometimes, the D-Bus server is a little bit too verbose with
570 * its error messages, so let's override them here */
571 if (sd_bus_error_has_name(e
, SD_BUS_ERROR_ACCESS_DENIED
))
572 return "Access denied";
581 return strerror(error
);
584 static bool map_ok(const sd_bus_error_map
*map
) {
585 for (; map
->code
!= BUS_ERROR_MAP_END_MARKER
; map
++)
586 if (!map
->name
|| map
->code
<=0)
591 _public_
int sd_bus_error_add_map(const sd_bus_error_map
*map
) {
592 const sd_bus_error_map
**maps
= NULL
;
595 assert_return(map
, -EINVAL
);
596 assert_return(map_ok(map
), -EINVAL
);
598 if (additional_error_maps
)
599 for (; additional_error_maps
[n
] != NULL
; n
++)
600 if (additional_error_maps
[n
] == map
)
603 maps
= reallocarray(additional_error_maps
, n
+ 2, sizeof(struct sd_bus_error_map
*));
610 additional_error_maps
= maps
;