chiark / gitweb /
bus: rework sd_bus_error APIs
authorLennart Poettering <lennart@poettering.net>
Fri, 22 Nov 2013 03:01:46 +0000 (04:01 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 22 Nov 2013 03:23:22 +0000 (04:23 +0100)
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
src/core/syscall-list.c
src/libsystemd-bus/bus-convenience.c
src/libsystemd-bus/bus-error.c
src/libsystemd-bus/bus-message.c
src/libsystemd-bus/test-bus-error.c [new file with mode: 0644]
src/shared/.gitignore [new file with mode: 0644]
src/shared/errno-list.c [new file with mode: 0644]
src/shared/errno-list.h [new file with mode: 0644]

index fa215e8e098fc2cffdbfc88077bc47700ac659a8..06d4fcf233951bc12013e592c101d11cd87bd321 100644 (file)
@@ -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
 
index 35216b2a88520c8ca91e04d0644c9d7a2a1a0fb6..dc84dca8fe14607c8e5200865a4d034f47d3149f 100644 (file)
@@ -23,7 +23,6 @@
 #include <string.h>
 
 #include "util.h"
-
 #include "syscall-list.h"
 
 static const struct syscall_name* lookup_syscall(register const char *str,
index 644687471311186e351e06d1f84412829a9c83a2..e765ddb21d47f925ee924cdd0481c3f1866c8eed 100644 (file)
@@ -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);
 }
 
index 7fef681413205c0461b3bb7ae5e6b44dd05217a1..968e80df7dc80221cce62f9544e40b0b7fd13804 100644 (file)
 #include <stdio.h>
 
 #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);
 
index 538a28af9e63590ef0b17fbfd8eca15c5e938c65..132426f6fdc1d4a47ab446857a904be5fbf80fe1 100644 (file)
@@ -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 (file)
index 0000000..9c0f4e0
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#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 (file)
index 0000000..c9b5f81
--- /dev/null
@@ -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 (file)
index 0000000..c63296f
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <string.h>
+
+#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 (file)
index 0000000..ba53329
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+const char *errno_to_name(int id);
+int errno_from_name(const char *name);
+
+int errno_max(void);