From 780896a4f1ec7e36c8f72c866ba9693d790f9741 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Nov 2013 04:01:46 +0100 Subject: [PATCH] bus: rework sd_bus_error APIs All calls that set a sd_bus_error structure will now return the same error converted to a negative errno. This may be used as syntactic sugar to return from a function and setting a bus_error structure in one go. Also, translate all Linux Exyz (EIO, EINVAL, EUCLEAN, EPIPE, ...) automatically into counterparts in the (new) "Posix.Error." namespace. If we fail to allocate memory for the components of a sd_bus_error automatically reset it to an OOM error which we always can write. --- Makefile.am | 45 ++- src/core/syscall-list.c | 1 - src/libsystemd-bus/bus-convenience.c | 6 +- src/libsystemd-bus/bus-error.c | 562 ++++++++++++++++----------- src/libsystemd-bus/bus-message.c | 12 +- src/libsystemd-bus/test-bus-error.c | 84 ++++ src/shared/.gitignore | 4 + src/shared/errno-list.c | 59 +++ src/shared/errno-list.h | 27 ++ 9 files changed, 548 insertions(+), 252 deletions(-) create mode 100644 src/libsystemd-bus/test-bus-error.c create mode 100644 src/shared/.gitignore create mode 100644 src/shared/errno-list.c create mode 100644 src/shared/errno-list.h diff --git a/Makefile.am b/Makefile.am index fa215e8e0..06d4fcf23 100644 --- a/Makefile.am +++ b/Makefile.am @@ -177,6 +177,7 @@ AM_CPPFLAGS = \ -DQUOTACHECK=\"$(QUOTACHECK)\" \ -DKEXEC=\"$(KEXEC)\" \ -I $(top_srcdir)/src \ + -I $(top_builddir)/src/shared \ -I $(top_srcdir)/src/shared \ -I $(top_srcdir)/src/network \ -I $(top_srcdir)/src/login \ @@ -761,7 +762,13 @@ libsystemd_shared_la_SOURCES = \ src/shared/ptyfwd.c \ src/shared/ptyfwd.h \ src/shared/net-util.c \ - src/shared/net-util.h + src/shared/net-util.h \ + src/shared/errno-list.c \ + src/shared/errno-list.h + +nodist_libsystemd_shared_la_SOURCES = \ + src/shared/errno-from-name.h \ + src/shared/errno-to-name.h # ------------------------------------------------------------------------------ noinst_LTLIBRARIES += \ @@ -1031,11 +1038,15 @@ CLEANFILES += \ src/core/load-fragment-gperf.c \ src/core/load-fragment-gperf-nulstr.c \ src/core/syscall-list.txt \ - src/core/syscall-from-name.gperf + src/core/syscall-from-name.gperf \ + src/shared/errno-list.txt \ + src/shared/errno-from-name.gperf BUILT_SOURCES += \ src/core/syscall-from-name.h \ - src/core/syscall-to-name.h + src/core/syscall-to-name.h \ + src/shared/errno-from-name.h \ + src/shared/errno-to-name.h src/core/syscall-list.txt: Makefile $(AM_V_at)$(MKDIR_P) $(dir $@) @@ -1051,7 +1062,23 @@ src/core/syscall-from-name.h: src/core/syscall-from-name.gperf Makefile src/core/syscall-to-name.h: src/core/syscall-list.txt Makefile $(AM_V_at)$(MKDIR_P) $(dir $@) - $(AM_V_GEN)$(AWK) 'BEGIN{ print "const char* const syscall_names[] = { "} { printf "[SYSCALL_TO_INDEX(__NR_%s)] = \"%s\",\n", $$1, $$1 } END{print "};"}' < $< > $@ + $(AM_V_GEN)$(AWK) 'BEGIN{ print "static const char* const syscall_names[] = { "} { printf "[SYSCALL_TO_INDEX(__NR_%s)] = \"%s\",\n", $$1, $$1 } END{print "};"}' < $< > $@ + +src/shared/errno-list.txt: Makefile + $(AM_V_at)$(MKDIR_P) $(dir $@) + $(AM_V_GEN)$(CPP) $(CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) -dM -include errno.h - < /dev/null | $(AWK) '/^#define[ \t]+E[^ _]+[ \t]+[0-9]/ { print $$2; }' > $@ + +src/shared/errno-from-name.gperf: src/shared/errno-list.txt Makefile + $(AM_V_at)$(MKDIR_P) $(dir $@) + $(AM_V_GEN)$(AWK) 'BEGIN{ print "struct errno_name { const char* name; int id; };"; print "%null-strings"; print "%%";} { printf "%s, %s\n", $$1, $$1 }' < $< > $@ + +src/shared/errno-from-name.h: src/shared/errno-from-name.gperf Makefile + $(AM_V_at)$(MKDIR_P) $(dir $@) + $(AM_V_GPERF)$(GPERF) -L ANSI-C -t --ignore-case -N lookup_errno -H hash_errno_name -p -C < $< > $@ + +src/shared/errno-to-name.h: src/shared/errno-list.txt Makefile + $(AM_V_at)$(MKDIR_P) $(dir $@) + $(AM_V_GEN)$(AWK) 'BEGIN{ print "static const char* const errno_names[] = { "} { printf "[%s] = \"%s\",\n", $$1, $$1 } END{print "};"}' < $< > $@ # ------------------------------------------------------------------------------ systemd_SOURCES = \ @@ -2004,6 +2031,7 @@ tests += \ test-bus-zero-copy \ test-bus-introspect \ test-bus-objects \ + test-bus-error \ test-event bin_PROGRAMS += \ @@ -2079,6 +2107,15 @@ test_bus_objects_LDADD = \ libsystemd-capability.la \ $(CAP_LIBS) +test_bus_error_SOURCES = \ + src/libsystemd-bus/test-bus-error.c + +test_bus_error_LDADD = \ + libsystemd-bus-internal.la \ + libsystemd-id128-internal.la \ + libsystemd-daemon-internal.la \ + libsystemd-shared.la + test_bus_match_SOURCES = \ src/libsystemd-bus/test-bus-match.c diff --git a/src/core/syscall-list.c b/src/core/syscall-list.c index 35216b2a8..dc84dca8f 100644 --- a/src/core/syscall-list.c +++ b/src/core/syscall-list.c @@ -23,7 +23,6 @@ #include #include "util.h" - #include "syscall-list.h" static const struct syscall_name* lookup_syscall(register const char *str, diff --git a/src/libsystemd-bus/bus-convenience.c b/src/libsystemd-bus/bus-convenience.c index 644687471..e765ddb21 100644 --- a/src/libsystemd-bus/bus-convenience.c +++ b/src/libsystemd-bus/bus-convenience.c @@ -155,7 +155,6 @@ _public_ int sd_bus_reply_method_errorf( _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; va_list ap; - int r; assert_return(call, -EINVAL); assert_return(call->sealed, -EPERM); @@ -167,12 +166,9 @@ _public_ int sd_bus_reply_method_errorf( return 0; va_start(ap, format); - r = bus_error_setfv(&error, name, format, ap); + bus_error_setfv(&error, name, format, ap); va_end(ap); - if (r < 0) - return r; - return sd_bus_reply_method_error(call, &error); } diff --git a/src/libsystemd-bus/bus-error.c b/src/libsystemd-bus/bus-error.c index 7fef68141..968e80df7 100644 --- a/src/libsystemd-bus/bus-error.c +++ b/src/libsystemd-bus/bus-error.c @@ -27,13 +27,188 @@ #include #include "util.h" +#include "errno-list.h" #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") + +static int bus_error_name_to_errno(const char *name) { + const char *p; + int r; + + if (!name) + return EINVAL; + + p = startswith(name, "Posix.Error."); + if (p) { + r = errno_from_name(p); + if (r <= 0) + return EIO; + + return r; + } + + /* Better replace this with a gperf table */ + + if (streq(name, SD_BUS_ERROR_NO_MEMORY)) + return ENOMEM; + + if (streq(name, SD_BUS_ERROR_SERVICE_UNKNOWN)) + return EHOSTUNREACH; + + if (streq(name, SD_BUS_ERROR_NAME_HAS_NO_OWNER)) + return ENXIO; + + if (streq(name, SD_BUS_ERROR_NO_REPLY) || + streq(name, SD_BUS_ERROR_TIMEOUT) || + streq(name, "org.freedesktop.DBus.Error.TimedOut")) + return ETIMEDOUT; + + if (streq(name, SD_BUS_ERROR_IO_ERROR)) + return EIO; + + if (streq(name, SD_BUS_ERROR_BAD_ADDRESS)) + return EADDRNOTAVAIL; + + if (streq(name, SD_BUS_ERROR_NOT_SUPPORTED)) + return ENOTSUP; + + if (streq(name, SD_BUS_ERROR_LIMITS_EXCEEDED)) + return ENOBUFS; + + if (streq(name, SD_BUS_ERROR_ACCESS_DENIED) || + streq(name, SD_BUS_ERROR_AUTH_FAILED)) + return EACCES; + + if (streq(name, SD_BUS_ERROR_NO_SERVER)) + return EHOSTDOWN; + + if (streq(name, SD_BUS_ERROR_NO_NETWORK)) + return ENONET; + + if (streq(name, SD_BUS_ERROR_ADDRESS_IN_USE)) + return EADDRINUSE; + + if (streq(name, SD_BUS_ERROR_DISCONNECTED)) + return ECONNRESET; + + if (streq(name, SD_BUS_ERROR_INVALID_ARGS) || + streq(name, SD_BUS_ERROR_INVALID_SIGNATURE) || + streq(name, "org.freedesktop.DBus.Error.MatchRuleInvalid") || + streq(name, "org.freedesktop.DBus.Error.InvalidFileContent")) + return EINVAL; + + if (streq(name, SD_BUS_ERROR_FILE_NOT_FOUND) || + streq(name, "org.freedesktop.DBus.Error.MatchRuleNotFound")) + return ENOENT; + + if (streq(name, SD_BUS_ERROR_FILE_EXISTS)) + return EEXIST; + + if (streq(name, SD_BUS_ERROR_UNKNOWN_METHOD) || + streq(name, SD_BUS_ERROR_UNKNOWN_OBJECT) || + streq(name, SD_BUS_ERROR_UNKNOWN_INTERFACE) || + streq(name, SD_BUS_ERROR_UNKNOWN_PROPERTY)) + return EBADR; + + if (streq(name, SD_BUS_ERROR_PROPERTY_READ_ONLY)) + return EROFS; + + if (streq(name, SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN) || + streq(name, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown")) + return ESRCH; + + if (streq(name, SD_BUS_ERROR_INCONSISTENT_MESSAGE)) + return EBADMSG; + + if (streq(name, "org.freedesktop.DBus.Error.ObjectPathInUse")) + return EBUSY; + + return EIO; +} + +static sd_bus_error errno_to_bus_error_const(int error) { + + if (error < 0) + error = -error; + + switch (error) { + + case ENOMEM: + return BUS_ERROR_OOM; + + case EPERM: + case EACCES: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED, "Access denied"); + + case EINVAL: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument"); + + case ESRCH: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process"); + + case ENOENT: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found"); + + case EEXIST: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "File exists"); + + case ETIMEDOUT: + case ETIME: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT, "Timed out"); + + case EIO: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR, "Input/output error"); + + case ENETRESET: + case ECONNABORTED: + case ECONNRESET: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected"); + + case ENOTSUP: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported"); + + case EADDRNOTAVAIL: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS, "Address not available"); + + case ENOBUFS: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded"); + + case EADDRINUSE: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use"); + + case EBADMSG: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message"); + } + + return SD_BUS_ERROR_NULL; +} + +static int errno_to_bus_error_name_new(int error, char **ret) { + const char *name; + char *n; + + if (error < 0) + error = -error; + + name = errno_to_name(error); + if (!name) + return 0; + + n = strappend("Posix.Error.", name); + if (!n) + return -ENOMEM; + + *ret = n; + return 1; +} + bool bus_error_is_dirty(sd_bus_error *e) { if (!e) - return 0; + return false; return e->name || e->message || e->need_free; } @@ -52,58 +227,51 @@ _public_ void sd_bus_error_free(sd_bus_error *e) { } _public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) { - char *n, *m = NULL; - if (!e) + if (!name) return 0; + if (!e) + goto finish; assert_return(!bus_error_is_dirty(e), -EINVAL); - assert_return(name, -EINVAL); - n = strdup(name); - if (!n) + e->name = strdup(name); + if (!e->name) { + *e = BUS_ERROR_OOM; return -ENOMEM; - - if (message) { - m = strdup(message); - if (!m) - return -ENOMEM; } - e->name = n; - e->message = m; + if (message) + e->message = strdup(message); + e->need_free = true; - return sd_bus_error_get_errno(e); +finish: + return -bus_error_name_to_errno(name); } int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) { - char *n, *m = NULL; - int r; - if (!e) + if (!name) return 0; + if (!e) + goto finish; assert_return(!bus_error_is_dirty(e), -EINVAL); - assert_return(name, -EINVAL); - n = strdup(name); - if (!n) + e->name = strdup(name); + if (!e->name) { + *e = BUS_ERROR_OOM; return -ENOMEM; - - if (format) { - r = vasprintf(&m, format, ap); - if (r < 0) { - free(n); - return -ENOMEM; - } } - e->name = n; - e->message = m; + if (format) + vasprintf((char**) &e->message, format, ap); + e->need_free = true; - return sd_bus_error_get_errno(e); +finish: + return -bus_error_name_to_errno(name); } _public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) { @@ -123,42 +291,45 @@ _public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *fo } _public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) { - char *x, *y = NULL; - if (!dest) - return 0; if (!sd_bus_error_is_set(e)) return 0; + if (!dest) + goto finish; assert_return(!bus_error_is_dirty(dest), -EINVAL); - x = strdup(e->name); - if (!x) - return -ENOMEM; - - if (e->message) { - y = strdup(e->message); - if (!y) { - free(x); + if (!e->need_free) + *dest = *e; + else { + dest->name = strdup(e->name); + if (!dest->name) { + *dest = BUS_ERROR_OOM; return -ENOMEM; } + + if (e->message) + dest->message = strdup(e->message); + + dest->need_free = true; } - dest->name = x; - dest->message = y; - dest->need_free = true; - return sd_bus_error_get_errno(e); +finish: + return -bus_error_name_to_errno(e->name); } _public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) { - if (!e) + if (!name) return 0; + if (!e) + goto finish; assert_return(!bus_error_is_dirty(e), -EINVAL); - assert_return(name, -EINVAL); *e = SD_BUS_ERROR_MAKE_CONST(name, message); - return sd_bus_error_get_errno(e); + +finish: + return -bus_error_name_to_errno(name); } _public_ int sd_bus_error_is_set(const sd_bus_error *e) { @@ -176,111 +347,24 @@ _public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) { } _public_ int sd_bus_error_get_errno(const sd_bus_error* e) { - - /* Better replce this with a gperf table */ - if (!e) - return EIO; - - if (!e->name) - return EIO; - - if (streq(e->name, SD_BUS_ERROR_NO_MEMORY)) - return ENOMEM; - - if (streq(e->name, SD_BUS_ERROR_SERVICE_UNKNOWN)) - return EHOSTUNREACH; - - if (streq(e->name, SD_BUS_ERROR_NAME_HAS_NO_OWNER)) - return ENXIO; - - if (streq(e->name, SD_BUS_ERROR_NO_REPLY) || - streq(e->name, SD_BUS_ERROR_TIMEOUT) || - streq(e->name, "org.freedesktop.DBus.Error.TimedOut")) - return ETIMEDOUT; - - if (streq(e->name, SD_BUS_ERROR_IO_ERROR)) - return EIO; - - if (streq(e->name, SD_BUS_ERROR_BAD_ADDRESS)) - return EADDRNOTAVAIL; - - if (streq(e->name, SD_BUS_ERROR_NOT_SUPPORTED)) - return ENOTSUP; - - if (streq(e->name, SD_BUS_ERROR_LIMITS_EXCEEDED)) - return ENOBUFS; - - if (streq(e->name, SD_BUS_ERROR_ACCESS_DENIED) || - streq(e->name, SD_BUS_ERROR_AUTH_FAILED)) - return EACCES; - - if (streq(e->name, SD_BUS_ERROR_NO_SERVER)) - return EHOSTDOWN; - - if (streq(e->name, SD_BUS_ERROR_NO_NETWORK)) - return ENONET; - - if (streq(e->name, SD_BUS_ERROR_ADDRESS_IN_USE)) - return EADDRINUSE; - - if (streq(e->name, SD_BUS_ERROR_DISCONNECTED)) - return ECONNRESET; - - if (streq(e->name, SD_BUS_ERROR_INVALID_ARGS) || - streq(e->name, SD_BUS_ERROR_INVALID_SIGNATURE) || - streq(e->name, "org.freedesktop.DBus.Error.MatchRuleInvalid") || - streq(e->name, "org.freedesktop.DBus.Error.InvalidFileContent")) - return EINVAL; - - if (streq(e->name, SD_BUS_ERROR_FILE_NOT_FOUND) || - streq(e->name, "org.freedesktop.DBus.Error.MatchRuleNotFound")) - return ENOENT; - - if (streq(e->name, SD_BUS_ERROR_FILE_EXISTS)) - return EEXIST; - - if (streq(e->name, SD_BUS_ERROR_UNKNOWN_METHOD) || - streq(e->name, SD_BUS_ERROR_UNKNOWN_OBJECT) || - streq(e->name, SD_BUS_ERROR_UNKNOWN_INTERFACE) || - streq(e->name, SD_BUS_ERROR_UNKNOWN_PROPERTY)) - return EBADR; - - if (streq(e->name, SD_BUS_ERROR_PROPERTY_READ_ONLY)) - return EROFS; - - if (streq(e->name, SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN) || - streq(e->name, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown")) - return ESRCH; - - if (streq(e->name, SD_BUS_ERROR_INCONSISTENT_MESSAGE)) - return EBADMSG; - - if (streq(e->name, "org.freedesktop.DBus.Error.ObjectPathInUse")) - return EBUSY; + return 0; - return EIO; + return bus_error_name_to_errno(e->name); } -static int bus_error_set_strerror_or_const(sd_bus_error *e, const char *name, int error, const char *fallback) { +static void bus_error_strerror(sd_bus_error *e, int error) { size_t k = 64; - char *n = NULL, *m = NULL; - - if (error < 0) - error = -error; + char *m; - if (!e) - return -error; - - assert_return(!bus_error_is_dirty(e), -EINVAL); - assert_return(name, -EINVAL); + assert(e); for (;;) { char *x; m = new(char, k); if (!m) - goto use_fallback; + return; errno = 0; x = strerror_r(error, m, k); @@ -292,104 +376,88 @@ static int bus_error_set_strerror_or_const(sd_bus_error *e, const char *name, in if (!x || errno) { free(m); - goto use_fallback; + return; } - - if (x != m) { + if (x == m) { + if (e->need_free) { + /* Error is already dynamic, let's just update the message */ + free((char*) e->message); + e->message = x; + + } else { + char *t; + /* Error was const so far, let's make it dynamic, if we can */ + + t = strdup(e->name); + if (!t) { + free(m); + return; + } + + e->need_free = true; + e->name = t; + e->message = x; + } + } else { free(m); - sd_bus_error_set_const(e, name, x); - return -error; - } - - break; - } - - n = strdup(name); - if (!n) { - free(m); - goto use_fallback; - } + if (e->need_free) { + char *t; - e->name = n; - e->message = m; - e->need_free = true; + /* Error is dynamic, let's hence make the message also dynamic */ + t = strdup(x); + if (!t) + return; - return -error; + free((char*) e->message); + e->message = t; + } else { + /* Error is const, hence we can just override */ + e->message = x; + } + } -use_fallback: - sd_bus_error_set_const(e, name, fallback); - return -error; + return; + } } -static sd_bus_error map_from_errno(int error) { +_public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) { if (error < 0) error = -error; - switch (error) { - - case ENOMEM: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_NETWORK, "Out of memory"); - - case EPERM: - case EACCES: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED, "Access denied"); - - case EINVAL: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument"); - - case ESRCH: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process"); - - case ENOENT: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found"); - - case EEXIST: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "File exists"); - - case ETIMEDOUT: - case ETIME: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT, "Timed out"); - - case EIO: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR, "Input/output error"); - - case ENETRESET: - case ECONNABORTED: - case ECONNRESET: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected"); + if (!e) + return -error; + if (error == 0) + return -error; - case ENOTSUP: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported"); + assert_return(!bus_error_is_dirty(e), -EINVAL); - case EADDRNOTAVAIL: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS, "Address not available"); + /* First, try a const translation */ + *e = errno_to_bus_error_const(error); - case ENOBUFS: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded"); + if (!sd_bus_error_is_set(e)) { + int k; - case EADDRINUSE: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use"); + /* If that didn't work, try a dynamic one. */ - case EBADMSG: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message"); + k = errno_to_bus_error_name_new(error, (char**) &e->name); + if (k > 0) + e->need_free = true; + else if (k < 0) { + *e = BUS_ERROR_OOM; + return -error; + } else + *e = BUS_ERROR_FAILED; } - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FAILED, "Operation failed"); -} - -_public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) { - sd_bus_error x; - - x = map_from_errno(error); - - return bus_error_set_strerror_or_const(e, x.name, error, x.message); + /* Now, fill in the message from strerror() if we can */ + bus_error_strerror(e, error); + return -error; } int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) { - sd_bus_error x; int r; if (error < 0) @@ -397,32 +465,60 @@ int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_lis if (!e) return -error; + if (error == 0) + return 0; assert_return(!bus_error_is_dirty(e), -EINVAL); - x = map_from_errno(error); + /* First, try a const translation */ + *e = errno_to_bus_error_const(error); + + if (!sd_bus_error_is_set(e)) { + int k; + + /* If that didn't work, try a dynamic one */ + + k = errno_to_bus_error_name_new(error, (char**) &e->name); + if (k > 0) + e->need_free = true; + else if (k < 0) { + *e = BUS_ERROR_OOM; + return -ENOMEM; + } else + *e = BUS_ERROR_FAILED; + } if (format) { - char *n, *m; + char *m; - r = vasprintf(&m, format, ap); - if (r < 0) - goto fallback; + /* First, let's try to fill in the supplied message */ - n = strdup(x.name); - if (!n) { - free(m); - goto fallback; + r = vasprintf(&m, format, ap); + if (r >= 0) { + + if (!e->need_free) { + char *t; + + t = strdup(e->name); + if (t) { + e->need_free = true; + e->name = t; + e->message = m; + return -error; + } + + free(m); + } else { + free((char*) e->message); + e->message = m; + return -error; + } } - - e->name = n; - e->message = m; - e->need_free = true; - return -error; } -fallback: - return bus_error_set_strerror_or_const(e, x.name, error, x.message); + /* If that didn't work, use strerror() for the message */ + bus_error_strerror(e, error); + return -error; } _public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) { @@ -433,6 +529,8 @@ _public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *for if (!e) return -error; + if (error == 0) + return 0; assert_return(!bus_error_is_dirty(e), -EINVAL); diff --git a/src/libsystemd-bus/bus-message.c b/src/libsystemd-bus/bus-message.c index 538a28af9..132426f6f 100644 --- a/src/libsystemd-bus/bus-message.c +++ b/src/libsystemd-bus/bus-message.c @@ -624,18 +624,14 @@ _public_ int sd_bus_message_new_method_errorf( _cleanup_free_ sd_bus_error error = SD_BUS_ERROR_NULL; va_list ap; - int r; assert_return(name, -EINVAL); assert_return(m, -EINVAL); va_start(ap, format); - r = bus_error_setfv(&error, name, format, ap); + bus_error_setfv(&error, name, format, ap); va_end(ap); - if (r < 0) - return r; - return sd_bus_message_new_method_error(call, &error, m); } @@ -664,15 +660,11 @@ _public_ int sd_bus_message_new_method_errnof( _cleanup_free_ sd_bus_error berror = SD_BUS_ERROR_NULL; va_list ap; - int r; va_start(ap, format); - r = bus_error_set_errnofv(&berror, error, format, ap); + bus_error_set_errnofv(&berror, error, format, ap); va_end(ap); - if (r < 0) - return r; - return sd_bus_message_new_method_error(call, &berror, m); } diff --git a/src/libsystemd-bus/test-bus-error.c b/src/libsystemd-bus/test-bus-error.c new file mode 100644 index 000000000..9c0f4e015 --- /dev/null +++ b/src/libsystemd-bus/test-bus-error.c @@ -0,0 +1,84 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "sd-bus.h" +#include "bus-error.h" +#include "bus-util.h" + +int main(int argc, char *argv[]) { + + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL, second = SD_BUS_ERROR_NULL; + + assert_se(!sd_bus_error_is_set(&error)); + assert_se(sd_bus_error_set(&error, SD_BUS_ERROR_NOT_SUPPORTED, "xxx") == -ENOTSUP); + assert_se(streq(error.name, SD_BUS_ERROR_NOT_SUPPORTED)); + assert_se(streq(error.message, "xxx")); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_NOT_SUPPORTED)); + assert_se(sd_bus_error_get_errno(&error) == ENOTSUP); + assert_se(sd_bus_error_is_set(&error)); + sd_bus_error_free(&error); + + assert_se(!sd_bus_error_is_set(&error)); + assert_se(sd_bus_error_setf(&error, SD_BUS_ERROR_FILE_NOT_FOUND, "yyy %i", -1) == -ENOENT); + assert_se(streq(error.name, SD_BUS_ERROR_FILE_NOT_FOUND)); + assert_se(streq(error.message, "yyy -1")); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND)); + assert_se(sd_bus_error_get_errno(&error) == ENOENT); + assert_se(sd_bus_error_is_set(&error)); + + assert_se(!sd_bus_error_is_set(&second)); + assert_se(sd_bus_error_copy(&second, &error) == -ENOENT); + assert_se(streq(error.name, second.name)); + assert_se(streq(error.message, second.message)); + assert_se(sd_bus_error_get_errno(&second) == ENOENT); + assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_FILE_NOT_FOUND)); + assert_se(sd_bus_error_is_set(&second)); + + sd_bus_error_free(&error); + + assert_se(!sd_bus_error_is_set(&error)); + assert_se(sd_bus_error_set_const(&error, "Posix.Error.EUCLEAN", "Hallo") == -EUCLEAN); + assert_se(streq(error.name, "Posix.Error.EUCLEAN")); + assert_se(streq(error.message, "Hallo")); + assert_se(sd_bus_error_has_name(&error, "Posix.Error.EUCLEAN")); + assert_se(sd_bus_error_get_errno(&error) == EUCLEAN); + assert_se(sd_bus_error_is_set(&error)); + sd_bus_error_free(&error); + + assert_se(!sd_bus_error_is_set(&error)); + assert_se(sd_bus_error_set_errno(&error, EBUSY) == -EBUSY); + assert_se(streq(error.name, "Posix.Error.EBUSY")); + assert_se(streq(error.message, strerror(EBUSY))); + assert_se(sd_bus_error_has_name(&error, "Posix.Error.EBUSY")); + assert_se(sd_bus_error_get_errno(&error) == EBUSY); + assert_se(sd_bus_error_is_set(&error)); + sd_bus_error_free(&error); + + assert_se(!sd_bus_error_is_set(&error)); + assert_se(sd_bus_error_set_errnof(&error, EIO, "Waldi %c", 'X') == -EIO); + assert_se(streq(error.name, SD_BUS_ERROR_IO_ERROR)); + assert_se(streq(error.message, "Waldi X")); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_IO_ERROR)); + assert_se(sd_bus_error_get_errno(&error) == EIO); + assert_se(sd_bus_error_is_set(&error)); + + return 0; +} diff --git a/src/shared/.gitignore b/src/shared/.gitignore new file mode 100644 index 000000000..c9b5f8171 --- /dev/null +++ b/src/shared/.gitignore @@ -0,0 +1,4 @@ +/errno-from-name.gperf +/errno-from-name.h +/errno-list.txt +/errno-to-name.h diff --git a/src/shared/errno-list.c b/src/shared/errno-list.c new file mode 100644 index 000000000..c63296f29 --- /dev/null +++ b/src/shared/errno-list.c @@ -0,0 +1,59 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "util.h" +#include "errno-list.h" + +static const struct errno_name* lookup_errno(register const char *str, + register unsigned int len); + +#include "errno-to-name.h" +#include "errno-from-name.h" + +const char *errno_to_name(int id) { + + if (id < 0) + id = -id; + + if (id >= (int) ELEMENTSOF(errno_names)) + return NULL; + + return errno_names[id]; +} + +int errno_from_name(const char *name) { + const struct errno_name *sc; + + assert(name); + + sc = lookup_errno(name, strlen(name)); + if (!sc) + return 0; + + return sc->id; +} + +int errno_max(void) { + return ELEMENTSOF(errno_names); +} diff --git a/src/shared/errno-list.h b/src/shared/errno-list.h new file mode 100644 index 000000000..ba533294e --- /dev/null +++ b/src/shared/errno-list.h @@ -0,0 +1,27 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +const char *errno_to_name(int id); +int errno_from_name(const char *name); + +int errno_max(void); -- 2.30.2