1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
30 #include "alloc-util.h"
31 #include "bus-error.h"
32 #include "errno-list.h"
33 #include "string-util.h"
36 BUS_ERROR_MAP_ELF_REGISTER
const sd_bus_error_map bus_standard_errors
[] = {
37 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Failed", EACCES
),
38 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoMemory", ENOMEM
),
39 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ServiceUnknown", EHOSTUNREACH
),
40 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NameHasNoOwner", ENXIO
),
41 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoReply", ETIMEDOUT
),
42 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.IOError", EIO
),
43 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL
),
44 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", EOPNOTSUPP
),
45 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS
),
46 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AccessDenied", EACCES
),
47 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AuthFailed", EACCES
),
48 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InteractiveAuthorizationRequired", EACCES
),
49 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoServer", EHOSTDOWN
),
50 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Timeout", ETIMEDOUT
),
51 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoNetwork", ENONET
),
52 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AddressInUse", EADDRINUSE
),
53 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Disconnected", ECONNRESET
),
54 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidArgs", EINVAL
),
55 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileNotFound", ENOENT
),
56 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileExists", EEXIST
),
57 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownMethod", EBADR
),
58 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownObject", EBADR
),
59 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownInterface", EBADR
),
60 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownProperty", EBADR
),
61 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.PropertyReadOnly", EROFS
),
62 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnixProcessIdUnknown", ESRCH
),
63 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidSignature", EINVAL
),
64 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InconsistentMessage", EBADMSG
),
65 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.TimedOut", ETIMEDOUT
),
66 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleInvalid", EINVAL
),
67 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidFileContent", EINVAL
),
68 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleNotFound", ENOENT
),
69 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown", ESRCH
),
70 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ObjectPathInUse", EBUSY
),
74 /* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section */
75 extern const sd_bus_error_map __start_BUS_ERROR_MAP
[];
76 extern const sd_bus_error_map __stop_BUS_ERROR_MAP
[];
78 /* Additional maps registered with sd_bus_error_add_map() are in this
79 * NULL terminated array */
80 static const sd_bus_error_map
**additional_error_maps
= NULL
;
82 static int bus_error_name_to_errno(const char *name
) {
83 const sd_bus_error_map
**map
, *m
;
90 p
= startswith(name
, "System.Error.");
92 r
= errno_from_name(p
);
99 if (additional_error_maps
)
100 for (map
= additional_error_maps
; *map
; map
++)
101 for (m
= *map
;; m
++) {
102 /* For additional error maps the end marker is actually the end marker */
103 if (m
->code
== BUS_ERROR_MAP_END_MARKER
)
106 if (streq(m
->name
, name
))
110 m
= __start_BUS_ERROR_MAP
;
111 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
112 while (m
< __stop_BUS_ERROR_MAP
) {
113 /* For magic ELF error maps, the end marker might
114 * appear in the middle of things, since multiple maps
115 * might appear in the same section. Hence, let's skip
116 * over it, but realign the pointer to the next 8 byte
117 * boundary, which is the selected alignment for the
119 if (m
->code
== BUS_ERROR_MAP_END_MARKER
) {
124 if (streq(m
->name
, name
))
134 static sd_bus_error
errno_to_bus_error_const(int error
) {
142 return BUS_ERROR_OOM
;
146 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED
, "Access denied");
149 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS
, "Invalid argument");
152 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN
, "No such process");
155 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND
, "File not found");
158 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS
, "File exists");
162 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT
, "Timed out");
165 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR
, "Input/output error");
170 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED
, "Disconnected");
173 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED
, "Not supported");
176 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS
, "Address not available");
179 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED
, "Limits exceeded");
182 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE
, "Address in use");
185 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE
, "Inconsistent message");
188 return SD_BUS_ERROR_NULL
;
191 static int errno_to_bus_error_name_new(int error
, char **ret
) {
198 name
= errno_to_name(error
);
202 n
= strappend("System.Error.", name
);
210 bool bus_error_is_dirty(sd_bus_error
*e
) {
214 return e
->name
|| e
->message
|| e
->_need_free
!= 0;
217 _public_
void sd_bus_error_free(sd_bus_error
*e
) {
221 if (e
->_need_free
> 0) {
222 free((void*) e
->name
);
223 free((void*) e
->message
);
226 e
->name
= e
->message
= NULL
;
230 _public_
int sd_bus_error_set(sd_bus_error
*e
, const char *name
, const char *message
) {
237 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
239 e
->name
= strdup(name
);
246 e
->message
= strdup(message
);
251 return -bus_error_name_to_errno(name
);
254 int bus_error_setfv(sd_bus_error
*e
, const char *name
, const char *format
, va_list ap
) {
260 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
262 e
->name
= strdup(name
);
268 /* If we hit OOM on formatting the pretty message, we ignore
269 * this, since we at least managed to write the error name */
271 (void) vasprintf((char**) &e
->message
, format
, ap
);
276 return -bus_error_name_to_errno(name
);
279 _public_
int sd_bus_error_setf(sd_bus_error
*e
, const char *name
, const char *format
, ...) {
285 va_start(ap
, format
);
286 r
= bus_error_setfv(e
, name
, format
, ap
);
292 return sd_bus_error_set(e
, name
, NULL
);
295 _public_
int sd_bus_error_copy(sd_bus_error
*dest
, const sd_bus_error
*e
) {
297 if (!sd_bus_error_is_set(e
))
302 assert_return(!bus_error_is_dirty(dest
), -EINVAL
);
305 * _need_free < 0 indicates that the error is temporarily const, needs deep copying
306 * _need_free == 0 indicates that the error is perpetually const, needs no deep copying
307 * _need_free > 0 indicates that the error is fully dynamic, needs deep copying
310 if (e
->_need_free
== 0)
313 dest
->name
= strdup(e
->name
);
315 *dest
= BUS_ERROR_OOM
;
320 dest
->message
= strdup(e
->message
);
322 dest
->_need_free
= 1;
326 return -bus_error_name_to_errno(e
->name
);
329 _public_
int sd_bus_error_set_const(sd_bus_error
*e
, const char *name
, const char *message
) {
335 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
337 *e
= SD_BUS_ERROR_MAKE_CONST(name
, message
);
340 return -bus_error_name_to_errno(name
);
343 _public_
int sd_bus_error_is_set(const sd_bus_error
*e
) {
350 _public_
int sd_bus_error_has_name(const sd_bus_error
*e
, const char *name
) {
354 return streq_ptr(e
->name
, name
);
357 _public_
int sd_bus_error_get_errno(const sd_bus_error
* e
) {
364 return bus_error_name_to_errno(e
->name
);
367 static void bus_error_strerror(sd_bus_error
*e
, int error
) {
381 x
= strerror_r(error
, m
, k
);
382 if (errno
== ERANGE
|| strlen(x
) >= k
- 1) {
394 if (e
->_need_free
> 0) {
395 /* Error is already dynamic, let's just update the message */
396 free((char*) e
->message
);
401 /* Error was const so far, let's make it dynamic, if we can */
416 if (e
->_need_free
> 0) {
419 /* Error is dynamic, let's hence make the message also dynamic */
424 free((char*) e
->message
);
427 /* Error is const, hence we can just override */
436 _public_
int sd_bus_error_set_errno(sd_bus_error
*e
, int error
) {
446 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
448 /* First, try a const translation */
449 *e
= errno_to_bus_error_const(error
);
451 if (!sd_bus_error_is_set(e
)) {
454 /* If that didn't work, try a dynamic one. */
456 k
= errno_to_bus_error_name_new(error
, (char**) &e
->name
);
463 *e
= BUS_ERROR_FAILED
;
466 /* Now, fill in the message from strerror() if we can */
467 bus_error_strerror(e
, error
);
471 _public_
int sd_bus_error_set_errnofv(sd_bus_error
*e
, int error
, const char *format
, va_list ap
) {
483 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
485 /* First, try a const translation */
486 *e
= errno_to_bus_error_const(error
);
488 if (!sd_bus_error_is_set(e
)) {
491 /* If that didn't work, try a dynamic one */
493 k
= errno_to_bus_error_name_new(error
, (char**) &e
->name
);
500 *e
= BUS_ERROR_FAILED
;
506 /* Then, let's try to fill in the supplied message */
508 errno
= error
; /* Make sure that %m resolves to the specified error */
509 r
= vasprintf(&m
, format
, ap
);
512 if (e
->_need_free
<= 0) {
525 free((char*) e
->message
);
532 /* If that didn't work, use strerror() for the message */
533 bus_error_strerror(e
, error
);
537 _public_
int sd_bus_error_set_errnof(sd_bus_error
*e
, int error
, const char *format
, ...) {
548 assert_return(!bus_error_is_dirty(e
), -EINVAL
);
553 va_start(ap
, format
);
554 r
= sd_bus_error_set_errnofv(e
, error
, format
, ap
);
560 return sd_bus_error_set_errno(e
, error
);
563 const char *bus_error_message(const sd_bus_error
*e
, int error
) {
566 /* Sometimes, the D-Bus server is a little bit too verbose with
567 * its error messages, so let's override them here */
568 if (sd_bus_error_has_name(e
, SD_BUS_ERROR_ACCESS_DENIED
))
569 return "Access denied";
578 return strerror(error
);
581 static bool map_ok(const sd_bus_error_map
*map
) {
582 for (; map
->code
!= BUS_ERROR_MAP_END_MARKER
; map
++)
583 if (!map
->name
|| map
->code
<=0)
588 _public_
int sd_bus_error_add_map(const sd_bus_error_map
*map
) {
589 const sd_bus_error_map
**maps
= NULL
;
592 assert_return(map
, -EINVAL
);
593 assert_return(map_ok(map
), -EINVAL
);
595 if (additional_error_maps
)
596 for (; additional_error_maps
[n
] != NULL
; n
++)
597 if (additional_error_maps
[n
] == map
)
600 maps
= reallocarray(additional_error_maps
, n
+ 2, sizeof(struct sd_bus_error_map
*));
607 additional_error_maps
= maps
;