2 This file is part of systemd.
4 Copyright 2013 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include "alloc-util.h"
30 #include "bus-error.h"
31 #include "errno-list.h"
32 #include "string-util.h"
35 BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = {
36 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Failed", EACCES),
37 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoMemory", ENOMEM),
38 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ServiceUnknown", EHOSTUNREACH),
39 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NameHasNoOwner", ENXIO),
40 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoReply", ETIMEDOUT),
41 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.IOError", EIO),
42 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL),
43 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", EOPNOTSUPP),
44 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS),
45 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AccessDenied", EACCES),
46 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AuthFailed", EACCES),
47 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InteractiveAuthorizationRequired", EACCES),
48 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoServer", EHOSTDOWN),
49 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Timeout", ETIMEDOUT),
50 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoNetwork", ENONET),
51 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AddressInUse", EADDRINUSE),
52 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Disconnected", ECONNRESET),
53 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidArgs", EINVAL),
54 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileNotFound", ENOENT),
55 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileExists", EEXIST),
56 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownMethod", EBADR),
57 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownObject", EBADR),
58 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownInterface", EBADR),
59 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownProperty", EBADR),
60 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.PropertyReadOnly", EROFS),
61 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnixProcessIdUnknown", ESRCH),
62 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidSignature", EINVAL),
63 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InconsistentMessage", EBADMSG),
64 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.TimedOut", ETIMEDOUT),
65 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleInvalid", EINVAL),
66 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidFileContent", EINVAL),
67 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleNotFound", ENOENT),
68 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown", ESRCH),
69 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ObjectPathInUse", EBUSY),
73 /* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section.
74 * Hide them; for currently unknown reasons they get exported to the shared libries
75 * even without being listed in the sym file. */
76 extern const sd_bus_error_map __start_BUS_ERROR_MAP[] _hidden_;
77 extern const sd_bus_error_map __stop_BUS_ERROR_MAP[] _hidden_;
79 /* Additional maps registered with sd_bus_error_add_map() are in this
80 * NULL terminated array */
81 static const sd_bus_error_map **additional_error_maps = NULL;
83 static int bus_error_name_to_errno(const char *name) {
84 const sd_bus_error_map **map, *m;
91 p = startswith(name, "System.Error.");
93 r = errno_from_name(p);
100 if (additional_error_maps)
101 for (map = additional_error_maps; *map; map++)
102 for (m = *map;; m++) {
103 /* For additional error maps the end marker is actually the end marker */
104 if (m->code == BUS_ERROR_MAP_END_MARKER)
107 if (streq(m->name, name))
111 m = __start_BUS_ERROR_MAP;
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))
133 static sd_bus_error errno_to_bus_error_const(int error) {
141 return BUS_ERROR_OOM;
145 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED, "Access denied");
148 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument");
151 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process");
154 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found");
157 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "File exists");
161 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT, "Timed out");
164 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR, "Input/output error");
169 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected");
172 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported");
175 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS, "Address not available");
178 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded");
181 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use");
184 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message");
187 return SD_BUS_ERROR_NULL;
190 static int errno_to_bus_error_name_new(int error, char **ret) {
197 name = errno_to_name(error);
201 n = strappend("System.Error.", name);
209 bool bus_error_is_dirty(sd_bus_error *e) {
213 return e->name || e->message || e->_need_free != 0;
216 _public_ void sd_bus_error_free(sd_bus_error *e) {
220 if (e->_need_free > 0) {
221 free((void*) e->name);
222 free((void*) e->message);
225 e->name = e->message = NULL;
229 _public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
236 assert_return(!bus_error_is_dirty(e), -EINVAL);
238 e->name = strdup(name);
245 e->message = strdup(message);
250 return -bus_error_name_to_errno(name);
253 int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
259 assert_return(!bus_error_is_dirty(e), -EINVAL);
261 e->name = strdup(name);
267 /* If we hit OOM on formatting the pretty message, we ignore
268 * this, since we at least managed to write the error name */
270 (void) vasprintf((char**) &e->message, format, ap);
275 return -bus_error_name_to_errno(name);
278 _public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
284 va_start(ap, format);
285 r = bus_error_setfv(e, name, format, ap);
291 return sd_bus_error_set(e, name, NULL);
294 _public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
296 if (!sd_bus_error_is_set(e))
301 assert_return(!bus_error_is_dirty(dest), -EINVAL);
304 * _need_free < 0 indicates that the error is temporarily const, needs deep copying
305 * _need_free == 0 indicates that the error is perpetually const, needs no deep copying
306 * _need_free > 0 indicates that the error is fully dynamic, needs deep copying
309 if (e->_need_free == 0)
312 dest->name = strdup(e->name);
314 *dest = BUS_ERROR_OOM;
319 dest->message = strdup(e->message);
321 dest->_need_free = 1;
325 return -bus_error_name_to_errno(e->name);
328 _public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
334 assert_return(!bus_error_is_dirty(e), -EINVAL);
336 *e = SD_BUS_ERROR_MAKE_CONST(name, message);
339 return -bus_error_name_to_errno(name);
342 _public_ int sd_bus_error_is_set(const sd_bus_error *e) {
349 _public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
353 return streq_ptr(e->name, name);
356 _public_ int sd_bus_error_get_errno(const sd_bus_error* e) {
363 return bus_error_name_to_errno(e->name);
366 static void bus_error_strerror(sd_bus_error *e, int error) {
380 x = strerror_r(error, m, k);
381 if (errno == ERANGE || strlen(x) >= k - 1) {
393 if (e->_need_free > 0) {
394 /* Error is already dynamic, let's just update the message */
395 free((char*) e->message);
400 /* Error was const so far, let's make it dynamic, if we can */
415 if (e->_need_free > 0) {
418 /* Error is dynamic, let's hence make the message also dynamic */
423 free((char*) e->message);
426 /* Error is const, hence we can just override */
435 _public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) {
445 assert_return(!bus_error_is_dirty(e), -EINVAL);
447 /* First, try a const translation */
448 *e = errno_to_bus_error_const(error);
450 if (!sd_bus_error_is_set(e)) {
453 /* If that didn't work, try a dynamic one. */
455 k = errno_to_bus_error_name_new(error, (char**) &e->name);
462 *e = BUS_ERROR_FAILED;
465 /* Now, fill in the message from strerror() if we can */
466 bus_error_strerror(e, error);
470 _public_ int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
482 assert_return(!bus_error_is_dirty(e), -EINVAL);
484 /* First, try a const translation */
485 *e = errno_to_bus_error_const(error);
487 if (!sd_bus_error_is_set(e)) {
490 /* If that didn't work, try a dynamic one */
492 k = errno_to_bus_error_name_new(error, (char**) &e->name);
499 *e = BUS_ERROR_FAILED;
505 /* Then, let's try to fill in the supplied message */
507 errno = error; /* Make sure that %m resolves to the specified error */
508 r = vasprintf(&m, format, ap);
511 if (e->_need_free <= 0) {
524 free((char*) e->message);
531 /* If that didn't work, use strerror() for the message */
532 bus_error_strerror(e, error);
536 _public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
547 assert_return(!bus_error_is_dirty(e), -EINVAL);
552 va_start(ap, format);
553 r = sd_bus_error_set_errnofv(e, error, format, ap);
559 return sd_bus_error_set_errno(e, error);
562 const char *bus_error_message(const sd_bus_error *e, int error) {
565 /* Sometimes, the D-Bus server is a little bit too verbose with
566 * its error messages, so let's override them here */
567 if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED))
568 return "Access denied";
577 return strerror(error);
580 static bool map_ok(const sd_bus_error_map *map) {
581 for (; map->code != BUS_ERROR_MAP_END_MARKER; map++)
582 if (!map->name || map->code <=0)
587 _public_ int sd_bus_error_add_map(const sd_bus_error_map *map) {
588 const sd_bus_error_map **maps = NULL;
591 assert_return(map, -EINVAL);
592 assert_return(map_ok(map), -EINVAL);
594 if (additional_error_maps)
595 for (; additional_error_maps[n] != NULL; n++)
596 if (additional_error_maps[n] == map)
599 maps = realloc_multiply(additional_error_maps, sizeof(struct sd_bus_error_map*), n + 2);
606 additional_error_maps = maps;