From 2fd9ae2e9bd585312e15f8383036caee704822fb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 25 Mar 2013 02:30:32 +0100 Subject: bus: implement 'unixexec:' protocol --- src/libsystemd-bus/bus-internal.h | 5 + src/libsystemd-bus/sd-bus.c | 540 ++++++++++++++++++++++++++++---------- src/libsystemd-bus/sd-bus.h | 3 +- src/shared/strv.c | 4 +- src/shared/strv.h | 6 +- 5 files changed, 417 insertions(+), 141 deletions(-) diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h index 19965083c6..b7ed42ccd4 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 df4010265f..3bdbd11132 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 c97b2f4f49..e5990ad431 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 e57e0ee7bf..a5ce7e9593 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 910d15337f..4cd3865e1d 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; } -- cgit v1.2.3-54-g00ecf