diff options
author | Lennart Poettering <lennart@poettering.net> | 2014-12-06 02:39:15 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2014-12-08 14:55:22 +0100 |
commit | 5f86c1f4c43ee9caa120d130e9b89d3fd25124c0 (patch) | |
tree | 34908816bfd429ba578f99626eb2deabc1ed631a /src/libsystemd/sd-bus | |
parent | 8b5e2af10830d55b2032e6c79d0cd1f959bb5b7f (diff) |
sd-bus: rework ELF error mapping table magic
The ELF magic cannot work for consumers of our shard library, since they
are in a different module. Hence make all the ELF magic private, and
instead introduce a public function to register additional static
mapping table.
Diffstat (limited to 'src/libsystemd/sd-bus')
-rw-r--r-- | src/libsystemd/sd-bus/bus-error.c | 155 | ||||
-rw-r--r-- | src/libsystemd/sd-bus/bus-error.h | 37 | ||||
-rw-r--r-- | src/libsystemd/sd-bus/test-bus-error.c | 66 |
3 files changed, 193 insertions, 65 deletions
diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c index 47f90c26f8..157b8d890a 100644 --- a/src/libsystemd/sd-bus/bus-error.c +++ b/src/libsystemd/sd-bus/bus-error.c @@ -32,61 +32,54 @@ #include "sd-bus.h" #include "bus-error.h" -#define BUS_ERROR_OOM SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_MEMORY, "Out of memory") -#define BUS_ERROR_FAILED SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FAILED, "Operation failed") - -SD_BUS_ERROR_MAPPING(sd_bus_standard) = { - {"org.freedesktop.DBus.Error.Failed", EACCES}, - {"org.freedesktop.DBus.Error.NoMemory", ENOMEM}, - {"org.freedesktop.DBus.Error.ServiceUnknown", EHOSTUNREACH}, - {"org.freedesktop.DBus.Error.NameHasNoOwner", ENXIO}, - {"org.freedesktop.DBus.Error.NoReply", ETIMEDOUT}, - {"org.freedesktop.DBus.Error.IOError", EIO}, - {"org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL}, - {"org.freedesktop.DBus.Error.NotSupported", ENOTSUP}, - {"org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS}, - {"org.freedesktop.DBus.Error.AccessDenied", EACCES}, - {"org.freedesktop.DBus.Error.AuthFailed", EACCES}, - {"org.freedesktop.DBus.Error.InteractiveAuthorizationRequired", EACCES}, - {"org.freedesktop.DBus.Error.NoServer", EHOSTDOWN}, - {"org.freedesktop.DBus.Error.Timeout", ETIMEDOUT}, - {"org.freedesktop.DBus.Error.NoNetwork", ENONET}, - {"org.freedesktop.DBus.Error.AddressInUse", EADDRINUSE}, - {"org.freedesktop.DBus.Error.Disconnected", ECONNRESET}, - {"org.freedesktop.DBus.Error.InvalidArgs", EINVAL}, - {"org.freedesktop.DBus.Error.FileNotFound", ENOENT}, - {"org.freedesktop.DBus.Error.FileExists", EEXIST}, - {"org.freedesktop.DBus.Error.UnknownMethod", EBADR}, - {"org.freedesktop.DBus.Error.UnknownObject", EBADR}, - {"org.freedesktop.DBus.Error.UnknownInterface", EBADR}, - {"org.freedesktop.DBus.Error.UnknownProperty", EBADR}, - {"org.freedesktop.DBus.Error.PropertyReadOnly", EROFS}, - {"org.freedesktop.DBus.Error.UnixProcessIdUnknown", ESRCH}, - {"org.freedesktop.DBus.Error.InvalidSignature", EINVAL}, - {"org.freedesktop.DBus.Error.InconsistentMessage", EBADMSG}, - - {"org.freedesktop.DBus.Error.TimedOut", ETIMEDOUT}, - {"org.freedesktop.DBus.Error.MatchRuleInvalid", EINVAL}, - {"org.freedesktop.DBus.Error.InvalidFileContent", EINVAL}, - {"org.freedesktop.DBus.Error.MatchRuleNotFound", ENOENT}, - {"org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown", ESRCH}, - {"org.freedesktop.DBus.Error.ObjectPathInUse", EBUSY}, +BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map standard_errors[] = { + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Failed", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoMemory", ENOMEM), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ServiceUnknown", EHOSTUNREACH), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NameHasNoOwner", ENXIO), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoReply", ETIMEDOUT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.IOError", EIO), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", ENOTSUP), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AccessDenied", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AuthFailed", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InteractiveAuthorizationRequired", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoServer", EHOSTDOWN), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Timeout", ETIMEDOUT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoNetwork", ENONET), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AddressInUse", EADDRINUSE), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Disconnected", ECONNRESET), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidArgs", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileNotFound", ENOENT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileExists", EEXIST), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownMethod", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownObject", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownInterface", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownProperty", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.PropertyReadOnly", EROFS), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnixProcessIdUnknown", ESRCH), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidSignature", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InconsistentMessage", EBADMSG), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.TimedOut", ETIMEDOUT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleInvalid", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidFileContent", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleNotFound", ENOENT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown", ESRCH), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ObjectPathInUse", EBUSY), + SD_BUS_ERROR_MAP_END }; -extern const sd_bus_name_error_mapping __start_sd_bus_errnomap[]; -extern const sd_bus_name_error_mapping __stop_sd_bus_errnomap[]; +/* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section */ +extern const sd_bus_error_map __start_BUS_ERROR_MAP[]; +extern const sd_bus_error_map __stop_BUS_ERROR_MAP[]; -static int bus_error_mapping_lookup(const char *name, size_t len) { - const sd_bus_name_error_mapping *m; - - for (m = __start_sd_bus_errnomap; m < __stop_sd_bus_errnomap; m++) - if (m->name && strneq(m->name, name, len)) - return m->code; - - return EIO; -} +/* Additional maps registered with sd_bus_error_add_map() are in this + * NULL terminated array */ +static const sd_bus_error_map **additional_error_maps = NULL; static int bus_error_name_to_errno(const char *name) { + const sd_bus_error_map **map, *m; const char *p; int r; @@ -102,7 +95,39 @@ static int bus_error_name_to_errno(const char *name) { return r; } - return bus_error_mapping_lookup(name, strlen(name)); + if (additional_error_maps) { + for (map = additional_error_maps; *map; map++) { + for (m = *map;; m++) { + /* For additional error maps the end marker is actually the end marker */ + if (m->code == BUS_ERROR_MAP_END_MARKER) + break; + + if (streq(m->name, name)) + return m->code; + } + } + } + + m = __start_BUS_ERROR_MAP; + while (m < __stop_BUS_ERROR_MAP) { + /* For magic ELF error maps, the end marker might + * appear in the middle of things, since multiple maps + * might appear in the same section. Hence, let's skip + * over it, but realign the pointer to the netx 8byte + * boundary, which is the selected alignment for the + * arrays. */ + if (m->code == BUS_ERROR_MAP_END_MARKER) { + m = ALIGN8_PTR(m+1); + continue; + } + + if (streq(m->name, name)) + return m->code; + + m++; + } + + return EIO; } static sd_bus_error errno_to_bus_error_const(int error) { @@ -552,3 +577,31 @@ const char *bus_error_message(const sd_bus_error *e, int error) { return strerror(error); } + +_public_ int sd_bus_error_add_map(const sd_bus_error_map *map) { + const sd_bus_error_map **maps = NULL; + unsigned n = 0; + + assert_return(map, -EINVAL); + + if (additional_error_maps) { + for (;; n++) { + if (additional_error_maps[n] == NULL) + break; + + if (additional_error_maps[n] == map) + return 0; + } + } + + maps = realloc_multiply(additional_error_maps, sizeof(struct sd_bus_error_map*), n + 2); + if (!maps) + return -ENOMEM; + + + maps[n] = map; + maps[n+1] = NULL; + + additional_error_maps = maps; + return 1; +} diff --git a/src/libsystemd/sd-bus/bus-error.h b/src/libsystemd/sd-bus/bus-error.h index 1469486187..56297156a9 100644 --- a/src/libsystemd/sd-bus/bus-error.h +++ b/src/libsystemd/sd-bus/bus-error.h @@ -26,15 +26,40 @@ #include "sd-bus.h" #include "macro.h" -struct name_error_mapping { - const char* name; - int code; -}; -typedef struct name_error_mapping name_error_mapping; - bool bus_error_is_dirty(sd_bus_error *e); const char *bus_error_message(const sd_bus_error *e, int error); int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) _printf_(3,0); int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) _printf_(3,0); + +#define BUS_ERROR_OOM SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_MEMORY, "Out of memory") +#define BUS_ERROR_FAILED SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FAILED, "Operation failed") + +/* + * There are two ways to register error maps with the error translation + * logic: by using BUS_ERROR_MAP_ELF_REGISTER, which however only + * works when linked into the same ELF module, or via + * sd_bus_error_add_map() which is the official, external API, that + * works from any module. + * + * Note that BUS_ERROR_MAP_ELF_REGISTER has to be used as decorator in + * the bus error table, and BUS_ERROR_MAP_ELF_USE has to be used at + * least once per compilation unit (i.e. per library), to ensure that + * the error map is really added to the final binary. + */ + +#define BUS_ERROR_MAP_ELF_REGISTER \ + __attribute__ ((__section__("BUS_ERROR_MAP"))) \ + __attribute__ ((__used__)) \ + __attribute__ ((aligned(8))) + +#define BUS_ERROR_MAP_ELF_USE(errors) \ + extern const sd_bus_error_map errors[]; \ + __attribute__ ((used)) static const sd_bus_error_map * CONCATENATE(errors ## _copy_, __COUNTER__) = errors; + +/* We use something exotic as end marker, to ensure people build the + * maps using the macsd-ros. */ +#define BUS_ERROR_MAP_END_MARKER -'x' + +BUS_ERROR_MAP_ELF_USE(standard_errors); diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c index ae894e39fd..f72bcdb6b5 100644 --- a/src/libsystemd/sd-bus/test-bus-error.c +++ b/src/libsystemd/sd-bus/test-bus-error.c @@ -22,6 +22,8 @@ #include "sd-bus.h" #include "bus-error.h" #include "bus-util.h" +#include "errno-list.h" +#include "bus-errors.h" static void test_error(void) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL, second = SD_BUS_ERROR_NULL; @@ -111,15 +113,24 @@ static void test_error(void) { assert_se(sd_bus_error_is_set(&error)); } -extern const sd_bus_name_error_mapping __start_sd_bus_errnomap[]; -extern const sd_bus_name_error_mapping __stop_sd_bus_errnomap[]; +extern const sd_bus_error_map __start_BUS_ERROR_MAP[]; +extern const sd_bus_error_map __stop_BUS_ERROR_MAP[]; static void dump_mapping_table(void) { - const sd_bus_name_error_mapping *m; + const sd_bus_error_map *m; printf("----- errno mappings ------\n"); - for (m = __start_sd_bus_errnomap; m < __stop_sd_bus_errnomap; m++) - printf("%s -> %d\n", m->name, m->code); + m = __start_BUS_ERROR_MAP; + while (m < __stop_BUS_ERROR_MAP) { + + if (m->code == BUS_ERROR_MAP_END_MARKER) { + m = ALIGN8_PTR(m+1); + continue; + } + + printf("%s -> %i/%s\n", strna(m->name), m->code, strna(errno_to_name(m->code))); + m ++; + } printf("---------------------------\n"); } @@ -130,15 +141,54 @@ static void test_errno_mapping_standard(void) { assert_se(sd_bus_error_set(NULL, "System.Error.WHATSIT", NULL) == -EIO); } -SD_BUS_ERROR_MAPPING(test) = { - {"org.freedesktop.custom-dbus-error", 5}, - {"org.freedesktop.custom-dbus-error-2", 52}, +BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map test_errors[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error", 5), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-2", 52), + SD_BUS_ERROR_MAP_END +}; + +BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map test_errors2[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-3", 33), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-4", 44), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-33", 333), + SD_BUS_ERROR_MAP_END +}; + +static const sd_bus_error_map test_errors3[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-88", 888), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-99", 999), + SD_BUS_ERROR_MAP_END +}; + +static const sd_bus_error_map test_errors4[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-77", 777), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-78", 778), + SD_BUS_ERROR_MAP_END }; static void test_errno_mapping_custom(void) { assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error", NULL) == -5); assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52); assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-x", NULL) == -EIO); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-33", NULL) == -333); + + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-88", NULL) == -EIO); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-99", NULL) == -EIO); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-77", NULL) == -EIO); + + assert_se(sd_bus_error_add_map(test_errors3) > 0); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-88", NULL) == -888); + assert_se(sd_bus_error_add_map(test_errors4) > 0); + assert_se(sd_bus_error_add_map(test_errors4) == 0); + assert_se(sd_bus_error_add_map(test_errors3) == 0); + + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-99", NULL) == -999); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-77", NULL) == -777); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-78", NULL) == -778); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-y", NULL) == -EIO); + + assert_se(sd_bus_error_set(NULL, BUS_ERROR_NO_SUCH_UNIT, NULL) == -ENOENT); } int main(int argc, char *argv[]) { |