[systemd-commits] 4 commits - src/libsystemd-bus src/shared

Lennart Poettering lennart at kemper.freedesktop.org
Sun Mar 24 19:06:54 PDT 2013


 src/libsystemd-bus/bus-internal.h     |   15 
 src/libsystemd-bus/bus-message.c      |  196 ++++++-
 src/libsystemd-bus/bus-message.h      |   14 
 src/libsystemd-bus/sd-bus.c           |  920 ++++++++++++++++++++++++----------
 src/libsystemd-bus/sd-bus.h           |   14 
 src/libsystemd-bus/test-bus-chat.c    |   71 ++
 src/libsystemd-bus/test-bus-marshal.c |    2 
 src/shared/strv.c                     |    4 
 src/shared/strv.h                     |    6 
 src/shared/util.c                     |    2 
 10 files changed, 935 insertions(+), 309 deletions(-)

New commits:
commit 2fd9ae2e9bd585312e15f8383036caee704822fb
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Mar 25 02:30:32 2013 +0100

    bus: implement 'unixexec:' protocol

diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h
index 1996508..b7ed42c 100644
--- a/src/libsystemd-bus/bus-internal.h
+++ b/src/libsystemd-bus/bus-internal.h
@@ -120,6 +120,9 @@ struct sd_bus {
 
         int *fds;
         unsigned n_fds;
+
+        char *exec_path;
+        char **exec_argv;
 };
 
 static inline void bus_unrefp(sd_bus **b) {
@@ -145,6 +148,8 @@ static inline void bus_unrefp(sd_bus **b) {
 
 #define BUS_FDS_MAX 1024
 
+#define BUS_EXEC_ARGV_MAX 256
+
 bool object_path_is_valid(const char *p);
 bool interface_name_is_valid(const char *p);
 bool service_name_is_valid(const char *p);
diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c
index df40102..3bdbd11 100644
--- a/src/libsystemd-bus/sd-bus.c
+++ b/src/libsystemd-bus/sd-bus.c
@@ -30,6 +30,7 @@
 #include "util.h"
 #include "macro.h"
 #include "missing.h"
+#include "strv.h"
 
 #include "sd-bus.h"
 #include "bus-internal.h"
@@ -53,6 +54,9 @@ static void bus_free(sd_bus *b) {
         free(b->auth_uid);
         free(b->address);
 
+        free(b->exec_path);
+        strv_free(b->exec_argv);
+
         close_many(b->fds, b->n_fds);
         free(b->fds);
 
@@ -141,6 +145,37 @@ int sd_bus_set_fd(sd_bus *bus, int fd) {
         return 0;
 }
 
+int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) {
+        char *p, **a;
+
+        if (!bus)
+                return -EINVAL;
+        if (bus->state != BUS_UNSET)
+                return -EPERM;
+        if (!path)
+                return -EINVAL;
+        if (strv_isempty(argv))
+                return -EINVAL;
+
+        p = strdup(path);
+        if (!p)
+                return -ENOMEM;
+
+        a = strv_copy(argv);
+        if (!a) {
+                free(p);
+                return -ENOMEM;
+        }
+
+        free(bus->exec_path);
+        strv_free(bus->exec_argv);
+
+        bus->exec_path = p;
+        bus->exec_argv = a;
+
+        return 0;
+}
+
 int sd_bus_set_hello(sd_bus *bus, int b) {
         if (!bus)
                 return -EINVAL;
@@ -234,21 +269,24 @@ static int parse_address_key(const char **p, const char *key, char **value) {
 
         assert(p);
         assert(*p);
-        assert(key);
         assert(value);
 
-        l = strlen(key);
-        if (strncmp(*p, key, l) != 0)
-                return 0;
+        if (key) {
+                l = strlen(key);
+                if (strncmp(*p, key, l) != 0)
+                        return 0;
 
-        if ((*p)[l] != '=')
-                return 0;
+                if ((*p)[l] != '=')
+                        return 0;
 
-        if (*value)
-                return -EINVAL;
+                if (*value)
+                        return -EINVAL;
 
-        a = *p + l + 1;
-        while (*a != ',' && *a != 0) {
+                a = *p + l + 1;
+        } else
+                a = *p;
+
+        while (*a != ';' && *a != ',' && *a != 0) {
                 char c, *t;
 
                 if (*a == '%') {
@@ -294,7 +332,10 @@ static int parse_address_key(const char **p, const char *key, char **value) {
                 a++;
 
         *p = a;
+
+        free(*value);
         *value = r;
+
         return 1;
 }
 
@@ -308,138 +349,293 @@ static void skip_address_key(const char **p) {
                 (*p) ++;
 }
 
-static int bus_parse_next_address(sd_bus *b) {
-        const char *a, *p;
-        _cleanup_free_ char *guid = NULL;
+static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
+        _cleanup_free_ char *path = NULL, *abstract = NULL;
+        size_t l;
         int r;
 
         assert(b);
+        assert(p);
+        assert(*p);
+        assert(guid);
 
-        if (!b->address)
-                return 0;
-        if (b->address[b->address_index] == 0)
-                return 0;
+        while (**p != 0 && **p != ';') {
+                r = parse_address_key(p, "guid", guid);
+                if (r < 0)
+                        return r;
+                else if (r > 0)
+                        continue;
 
-        a = b->address + b->address_index;
+                r = parse_address_key(p, "path", &path);
+                if (r < 0)
+                        return r;
+                else if (r > 0)
+                        continue;
 
-        zero(b->sockaddr);
-        b->sockaddr_size = 0;
-        b->peer = SD_ID128_NULL;
+                r = parse_address_key(p, "abstract", &abstract);
+                if (r < 0)
+                        return r;
+                else if (r > 0)
+                        continue;
 
-        if (startswith(a, "unix:")) {
-                _cleanup_free_ char *path = NULL, *abstract = NULL;
+                skip_address_key(p);
+        }
 
-                p = a + 5;
-                while (*p != 0) {
-                        r = parse_address_key(&p, "guid", &guid);
-                        if (r < 0)
-                                return r;
-                        else if (r > 0)
-                                continue;
+        if (!path && !abstract)
+                return -EINVAL;
 
-                        r = parse_address_key(&p, "path", &path);
-                        if (r < 0)
-                                return r;
-                        else if (r > 0)
-                                continue;
+        if (path && abstract)
+                return -EINVAL;
+
+        if (path) {
+                l = strlen(path);
+                if (l > sizeof(b->sockaddr.un.sun_path))
+                        return -E2BIG;
+
+                b->sockaddr.un.sun_family = AF_UNIX;
+                strncpy(b->sockaddr.un.sun_path, path, sizeof(b->sockaddr.un.sun_path));
+                b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l;
+        } else if (abstract) {
+                l = strlen(abstract);
+                if (l > sizeof(b->sockaddr.un.sun_path) - 1)
+                        return -E2BIG;
+
+                b->sockaddr.un.sun_family = AF_UNIX;
+                b->sockaddr.un.sun_path[0] = 0;
+                strncpy(b->sockaddr.un.sun_path+1, abstract, sizeof(b->sockaddr.un.sun_path)-1);
+                b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
+        }
+
+        return 0;
+}
+
+static int parse_tcp_address(sd_bus *b, const char **p, char **guid) {
+        _cleanup_free_ char *host = NULL, *port = NULL, *family = NULL;
+        struct addrinfo hints, *result;
+        int r;
+
+        assert(b);
+        assert(p);
+        assert(*p);
+        assert(guid);
+
+        while (**p != 0 && **p != ';') {
+                r = parse_address_key(p, "guid", guid);
+                if (r < 0)
+                        return r;
+                else if (r > 0)
+                        continue;
+
+                r = parse_address_key(p, "host", &host);
+                if (r < 0)
+                        return r;
+                else if (r > 0)
+                        continue;
+
+                r = parse_address_key(p, "port", &port);
+                if (r < 0)
+                        return r;
+                else if (r > 0)
+                        continue;
+
+                r = parse_address_key(p, "family", &family);
+                if (r < 0)
+                        return r;
+                else if (r > 0)
+                        continue;
+
+                skip_address_key(p);
+        }
+
+        if (!host || !port)
+                return -EINVAL;
+
+        zero(hints);
+        hints.ai_socktype = SOCK_STREAM;
+        hints.ai_flags = AI_ADDRCONFIG;
+
+        if (family) {
+                if (streq(family, "ipv4"))
+                        hints.ai_family = AF_INET;
+                else if (streq(family, "ipv6"))
+                        hints.ai_family = AF_INET6;
+                else
+                        return -EINVAL;
+        }
+
+        r = getaddrinfo(host, port, &hints, &result);
+        if (r == EAI_SYSTEM)
+                return -errno;
+        else if (r != 0)
+                return -EADDRNOTAVAIL;
+
+        memcpy(&b->sockaddr, result->ai_addr, result->ai_addrlen);
+        b->sockaddr_size = result->ai_addrlen;
+
+        freeaddrinfo(result);
+
+        return 0;
+}
+
+static int parse_exec_address(sd_bus *b, const char **p, char **guid) {
+        char *path = NULL;
+        unsigned n_argv = 0, j;
+        char **argv = NULL;
+        int r;
+
+        assert(b);
+        assert(p);
+        assert(*p);
+        assert(guid);
+
+        while (**p != 0 && **p != ';') {
+                r = parse_address_key(p, "guid", guid);
+                if (r < 0)
+                        goto fail;
+                else if (r > 0)
+                        continue;
+
+                r = parse_address_key(p, "path", &path);
+                if (r < 0)
+                        goto fail;
+                else if (r > 0)
+                        continue;
+
+                if (startswith(*p, "argv")) {
+                        unsigned ul;
+
+                        errno = 0;
+                        ul = strtoul(*p + 4, (char**) p, 10);
+                        if (errno != 0 || **p != '=' || ul > 256) {
+                                r = -EINVAL;
+                                goto fail;
+                        }
+
+                        (*p) ++;
 
-                        r = parse_address_key(&p, "abstract", &abstract);
+                        if (ul >= n_argv) {
+                                char **x;
+
+                                x = realloc(argv, sizeof(char*) * (ul + 2));
+                                if (!x) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
+
+                                memset(x + n_argv, 0, sizeof(char*) * (ul - n_argv + 2));
+
+                                argv = x;
+                                n_argv = ul + 1;
+                        }
+
+                        r = parse_address_key(p, NULL, argv + ul);
                         if (r < 0)
-                                return r;
-                        else if (r > 0)
-                                continue;
+                                goto fail;
 
-                        skip_address_key(&p);
+                        continue;
                 }
 
-                if (!path && !abstract)
-                        return -EINVAL;
+                skip_address_key(p);
+        }
 
-                if (path && abstract)
-                        return -EINVAL;
+        if (!path)
+                goto fail;
 
-                if (path) {
-                        size_t l;
+        /* Make sure there are no holes in the array, with the
+         * exception of argv[0] */
+        for (j = 1; j < n_argv; j++)
+                if (!argv[j]) {
+                        r = -EINVAL;
+                        goto fail;
+                }
 
-                        l = strlen(path);
-                        if (l > sizeof(b->sockaddr.un.sun_path))
-                                return -E2BIG;
+        if (argv && argv[0] == NULL) {
+                argv[0] = strdup(path);
+                if (!argv[0]) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+        }
 
-                        b->sockaddr.un.sun_family = AF_UNIX;
-                        strncpy(b->sockaddr.un.sun_path, path, sizeof(b->sockaddr.un.sun_path));
-                        b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l;
-                } else if (abstract) {
-                        size_t l;
+        b->exec_path = path;
+        b->exec_argv = argv;
+        return 0;
+
+fail:
+        for (j = 0; j < n_argv; j++)
+                free(argv[j]);
+
+        free(argv);
+        free(path);
+        return r;
+}
+
+static void bus_reset_parsed_address(sd_bus *b) {
+        assert(b);
+
+        zero(b->sockaddr);
+        b->sockaddr_size = 0;
+        strv_free(b->exec_argv);
+        free(b->exec_path);
+        b->exec_path = NULL;
+        b->exec_argv = NULL;
+        b->peer = SD_ID128_NULL;
+}
+
+static int bus_parse_next_address(sd_bus *b) {
+        _cleanup_free_ char *guid = NULL;
+        const char *a;
+        int r;
+
+        assert(b);
+
+        if (!b->address)
+                return 0;
+        if (b->address[b->address_index] == 0)
+                return 0;
 
-                        l = strlen(abstract);
-                        if (l > sizeof(b->sockaddr.un.sun_path) - 1)
-                                return -E2BIG;
+        bus_reset_parsed_address(b);
+
+        a = b->address + b->address_index;
 
-                        b->sockaddr.un.sun_family = AF_UNIX;
-                        b->sockaddr.un.sun_path[0] = 0;
-                        strncpy(b->sockaddr.un.sun_path+1, abstract, sizeof(b->sockaddr.un.sun_path)-1);
-                        b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
+        while (*a != 0) {
+
+                if (*a == ';') {
+                        a++;
+                        continue;
                 }
 
-        } else if (startswith(a, "tcp:")) {
-                _cleanup_free_ char *host = NULL, *port = NULL, *family = NULL;
-                struct addrinfo hints, *result;
+                if (startswith(a, "unix:")) {
+                        a += 5;
 
-                p = a + 4;
-                while (*p != 0) {
-                        r = parse_address_key(&p, "guid", &guid);
+                        r = parse_unix_address(b, &a, &guid);
                         if (r < 0)
                                 return r;
-                        else if (r > 0)
-                                continue;
+                        break;
 
-                        r = parse_address_key(&p, "host", &host);
-                        if (r < 0)
-                                return r;
-                        else if (r > 0)
-                                continue;
+                } else if (startswith(a, "tcp:")) {
 
-                        r = parse_address_key(&p, "port", &port);
+                        a += 4;
+                        r = parse_tcp_address(b, &a, &guid);
                         if (r < 0)
                                 return r;
-                        else if (r > 0)
-                                continue;
 
-                        r = parse_address_key(&p, "family", &family);
+                        break;
+
+                } else if (startswith(a, "unixexec:")) {
+
+                        a += 9;
+                        r = parse_exec_address(b, &a, &guid);
                         if (r < 0)
                                 return r;
-                        else if (r > 0)
-                                continue;
-
-                        skip_address_key(&p);
-                }
 
-                if (!host || !port)
-                        return -EINVAL;
+                        break;
 
-                zero(hints);
-                hints.ai_socktype = SOCK_STREAM;
-                hints.ai_flags = AI_ADDRCONFIG;
-
-                if (family) {
-                        if (streq(family, "ipv4"))
-                                hints.ai_family = AF_INET;
-                        else if (streq(family, "ipv6"))
-                                hints.ai_family = AF_INET6;
-                        else
-                                return -EINVAL;
                 }
 
-                r = getaddrinfo(host, port, &hints, &result);
-                if (r == EAI_SYSTEM)
-                        return -errno;
-                else if (r != 0)
-                        return -EADDRNOTAVAIL;
-
-                memcpy(&b->sockaddr, result->ai_addr, result->ai_addrlen);
-                b->sockaddr_size = result->ai_addrlen;
-
-                freeaddrinfo(result);
+                a = strchr(a, ';');
+                if (!a)
+                        return 0;
         }
 
         if (guid) {
@@ -448,7 +644,7 @@ static int bus_parse_next_address(sd_bus *b) {
                         return r;
         }
 
-        b->address_index = p - b->address;
+        b->address_index = a - b->address;
         return 1;
 }
 
@@ -643,11 +839,21 @@ static int bus_start_auth(sd_bus *b) {
         char text[20 + 1]; /* enough space for a 64bit integer plus NUL */
         size_t l;
         const char *auth_suffix;
+        int domain = 0, r;
+        socklen_t sl;
 
         assert(b);
 
         b->state = BUS_AUTHENTICATING;
 
+        sl = sizeof(domain);
+        r = getsockopt(b->fd, SOL_SOCKET, SO_DOMAIN, &domain, &sl);
+        if (r < 0)
+                return -errno;
+
+        if (domain != AF_UNIX)
+                b->negotiate_fds = false;
+
         snprintf(text, sizeof(text), "%llu", (unsigned long long) geteuid());
         char_array_0(text);
 
@@ -669,51 +875,114 @@ static int bus_start_auth(sd_bus *b) {
         return bus_write_auth(b);
 }
 
-static int bus_start_connect(sd_bus *b) {
+static int bus_connect(sd_bus *b) {
         int r;
 
         assert(b);
         assert(b->fd < 0);
+        assert(b->sockaddr.sa.sa_family != AF_UNSPEC);
 
-        for (;;) {
-                if (b->sockaddr.sa.sa_family == AF_UNSPEC) {
-                        r = bus_parse_next_address(b);
-                        if (r < 0)
-                                return r;
-                        if (r == 0)
-                                return b->last_connect_error ? -b->last_connect_error : -ECONNREFUSED;
-                }
+        b->fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (b->fd < 0)
+                return -errno;
 
-                b->fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
-                if (b->fd < 0) {
-                        b->last_connect_error = errno;
-                        goto try_again;
-                }
+        r = bus_setup_fd(b);
+        if (r < 0)
+                return r;
 
-                r = bus_setup_fd(b);
-                if (r < 0) {
-                        b->last_connect_error = errno;
-                        goto try_again;
-                }
+        r = connect(b->fd, &b->sockaddr.sa, b->sockaddr_size);
+        if (r < 0) {
+                if (errno == EINPROGRESS)
+                        return 1;
 
-                r = connect(b->fd, &b->sockaddr.sa, b->sockaddr_size);
-                if (r < 0) {
-                        if (errno == EINPROGRESS)
-                                return 1;
+                return -errno;
+        }
+
+        return bus_start_auth(b);
+}
+
+static int bus_exec(sd_bus *b) {
+        int s[2];
+        pid_t pid;
+
+        assert(b);
+        assert(b->fd < 0);
+        assert(b->exec_path);
 
-                        b->last_connect_error = errno;
-                        goto try_again;
+        b->fd = socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, s);
+        if (b->fd < 0)
+                return -errno;
+
+        pid = fork();
+        if (pid < 0) {
+                close_pipe(s);
+                return -errno;
+        }
+        if (pid == 0) {
+                /* Child */
+
+                close_all_fds(s, 2);
+                close_nointr_nofail(s[0]);
+
+                assert_se(dup3(s[1], STDIN_FILENO, 0) == STDIN_FILENO);
+                assert_se(dup3(s[1], STDOUT_FILENO, 0) == STDOUT_FILENO);
+
+                if (s[1] != STDIN_FILENO && s[1] != STDOUT_FILENO)
+                        close_nointr_nofail(s[1]);
+
+                fd_cloexec(STDIN_FILENO, false);
+                fd_cloexec(STDOUT_FILENO, false);
+                fd_nonblock(STDIN_FILENO, false);
+                fd_nonblock(STDOUT_FILENO, false);
+
+                if (b->exec_argv)
+                        execvp(b->exec_path, b->exec_argv);
+                else {
+                        const char *argv[] = { b->exec_path, NULL };
+                        execvp(b->exec_path, (char**) argv);
                 }
 
-                return bus_start_auth(b);
+                _exit(EXIT_FAILURE);
+        }
+
+        close_nointr_nofail(s[1]);
+        b->fd = s[0];
+
+        return bus_start_auth(b);
+}
 
-        try_again:
-                zero(b->sockaddr);
+static int bus_start_connect(sd_bus *b) {
+        int r;
 
+        assert(b);
+
+        for (;;) {
                 if (b->fd >= 0) {
                         close_nointr_nofail(b->fd);
                         b->fd = -1;
                 }
+
+                if (b->sockaddr.sa.sa_family != AF_UNSPEC) {
+                        r = bus_connect(b);
+                        if (r >= 0)
+                                return r;
+
+                        b->last_connect_error = -r;
+
+                } else if (b->exec_path) {
+
+                        r = bus_exec(b);
+                        if (r >= 0)
+                                return r;
+
+                        b->last_connect_error = -r;
+                }
+
+                r = bus_parse_next_address(b);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return b->last_connect_error ? -b->last_connect_error : -ECONNREFUSED;
         }
 }
 
@@ -1893,6 +2162,7 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
                         }
 
                         /* Try next address */
+                        bus_reset_parsed_address(bus);
                         r = bus_start_connect(bus);
                         goto null_message;
                 }
diff --git a/src/libsystemd-bus/sd-bus.h b/src/libsystemd-bus/sd-bus.h
index c97b2f4..e5990ad 100644
--- a/src/libsystemd-bus/sd-bus.h
+++ b/src/libsystemd-bus/sd-bus.h
@@ -29,8 +29,8 @@
 
 /* TODO:
  * - implicitly add stub introspection calls
- * - implement unix exec protocol
  * - server side
+ * - split out actual sending logic into backend-socket.c
  *
  * Later:
  * - add page donation logic
@@ -57,6 +57,7 @@ int sd_bus_open_user(sd_bus **ret);
 int sd_bus_new(sd_bus **ret);
 int sd_bus_set_address(sd_bus *bus, const char *address);
 int sd_bus_set_fd(sd_bus *bus, int fd);
+int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]);
 int sd_bus_set_hello(sd_bus *bus, int b);
 int sd_bus_set_negotiate_fds(sd_bus *bus, int b);
 int sd_bus_start(sd_bus *ret);
diff --git a/src/shared/strv.c b/src/shared/strv.c
index e57e0ee..a5ce7e9 100644
--- a/src/shared/strv.c
+++ b/src/shared/strv.c
@@ -64,7 +64,7 @@ void strv_free(char **l) {
         free(l);
 }
 
-char **strv_copy(char **l) {
+char **strv_copy(char * const *l) {
         char **r, **k;
 
         k = r = new(char*, strv_length(l) + 1);
@@ -84,7 +84,7 @@ char **strv_copy(char **l) {
         return r;
 }
 
-unsigned strv_length(char **l) {
+unsigned strv_length(char * const *l) {
         unsigned n = 0;
 
         if (!l)
diff --git a/src/shared/strv.h b/src/shared/strv.h
index 910d153..4cd3865 100644
--- a/src/shared/strv.h
+++ b/src/shared/strv.h
@@ -34,8 +34,8 @@ static inline void strv_freep(char ***l) {
         strv_free(*l);
 }
 
-char **strv_copy(char **l) _malloc_;
-unsigned strv_length(char **l);
+char **strv_copy(char * const *l) _malloc_;
+unsigned strv_length(char * const *l);
 
 char **strv_merge(char **a, char **b);
 char **strv_merge_concat(char **a, char **b, const char *suffix);
@@ -56,7 +56,7 @@ static inline const char* STRV_IFNOTNULL(const char *x) {
         return x ? x : (const char *) -1;
 }
 
-static inline bool strv_isempty(char **l) {
+static inline bool strv_isempty(char * const *l) {
         return !l || !*l;
 }
 

commit 021a1e78d7621bcd844a9bf22efca88960a8e28b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Mar 24 23:55:03 2013 +0100

    bus: make optional whether unix socket passing is negotiated and whether hello is sent
    
    This alos gets rid of explicit sd_open_fd() and sd_open_address()
    constructors in favour of sd_new() + sd_new_start() where the
    negotiation parameters may be set it in between.

diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h
index cdd7b21..1996508 100644
--- a/src/libsystemd-bus/bus-internal.h
+++ b/src/libsystemd-bus/bus-internal.h
@@ -57,6 +57,7 @@ struct object_callback {
 };
 
 enum bus_state {
+        BUS_UNSET,
         BUS_OPENING,
         BUS_AUTHENTICATING,
         BUS_HELLO,
@@ -68,8 +69,10 @@ struct sd_bus {
         enum bus_state state;
         int fd;
         int message_version;
+
+        bool negotiate_fds:1;
         bool can_fds:1;
-        bool sent_hello:1;
+        bool send_hello:1;
         bool ucred_valid:1;
 
         void *rbuffer;
diff --git a/src/libsystemd-bus/bus-message.c b/src/libsystemd-bus/bus-message.c
index 22e6404..416eedc 100644
--- a/src/libsystemd-bus/bus-message.c
+++ b/src/libsystemd-bus/bus-message.c
@@ -331,7 +331,7 @@ static sd_bus_message *message_new(sd_bus *bus, uint8_t type) {
         m->header->endian = SD_BUS_NATIVE_ENDIAN;
         m->header->type = type;
         m->header->version = bus ? bus->message_version : 1;
-        m->allow_fds = !bus || bus->can_fds;
+        m->allow_fds = !bus || bus->can_fds || (bus->state != BUS_HELLO && bus->state != BUS_RUNNING);
 
         return m;
 }
@@ -354,6 +354,8 @@ int sd_bus_message_new_signal(
                 return -EINVAL;
         if (!m)
                 return -EINVAL;
+        if (bus && bus->state == BUS_UNSET)
+                return -ENOTCONN;
 
         t = message_new(bus, SD_BUS_MESSAGE_TYPE_SIGNAL);
         if (!t)
@@ -396,6 +398,8 @@ int sd_bus_message_new_method_call(
                 return -EINVAL;
         if (!m)
                 return -EINVAL;
+        if (bus && bus->state == BUS_UNSET)
+                return -ENOTCONN;
 
         t = message_new(bus, SD_BUS_MESSAGE_TYPE_METHOD_CALL);
         if (!t)
@@ -445,6 +449,8 @@ static int message_new_reply(
                 return -EINVAL;
         if (!m)
                 return -EINVAL;
+        if (bus && bus->state == BUS_UNSET)
+                return -ENOTCONN;
 
         t = message_new(bus, type);
         if (!t)
diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c
index 941b33a..df40102 100644
--- a/src/libsystemd-bus/sd-bus.c
+++ b/src/libsystemd-bus/sd-bus.c
@@ -82,33 +82,91 @@ static void bus_free(sd_bus *b) {
         free(b);
 }
 
-static sd_bus* bus_new(void) {
+int sd_bus_new(sd_bus **ret) {
         sd_bus *r;
 
+        if (!ret)
+                return -EINVAL;
+
         r = new0(sd_bus, 1);
         if (!r)
-                return NULL;
+                return -ENOMEM;
 
         r->n_ref = 1;
         r->fd = -1;
         r->message_version = 1;
+        r->negotiate_fds = true;
 
         /* We guarantee that wqueue always has space for at least one
          * entry */
         r->wqueue = new(sd_bus_message*, 1);
         if (!r->wqueue) {
                 free(r);
-                return NULL;
+                return -ENOMEM;
         }
 
-        return r;
-};
+        *ret = r;
+        return 0;
+}
+
+int sd_bus_set_address(sd_bus *bus, const char *address) {
+        char *a;
+
+        if (!bus)
+                return -EINVAL;
+        if (bus->state != BUS_UNSET)
+                return -EPERM;
+        if (!address)
+                return -EINVAL;
+
+        a = strdup(address);
+        if (!a)
+                return -ENOMEM;
+
+        free(bus->address);
+        bus->address = a;
+
+        return 0;
+}
+
+int sd_bus_set_fd(sd_bus *bus, int fd) {
+        if (!bus)
+                return -EINVAL;
+        if (bus->state != BUS_UNSET)
+                return -EPERM;
+        if (fd < 0)
+                return -EINVAL;
+
+        bus->fd = fd;
+        return 0;
+}
+
+int sd_bus_set_hello(sd_bus *bus, int b) {
+        if (!bus)
+                return -EINVAL;
+        if (bus->state != BUS_UNSET)
+                return -EPERM;
+
+        bus->send_hello = !!b;
+        return 0;
+}
+
+int sd_bus_set_negotiate_fds(sd_bus *bus, int b) {
+        if (!bus)
+                return -EINVAL;
+        if (bus->state != BUS_UNSET)
+                return -EPERM;
+
+        bus->negotiate_fds = !!b;
+        return 0;
+}
 
 static int hello_callback(sd_bus *bus, int error, sd_bus_message *reply, void *userdata) {
         const char *s;
         int r;
 
         assert(bus);
+        assert(bus->state == BUS_HELLO);
 
         if (error != 0)
                 return -error;
@@ -137,6 +195,9 @@ static int bus_send_hello(sd_bus *bus) {
 
         assert(bus);
 
+        if (!bus->send_hello)
+                return 0;
+
         r = sd_bus_message_new_method_call(
                         bus,
                         "org.freedesktop.DBus",
@@ -151,14 +212,13 @@ static int bus_send_hello(sd_bus *bus) {
         if (r < 0)
                 return r;
 
-        bus->sent_hello = true;
         return r;
 }
 
 static int bus_start_running(sd_bus *bus) {
         assert(bus);
 
-        if (bus->sent_hello) {
+        if (bus->send_hello) {
                 bus->state = BUS_HELLO;
                 return 1;
         }
@@ -439,21 +499,31 @@ static int bus_write_auth(sd_bus *b) {
 }
 
 static int bus_auth_verify(sd_bus *b) {
-        char *e, *f;
+        char *e, *f, *start;
         sd_id128_t peer;
         unsigned i;
         int r;
 
-        /* We expect two response lines: "OK", "AGREE_UNIX_FD", and
-         * that's it */
+        /* We expect two response lines: "OK" and possibly
+         * "AGREE_UNIX_FD" */
 
         e = memmem(b->rbuffer, b->rbuffer_size, "\r\n", 2);
         if (!e)
                 return 0;
 
-        f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
-        if (!f)
-                return 0;
+        if (b->negotiate_fds) {
+                f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
+                if (!f)
+                        return 0;
+
+                start = f + 2;
+        } else {
+                f = NULL;
+                start = e + 2;
+        }
+
+        /* Nice! We got all the lines we need. First check the OK
+         * line */
 
         if (e - (char*) b->rbuffer != 3 + 32)
                 return -EPERM;
@@ -479,12 +549,15 @@ static int bus_auth_verify(sd_bus *b) {
 
         b->peer = peer;
 
-        b->can_fds =
-                (f - e == sizeof("\r\nAGREE_UNIX_FD") - 1) &&
-                memcmp(e + 2, "AGREE_UNIX_FD", sizeof("AGREE_UNIX_FD") - 1) == 0;
+        /* And possibly check the second line, too */
+
+        if (f)
+                b->can_fds =
+                        (f - e == sizeof("\r\nAGREE_UNIX_FD") - 1) &&
+                        memcmp(e + 2, "AGREE_UNIX_FD", sizeof("AGREE_UNIX_FD") - 1) == 0;
 
-        b->rbuffer_size -= (f + 2 - (char*) b->rbuffer);
-        memmove(b->rbuffer, f + 2, b->rbuffer_size);
+        b->rbuffer_size -= (start - (char*) b->rbuffer);
+        memmove(b->rbuffer, start, b->rbuffer_size);
 
         r = bus_start_running(b);
         if (r < 0)
@@ -564,10 +637,12 @@ static int bus_setup_fd(sd_bus *b) {
 
 static int bus_start_auth(sd_bus *b) {
         static const char auth_prefix[] = "\0AUTH EXTERNAL ";
-        static const char auth_suffix[] = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n";
+        static const char auth_suffix_with_unix_fd[] = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n";
+        static const char auth_suffix_without_unix_fd[] = "\r\nBEGIN\r\n";
 
         char text[20 + 1]; /* enough space for a 64bit integer plus NUL */
         size_t l;
+        const char *auth_suffix;
 
         assert(b);
 
@@ -581,12 +656,14 @@ static int bus_start_auth(sd_bus *b) {
         if (!b->auth_uid)
                 return -ENOMEM;
 
+        auth_suffix = b->negotiate_fds ? auth_suffix_with_unix_fd : auth_suffix_without_unix_fd;
+
         b->auth_iovec[0].iov_base = (void*) auth_prefix;
         b->auth_iovec[0].iov_len = sizeof(auth_prefix) -1;
         b->auth_iovec[1].iov_base = (void*) b->auth_uid;
         b->auth_iovec[1].iov_len = l * 2;
         b->auth_iovec[2].iov_base = (void*) auth_suffix;
-        b->auth_iovec[2].iov_len = sizeof(auth_suffix) -1;
+        b->auth_iovec[2].iov_len = strlen(auth_suffix);
         b->auth_size = sizeof(auth_prefix) - 1 + l * 2 + sizeof(auth_suffix) - 1;
 
         return bus_write_auth(b);
@@ -640,6 +717,49 @@ static int bus_start_connect(sd_bus *b) {
         }
 }
 
+static int bus_start_fd(sd_bus *b) {
+        int r;
+
+        assert(b);
+
+        r = fd_nonblock(b->fd, true);
+        if (r < 0)
+                return r;
+
+        r = fd_cloexec(b->fd, true);
+        if (r < 0)
+                return r;
+
+        r = bus_setup_fd(b);
+        if (r < 0)
+                return r;
+
+        return bus_start_auth(b);
+}
+
+int sd_bus_start(sd_bus *bus) {
+        int r;
+
+        if (!bus)
+                return -EINVAL;
+        if (bus->state != BUS_UNSET)
+                return -EPERM;
+
+        bus->state = BUS_OPENING;
+
+        if (bus->fd >= 0)
+                r = bus_start_fd(bus);
+        else if (bus->address)
+                r = bus_start_connect(bus);
+        else
+                return -EINVAL;
+
+        if (r < 0)
+                return r;
+
+        return bus_send_hello(bus);
+}
+
 int sd_bus_open_system(sd_bus **ret) {
         const char *e;
         sd_bus *b;
@@ -648,35 +768,33 @@ int sd_bus_open_system(sd_bus **ret) {
         if (!ret)
                 return -EINVAL;
 
+        r = sd_bus_new(&b);
+        if (r < 0)
+                return r;
+
         e = getenv("DBUS_SYSTEM_BUS_ADDRESS");
         if (e) {
-                r = sd_bus_open_address(e, &b);
+                r = sd_bus_set_address(b, e);
                 if (r < 0)
-                        return r;
+                        goto fail;
         } else {
-                b = bus_new();
-                if (!b)
-                        return -ENOMEM;
-
                 b->sockaddr.un.sun_family = AF_UNIX;
                 strncpy(b->sockaddr.un.sun_path, "/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path));
                 b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/dbus/system_bus_socket") - 1;
-
-                r = bus_start_connect(b);
-                if (r < 0) {
-                        bus_free(b);
-                        return r;
-                }
         }
 
-        r = bus_send_hello(b);
-        if (r < 0) {
-                sd_bus_unref(b);
-                return r;
-        }
+        b->send_hello = true;
+
+        r = sd_bus_start(b);
+        if (r < 0)
+                goto fail;
 
         *ret = b;
         return 0;
+
+fail:
+        bus_free(b);
+        return r;
 }
 
 int sd_bus_open_user(sd_bus **ret) {
@@ -688,102 +806,36 @@ int sd_bus_open_user(sd_bus **ret) {
         if (!ret)
                 return -EINVAL;
 
+        r = sd_bus_new(&b);
+        if (r < 0)
+                return r;
+
         e = getenv("DBUS_SESSION_BUS_ADDRESS");
         if (e) {
-                r = sd_bus_open_address(e, &b);
+                r = sd_bus_set_address(b, e);
                 if (r < 0)
-                        return r;
+                        goto fail;
         } else {
                 e = getenv("XDG_RUNTIME_DIR");
-                if (!e)
-                        return -ENOENT;
+                if (!e) {
+                        r = -ENOENT;
+                        goto fail;
+                }
 
                 l = strlen(e);
-                if (l + 4 > sizeof(b->sockaddr.un.sun_path))
-                        return -E2BIG;
-
-                b = bus_new();
-                if (!b)
-                        return -ENOMEM;
+                if (l + 4 > sizeof(b->sockaddr.un.sun_path)) {
+                        r = -E2BIG;
+                        goto fail;
+                }
 
                 b->sockaddr.un.sun_family = AF_UNIX;
                 memcpy(mempcpy(b->sockaddr.un.sun_path, e, l), "/bus", 4);
                 b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l + 4;
-
-                r = bus_start_connect(b);
-                if (r < 0) {
-                        bus_free(b);
-                        return r;
-                }
         }
 
-        r = bus_send_hello(b);
-        if (r < 0) {
-                sd_bus_unref(b);
-                return r;
-        }
-
-        *ret = b;
-        return 0;
-}
-
-int sd_bus_open_address(const char *address, sd_bus **ret) {
-        sd_bus *b;
-        int r;
-
-        if (!address)
-                return -EINVAL;
-        if (!ret)
-                return -EINVAL;
-
-        b = bus_new();
-        if (!b)
-                return -ENOMEM;
-
-        b->address = strdup(address);
-        if (!b->address) {
-                bus_free(b);
-                return -ENOMEM;
-        }
-
-        r = bus_start_connect(b);
-        if (r < 0) {
-                bus_free(b);
-                return r;
-        }
+        b->send_hello = true;
 
-        *ret = b;
-        return 0;
-}
-
-int sd_bus_open_fd(int fd, sd_bus **ret) {
-        sd_bus *b;
-        int r;
-
-        if (fd < 0)
-                return -EINVAL;
-        if (!ret)
-                return -EINVAL;
-
-        b = bus_new();
-        if (!b)
-                return -ENOMEM;
-
-        b->fd = fd;
-
-        r = fd_nonblock(b->fd, true);
-        if (r < 0)
-                goto fail;
-
-        fd_cloexec(b->fd, true);
-        if (r < 0)
-                goto fail;
-
-        r = bus_setup_fd(b);
-        if (r < 0)
-                goto fail;
-
-        r = bus_start_auth(b);
+        r = sd_bus_start(b);
         if (r < 0)
                 goto fail;
 
@@ -791,7 +843,7 @@ int sd_bus_open_fd(int fd, sd_bus **ret) {
         return 0;
 
 fail:
-                bus_free(b);
+        bus_free(b);
         return r;
 }
 
@@ -832,7 +884,7 @@ int sd_bus_is_open(sd_bus *bus) {
         if (!bus)
                 return -EINVAL;
 
-        return bus->fd >= 0;
+        return bus->state != BUS_UNSET && bus->fd >= 0;
 }
 
 int sd_bus_can_send(sd_bus *bus, char type) {
@@ -840,8 +892,13 @@ int sd_bus_can_send(sd_bus *bus, char type) {
 
         if (!bus)
                 return -EINVAL;
+        if (bus->fd < 0)
+                return -ENOTCONN;
 
         if (type == SD_BUS_TYPE_UNIX_FD) {
+                if (!bus->negotiate_fds)
+                        return 0;
+
                 r = bus_ensure_running(bus);
                 if (r < 0)
                         return r;
@@ -1191,12 +1248,20 @@ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) {
 
         if (!bus)
                 return -EINVAL;
+        if (bus->state == BUS_UNSET)
+                return -ENOTCONN;
         if (bus->fd < 0)
                 return -ENOTCONN;
         if (!m)
                 return -EINVAL;
-        if (m->n_fds > 0 && !bus->can_fds)
-                return -ENOTSUP;
+
+        if (m->n_fds > 0) {
+                r = sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return -ENOTSUP;
+        }
 
         /* If the serial number isn't kept, then we know that no reply
          * is expected */
@@ -1292,6 +1357,8 @@ int sd_bus_send_with_reply(
 
         if (!bus)
                 return -EINVAL;
+        if (bus->state == BUS_UNSET)
+                return -ENOTCONN;
         if (bus->fd < 0)
                 return -ENOTCONN;
         if (!m)
@@ -1374,6 +1441,11 @@ int bus_ensure_running(sd_bus *bus) {
 
         assert(bus);
 
+        if (bus->fd < 0)
+                return -ENOTCONN;
+        if (bus->state == BUS_UNSET)
+                return -ENOTCONN;
+
         if (bus->state == BUS_RUNNING)
                 return 1;
 
@@ -1408,6 +1480,8 @@ int sd_bus_send_with_reply_and_block(
                 return -EINVAL;
         if (bus->fd < 0)
                 return -ENOTCONN;
+        if (bus->state == BUS_UNSET)
+                return -ENOTCONN;
         if (!m)
                 return -EINVAL;
         if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
@@ -1526,6 +1600,8 @@ int sd_bus_get_events(sd_bus *bus) {
 
         if (!bus)
                 return -EINVAL;
+        if (bus->state == BUS_UNSET)
+                return -ENOTCONN;
         if (bus->fd < 0)
                 return -ENOTCONN;
 
@@ -1555,6 +1631,8 @@ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) {
                 return -EINVAL;
         if (!timeout_usec)
                 return -EINVAL;
+        if (bus->state == BUS_UNSET)
+                return -ENOTCONN;
         if (bus->fd < 0)
                 return -ENOTCONN;
 
@@ -1782,6 +1860,8 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
 
         if (!bus)
                 return -EINVAL;
+        if (bus->state == BUS_UNSET)
+                return -ENOTCONN;
         if (bus->fd < 0)
                 return -ENOTCONN;
 
@@ -1938,6 +2018,8 @@ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) {
 
         if (!bus)
                 return -EINVAL;
+        if (bus->state == BUS_UNSET)
+                return -ENOTCONN;
         if (bus->fd < 0)
                 return -ENOTCONN;
         if (bus->rqueue_size > 0)
@@ -1951,6 +2033,8 @@ int sd_bus_flush(sd_bus *bus) {
 
         if (!bus)
                 return -EINVAL;
+        if (bus->state == BUS_UNSET)
+                return -ENOTCONN;
         if (bus->fd < 0)
                 return -ENOTCONN;
 
diff --git a/src/libsystemd-bus/sd-bus.h b/src/libsystemd-bus/sd-bus.h
index bffce10..c97b2f4 100644
--- a/src/libsystemd-bus/sd-bus.h
+++ b/src/libsystemd-bus/sd-bus.h
@@ -53,8 +53,14 @@ typedef int (*sd_message_handler_t)(sd_bus *bus, int ret, sd_bus_message *m, voi
 
 int sd_bus_open_system(sd_bus **ret);
 int sd_bus_open_user(sd_bus **ret);
-int sd_bus_open_address(const char *address, sd_bus **ret);
-int sd_bus_open_fd(int fd, sd_bus **ret);
+
+int sd_bus_new(sd_bus **ret);
+int sd_bus_set_address(sd_bus *bus, const char *address);
+int sd_bus_set_fd(sd_bus *bus, int fd);
+int sd_bus_set_hello(sd_bus *bus, int b);
+int sd_bus_set_negotiate_fds(sd_bus *bus, int b);
+int sd_bus_start(sd_bus *ret);
+
 void sd_bus_close(sd_bus *bus);
 
 sd_bus *sd_bus_ref(sd_bus *bus);

commit 66f931b4c818a5f9f7f36b2a0f3b0422609a6e8d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Mar 24 23:54:44 2013 +0100

    bus: properly handle termination of connections

diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c
index 72c790b..941b33a 100644
--- a/src/libsystemd-bus/sd-bus.c
+++ b/src/libsystemd-bus/sd-bus.c
@@ -532,6 +532,8 @@ static int bus_read_auth(sd_bus *b) {
         k = recvmsg(b->fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
         if (k < 0)
                 return errno == EAGAIN ? 0 : -errno;
+        if (k == 0)
+                return -ECONNRESET;
 
         b->rbuffer_size += k;
 
@@ -1058,6 +1060,8 @@ static int message_read(sd_bus *bus, sd_bus_message **m) {
         k = recvmsg(bus->fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
         if (k < 0)
                 return errno == EAGAIN ? 0 : -errno;
+        if (k == 0)
+                return -ECONNRESET;
 
         bus->rbuffer_size += k;
 

commit 2c93b4efeca3ccf38d604d85490b796e875e2c31
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Mar 24 22:02:05 2013 +0100

    bus: implement support for FD passing

diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h
index 82f6084..cdd7b21 100644
--- a/src/libsystemd-bus/bus-internal.h
+++ b/src/libsystemd-bus/bus-internal.h
@@ -114,6 +114,9 @@ struct sd_bus {
 
         struct ucred ucred;
         char label[NAME_MAX];
+
+        int *fds;
+        unsigned n_fds;
 };
 
 static inline void bus_unrefp(sd_bus **b) {
@@ -137,6 +140,8 @@ static inline void bus_unrefp(sd_bus **b) {
  * bytes */
 #define BUS_ARRAY_MAX_SIZE 67108864
 
+#define BUS_FDS_MAX 1024
+
 bool object_path_is_valid(const char *p);
 bool interface_name_is_valid(const char *p);
 bool service_name_is_valid(const char *p);
diff --git a/src/libsystemd-bus/bus-message.c b/src/libsystemd-bus/bus-message.c
index 01213e3..22e6404 100644
--- a/src/libsystemd-bus/bus-message.c
+++ b/src/libsystemd-bus/bus-message.c
@@ -20,6 +20,7 @@
 ***/
 
 #include <errno.h>
+#include <fcntl.h>
 
 #include "util.h"
 #include "utf8.h"
@@ -50,8 +51,6 @@ static void reset_containers(sd_bus_message *m) {
 }
 
 static void message_free(sd_bus_message *m) {
-        unsigned i;
-
         assert(m);
 
         if (m->free_header)
@@ -63,8 +62,10 @@ static void message_free(sd_bus_message *m) {
         if (m->free_body)
                 free(m->body);
 
-        for (i = 0; i < m->n_fds; i++)
-                close_nointr_nofail(m->fds[i]);
+        if (m->free_fds) {
+                close_many(m->fds, m->n_fds);
+                free(m->fds);
+        }
 
         reset_containers(m);
         free(m->root_container.signature);
@@ -227,7 +228,9 @@ static int message_append_field_uint32(sd_bus_message *m, uint8_t h, uint32_t x)
 int bus_message_from_malloc(
                 void *buffer,
                 size_t length,
-                struct ucred *ucred,
+                int *fds,
+                unsigned n_fds,
+                const struct ucred *ucred,
                 const char *label,
                 sd_bus_message **ret) {
 
@@ -237,6 +240,7 @@ int bus_message_from_malloc(
         int r;
 
         assert(buffer || length <= 0);
+        assert(fds || n_fds <= 0);
         assert(ret);
 
         if (length < sizeof(struct bus_header))
@@ -276,11 +280,12 @@ int bus_message_from_malloc(
                 return -ENOMEM;
 
         m->n_ref = 1;
+        m->sealed = true;
         m->header = h;
-        m->free_header = true;
         m->fields = (uint8_t*) buffer + sizeof(struct bus_header);
         m->body = (uint8_t*) buffer + sizeof(struct bus_header) + ALIGN_TO(fs, 8);
-        m->sealed = true;
+        m->fds = fds;
+        m->n_fds = n_fds;
 
         if (ucred) {
                 m->uid = ucred->uid;
@@ -299,13 +304,19 @@ int bus_message_from_malloc(
         m->iovec[0].iov_len = length;
 
         r = message_parse_fields(m);
-        if (r < 0) {
-                message_free(m);
-                return r;
-        }
+        if (r < 0)
+                goto fail;
+
+        /* We take possession of the memory and fds now */
+        m->free_header = true;
+        m->free_fds = true;
 
         *ret = m;
         return 0;
+
+fail:
+        message_free(m);
+        return r;
 }
 
 static sd_bus_message *message_new(sd_bus *bus, uint8_t type) {
@@ -320,6 +331,7 @@ static sd_bus_message *message_new(sd_bus *bus, uint8_t type) {
         m->header->endian = SD_BUS_NATIVE_ENDIAN;
         m->header->type = type;
         m->header->version = bus ? bus->message_version : 1;
+        m->allow_fds = !bus || bus->can_fds;
 
         return m;
 }
@@ -770,9 +782,14 @@ int message_append_basic(sd_bus_message *m, char type, const void *p, const void
         uint32_t k;
         void *a;
         char *e = NULL;
+        int fd = -1;
+        uint32_t fdi;
+        int r;
 
         if (!m)
                 return -EINVAL;
+        if (!p)
+                return -EINVAL;
         if (m->sealed)
                 return -EPERM;
         if (!bus_type_is_basic(type))
@@ -800,26 +817,12 @@ int message_append_basic(sd_bus_message *m, char type, const void *p, const void
         case SD_BUS_TYPE_STRING:
         case SD_BUS_TYPE_OBJECT_PATH:
 
-                if (!p) {
-                        if (e)
-                                c->signature[c->index] = 0;
-
-                        return -EINVAL;
-                }
-
                 align = 4;
                 sz = 4 + strlen(p) + 1;
                 break;
 
         case SD_BUS_TYPE_SIGNATURE:
 
-                if (!p) {
-                        if (e)
-                                c->signature[c->index] = 0;
-
-                        return -EINVAL;
-                }
-
                 align = 1;
                 sz = 1 + strlen(p) + 1;
                 break;
@@ -833,6 +836,41 @@ int message_append_basic(sd_bus_message *m, char type, const void *p, const void
                 p = &k;
                 break;
 
+        case SD_BUS_TYPE_UNIX_FD: {
+                int z, *f;
+
+                if (!m->allow_fds) {
+                        r = -ENOTSUP;
+                        goto fail;
+                }
+
+                align = sz = 4;
+
+                z = *(int*) p;
+                if (z < 0) {
+                        r = -EINVAL;
+                        goto fail;
+                }
+
+                fd = fcntl(z, F_DUPFD_CLOEXEC, 3);
+                if (fd < 0) {
+                        r = -errno;
+                        goto fail;
+                }
+
+                f = realloc(m->fds, sizeof(int) * (m->n_fds + 1));
+                if (!f) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+
+                fdi = m->n_fds;
+                f[fdi] = fd;
+                m->fds = f;
+                m->free_fds = true;
+                break;
+        }
+
         default:
                 align = bus_type_get_alignment(type);
                 sz = bus_type_get_size(type);
@@ -844,11 +882,8 @@ int message_append_basic(sd_bus_message *m, char type, const void *p, const void
 
         a = message_extend_body(m, align, sz);
         if (!a) {
-                /* Truncate extended signature again */
-                if (e)
-                        c->signature[c->index] = 0;
-
-                return -ENOMEM;
+                r = -ENOMEM;
+                goto fail;
         }
 
         if (type == SD_BUS_TYPE_STRING || type == SD_BUS_TYPE_OBJECT_PATH) {
@@ -864,6 +899,13 @@ int message_append_basic(sd_bus_message *m, char type, const void *p, const void
 
                 if (stored)
                         *stored = (const uint8_t*) a + 1;
+        } else if (type == SD_BUS_TYPE_UNIX_FD) {
+                *(uint32_t*) a = fdi;
+
+                if (stored)
+                        *stored = a;
+
+                m->n_fds ++;
 
         } else {
                 memcpy(a, p, sz);
@@ -876,6 +918,16 @@ int message_append_basic(sd_bus_message *m, char type, const void *p, const void
                 c->index++;
 
         return 0;
+
+fail:
+        /* Truncate extended signature again */
+        if (e)
+                c->signature[c->index] = 0;
+
+        if (fd >= 0)
+                close_nointr_nofail(fd);
+
+        return r;
 }
 
 int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p) {
@@ -1464,6 +1516,8 @@ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
                 return -EPERM;
         if (!bus_type_is_basic(type))
                 return -EINVAL;
+        if (!p)
+                return -EINVAL;
 
         c = message_get_container(m);
 
@@ -1530,12 +1584,13 @@ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
         }
 
         default: {
-                size_t sz, align;
+                size_t sz, align, rindex;
 
                 align = bus_type_get_alignment(type);
                 sz = bus_type_get_size(type);
 
-                r = message_peek_body(m, &m->rindex, align, sz, &q);
+                rindex = m->rindex;
+                r = message_peek_body(m, &rindex, align, sz, &q);
                 if (r <= 0)
                         return r;
 
@@ -1565,10 +1620,28 @@ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
                         *(uint64_t*) p = BUS_MESSAGE_BSWAP64(m, *(uint64_t*) q);
                         break;
 
+                case SD_BUS_TYPE_UNIX_FD: {
+                        int copy;
+                        uint32_t j;
+
+                        j = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
+                        if (j >= m->n_fds)
+                                return -EBADMSG;
+
+                        copy = fcntl(m->fds[j], F_DUPFD_CLOEXEC, 3);
+                        if (copy < 0)
+                                return -errno;
+
+                        *(int*) p = copy;
+                        break;
+                }
+
                 default:
                         assert_not_reached("Unknown basic type...");
                 }
 
+                        m->rindex = rindex;
+
                 break;
         }
         }
@@ -2028,7 +2101,8 @@ static int message_read_ap(sd_bus_message *m, const char *types, va_list ap) {
                 case SD_BUS_TYPE_DOUBLE:
                 case SD_BUS_TYPE_STRING:
                 case SD_BUS_TYPE_OBJECT_PATH:
-                case SD_BUS_TYPE_SIGNATURE: {
+                case SD_BUS_TYPE_SIGNATURE:
+                case SD_BUS_TYPE_UNIX_FD: {
                         void *p;
 
                         p = va_arg(ap, void*);
@@ -2399,6 +2473,7 @@ static int message_skip_fields(
 static int message_parse_fields(sd_bus_message *m) {
         size_t ri;
         int r;
+        uint32_t unix_fds = 0;
 
         assert(m);
 
@@ -2419,6 +2494,10 @@ static int message_parse_fields(sd_bus_message *m) {
                         return -EBADMSG;
 
                 case SD_BUS_MESSAGE_HEADER_PATH:
+
+                        if (m->path)
+                                return -EBADMSG;
+
                         if (!streq(signature, "o"))
                                 return -EBADMSG;
 
@@ -2426,6 +2505,10 @@ static int message_parse_fields(sd_bus_message *m) {
                         break;
 
                 case SD_BUS_MESSAGE_HEADER_INTERFACE:
+
+                        if (m->interface)
+                                return -EBADMSG;
+
                         if (!streq(signature, "s"))
                                 return -EBADMSG;
 
@@ -2433,6 +2516,10 @@ static int message_parse_fields(sd_bus_message *m) {
                         break;
 
                 case SD_BUS_MESSAGE_HEADER_MEMBER:
+
+                        if (m->member)
+                                return -EBADMSG;
+
                         if (!streq(signature, "s"))
                                 return -EBADMSG;
 
@@ -2440,6 +2527,10 @@ static int message_parse_fields(sd_bus_message *m) {
                         break;
 
                 case SD_BUS_MESSAGE_HEADER_ERROR_NAME:
+
+                        if (m->error.name)
+                                return -EBADMSG;
+
                         if (!streq(signature, "s"))
                                 return -EBADMSG;
 
@@ -2447,6 +2538,10 @@ static int message_parse_fields(sd_bus_message *m) {
                         break;
 
                 case SD_BUS_MESSAGE_HEADER_DESTINATION:
+
+                        if (m->destination)
+                                return -EBADMSG;
+
                         if (!streq(signature, "s"))
                                 return -EBADMSG;
 
@@ -2454,6 +2549,10 @@ static int message_parse_fields(sd_bus_message *m) {
                         break;
 
                 case SD_BUS_MESSAGE_HEADER_SENDER:
+
+                        if (m->sender)
+                                return -EBADMSG;
+
                         if (!streq(signature, "s"))
                                 return -EBADMSG;
 
@@ -2465,6 +2564,9 @@ static int message_parse_fields(sd_bus_message *m) {
                         const char *s;
                         char *c;
 
+                        if (m->root_container.signature)
+                                return -EBADMSG;
+
                         if (!streq(signature, "g"))
                                 return -EBADMSG;
 
@@ -2482,6 +2584,9 @@ static int message_parse_fields(sd_bus_message *m) {
                 }
 
                 case SD_BUS_MESSAGE_HEADER_REPLY_SERIAL:
+                        if (m->reply_serial != 0)
+                                return -EBADMSG;
+
                         if (!streq(signature, "u"))
                                 return -EBADMSG;
 
@@ -2494,6 +2599,22 @@ static int message_parse_fields(sd_bus_message *m) {
 
                         break;
 
+                case SD_BUS_MESSAGE_HEADER_UNIX_FDS:
+                        if (unix_fds != 0)
+                                return -EBADMSG;
+
+                        if (!streq(signature, "u"))
+                                return -EBADMSG;
+
+                        r = message_peek_field_uint32(m, &ri, &unix_fds);
+                        if (r < 0)
+                                return -EBADMSG;
+
+                        if (unix_fds == 0)
+                                return -EBADMSG;
+
+                        break;
+
                 default:
                         r = message_skip_fields(m, &ri, (uint32_t) -1, (const char **) &signature);
                 }
@@ -2502,6 +2623,9 @@ static int message_parse_fields(sd_bus_message *m) {
                         return r;
         }
 
+        if (m->n_fds != unix_fds)
+                return -EBADMSG;
+
         if (isempty(m->root_container.signature) != (BUS_MESSAGE_BODY_SIZE(m) == 0))
                 return -EBADMSG;
 
diff --git a/src/libsystemd-bus/bus-message.h b/src/libsystemd-bus/bus-message.h
index 1a6c614..3289b37 100644
--- a/src/libsystemd-bus/bus-message.h
+++ b/src/libsystemd-bus/bus-message.h
@@ -67,12 +67,14 @@ struct sd_bus_message {
         pid_t tid;
 
         bool sealed:1;
+        bool dont_send:1;
+        bool allow_fds:1;
         bool uid_valid:1;
         bool gid_valid:1;
         bool free_header:1;
         bool free_fields:1;
         bool free_body:1;
-        bool dont_send:1;
+        bool free_fds:1;
 
         struct bus_header *header;
         void *fields;
@@ -130,5 +132,13 @@ static inline void bus_message_unrefp(sd_bus_message **m) {
 int bus_message_seal(sd_bus_message *m, uint64_t serial);
 int bus_message_dump(sd_bus_message *m);
 int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz);
-int bus_message_from_malloc(void *buffer, size_t length, struct ucred *ucred, const char *label, sd_bus_message **ret);
 int bus_message_read_strv_extend(sd_bus_message *m, char ***l);
+
+int bus_message_from_malloc(
+                void *buffer,
+                size_t length,
+                int *fds,
+                unsigned n_fds,
+                const struct ucred *ucred,
+                const char *label,
+                sd_bus_message **ret);
diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c
index 0acc6b2..72c790b 100644
--- a/src/libsystemd-bus/sd-bus.c
+++ b/src/libsystemd-bus/sd-bus.c
@@ -53,6 +53,9 @@ static void bus_free(sd_bus *b) {
         free(b->auth_uid);
         free(b->address);
 
+        close_many(b->fds, b->n_fds);
+        free(b->fds);
+
         for (i = 0; i < b->rqueue_size; i++)
                 sd_bus_message_unref(b->rqueue[i]);
         free(b->rqueue);
@@ -889,6 +892,18 @@ static int message_write(sd_bus *bus, sd_bus_message *m, size_t *idx) {
 
         if (*idx >= m->size)
                 return 0;
+        zero(mh);
+
+        if (m->n_fds > 0) {
+                struct cmsghdr *control;
+                control = alloca(CMSG_SPACE(sizeof(int) * m->n_fds));
+
+                mh.msg_control = control;
+                control->cmsg_level = SOL_SOCKET;
+                control->cmsg_type = SCM_RIGHTS;
+                mh.msg_controllen = control->cmsg_len = CMSG_LEN(sizeof(int) * m->n_fds);
+                memcpy(CMSG_DATA(control), m->fds, sizeof(int) * m->n_fds);
+        }
 
         n = m->n_iovec * sizeof(struct iovec);
         iov = alloca(n);
@@ -897,7 +912,6 @@ static int message_write(sd_bus *bus, sd_bus_message *m, size_t *idx) {
         j = 0;
         iovec_advance(iov, &j, *idx);
 
-        zero(mh);
         mh.msg_iov = iov;
         mh.msg_iovlen = m->n_iovec;
 
@@ -963,7 +977,7 @@ static int message_read_need(sd_bus *bus, size_t *need) {
 
 static int message_make(sd_bus *bus, size_t size, sd_bus_message **m) {
         sd_bus_message *t;
-        void *b = NULL;
+        void *b;
         int r;
 
         assert(bus);
@@ -976,11 +990,14 @@ static int message_make(sd_bus *bus, size_t size, sd_bus_message **m) {
                            bus->rbuffer_size - size);
                 if (!b)
                         return -ENOMEM;
-        }
+        } else
+                b = NULL;
 
         r = bus_message_from_malloc(bus->rbuffer, size,
+                                    bus->fds, bus->n_fds,
                                     bus->ucred_valid ? &bus->ucred : NULL,
-                                    bus->label[0] ? bus->label : NULL, &t);
+                                    bus->label[0] ? bus->label : NULL,
+                                    &t);
         if (r < 0) {
                 free(b);
                 return r;
@@ -989,6 +1006,9 @@ static int message_make(sd_bus *bus, size_t size, sd_bus_message **m) {
         bus->rbuffer = b;
         bus->rbuffer_size -= size;
 
+        bus->fds = NULL;
+        bus->n_fds = 0;
+
         *m = t;
         return 1;
 }
@@ -1002,7 +1022,8 @@ static int message_read(sd_bus *bus, sd_bus_message **m) {
         void *b;
         union {
                 struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
+                uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX) +
+                            CMSG_SPACE(sizeof(struct ucred)) +
                             CMSG_SPACE(NAME_MAX)]; /*selinux label */
         } control;
         struct cmsghdr *cmsg;
@@ -1039,11 +1060,24 @@ static int message_read(sd_bus *bus, sd_bus_message **m) {
                 return errno == EAGAIN ? 0 : -errno;
 
         bus->rbuffer_size += k;
-        bus->ucred_valid = false;
-        bus->label[0] = 0;
 
         for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
                 if (cmsg->cmsg_level == SOL_SOCKET &&
+                    cmsg->cmsg_type == SCM_RIGHTS) {
+                        int n, *f;
+
+                        n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+
+                        f = realloc(bus->fds, sizeof(int) + (bus->n_fds + n));
+                        if (!f) {
+                                close_many((int*) CMSG_DATA(cmsg), n);
+                                return -ENOMEM;
+                        }
+
+                        memcpy(f + bus->n_fds, CMSG_DATA(cmsg), n * sizeof(int));
+                        bus->fds = f;
+                        bus->n_fds += n;
+                } else if (cmsg->cmsg_level == SOL_SOCKET &&
                     cmsg->cmsg_type == SCM_CREDENTIALS &&
                     cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
 
@@ -1157,6 +1191,8 @@ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) {
                 return -ENOTCONN;
         if (!m)
                 return -EINVAL;
+        if (m->n_fds > 0 && !bus->can_fds)
+                return -ENOTSUP;
 
         /* If the serial number isn't kept, then we know that no reply
          * is expected */
diff --git a/src/libsystemd-bus/sd-bus.h b/src/libsystemd-bus/sd-bus.h
index b82caec..bffce10 100644
--- a/src/libsystemd-bus/sd-bus.h
+++ b/src/libsystemd-bus/sd-bus.h
@@ -28,7 +28,6 @@
 #include "sd-bus-protocol.h"
 
 /* TODO:
- * - make unix fd passing work
  * - implicitly add stub introspection calls
  * - implement unix exec protocol
  * - server side
diff --git a/src/libsystemd-bus/test-bus-chat.c b/src/libsystemd-bus/test-bus-chat.c
index 92fdd13..bdcca18 100644
--- a/src/libsystemd-bus/test-bus-chat.c
+++ b/src/libsystemd-bus/test-bus-chat.c
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <pthread.h>
 #include <unistd.h>
+#include <fcntl.h>
 
 #include "log.h"
 #include "util.h"
@@ -202,6 +203,31 @@ static int server(sd_bus *bus) {
                         }
 
                         sleep(1);
+
+                } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "FileDescriptor")) {
+                        int fd;
+                        static const char x = 'X';
+
+                        r = sd_bus_message_read(m, "h", &fd);
+                        if (r < 0) {
+                                log_error("Failed to get parameter: %s", strerror(-r));
+                                goto fail;
+                        }
+
+                        if (write(fd, &x, 1) < 0) {
+                                log_error("Failed to write to fd: %m");
+                                close_nointr_nofail(fd);
+                                goto fail;
+                        }
+
+                        close_nointr_nofail(fd);
+
+                        r = sd_bus_message_new_method_return(bus, m, &reply);
+                        if (r < 0) {
+                                log_error("Failed to allocate return: %s", strerror(-r));
+                                goto fail;
+                        }
+
                 } else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
                         const sd_bus_error e = SD_BUS_ERROR_INIT_CONST("org.freedesktop.DBus.Error.UnknownMethod", "Unknown method.");
 
@@ -242,6 +268,8 @@ static void* client1(void*p) {
         sd_bus_error error = SD_BUS_ERROR_INIT;
         const char *hello;
         int r;
+        int pp[2] = { -1, -1 };
+        char x;
 
         r = sd_bus_open_user(&bus);
         if (r < 0) {
@@ -281,6 +309,46 @@ static void* client1(void*p) {
 
         assert(streq(hello, "hello"));
 
+        if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) {
+                log_error("Failed to allocate pipe: %m");
+                r = -errno;
+                goto finish;
+        }
+
+        sd_bus_message_unref(m);
+        m = NULL;
+        r = sd_bus_message_new_method_call(
+                        bus,
+                        "org.freedesktop.systemd.test",
+                        "/",
+                        "org.freedesktop.systemd.test",
+                        "FileDescriptor",
+                        &m);
+        if (r < 0) {
+                log_error("Failed to allocate method call: %s", strerror(-r));
+                goto finish;
+        }
+
+        r = sd_bus_message_append(m, "h", pp[1]);
+        if (r < 0) {
+                log_error("Failed to append string: %s", strerror(-r));
+                goto finish;
+        }
+
+        sd_bus_message_unref(reply);
+        reply = NULL;
+        r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
+        if (r < 0) {
+                log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
+                goto finish;
+        }
+
+        errno = 0;
+        if (read(pp[0], &x, 1) <= 0) {
+                log_error("Failed to read from pipe: %s", errno ? strerror(errno) : "early read");
+                goto finish;
+        }
+
         r = 0;
 
 finish:
@@ -305,6 +373,9 @@ finish:
         }
 
         sd_bus_error_free(&error);
+
+        close_pipe(pp);
+
         return INT_TO_PTR(r);
 }
 
diff --git a/src/libsystemd-bus/test-bus-marshal.c b/src/libsystemd-bus/test-bus-marshal.c
index 32bf44f..e7d0cc7 100644
--- a/src/libsystemd-bus/test-bus-marshal.c
+++ b/src/libsystemd-bus/test-bus-marshal.c
@@ -121,7 +121,7 @@ int main(int argc, char *argv[]) {
 
         m = sd_bus_message_unref(m);
 
-        r = bus_message_from_malloc(buffer, sz, NULL, NULL, &m);
+        r = bus_message_from_malloc(buffer, sz, NULL, 0, NULL, NULL, &m);
         assert_se(r >= 0);
 
         bus_message_dump(m);
diff --git a/src/shared/util.c b/src/shared/util.c
index 260c100..03d6f00 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -219,6 +219,8 @@ void close_nointr_nofail(int fd) {
 void close_many(const int fds[], unsigned n_fd) {
         unsigned i;
 
+        assert(fds || n_fd <= 0);
+
         for (i = 0; i < n_fd; i++)
                 close_nointr_nofail(fds[i]);
 }



More information about the systemd-commits mailing list