summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2013-03-25 02:30:32 +0100
committerLennart Poettering <lennart@poettering.net>2013-03-25 02:33:35 +0100
commit2fd9ae2e9bd585312e15f8383036caee704822fb (patch)
treec37a71c351e183c47539e54eccd59548e5e2dc51 /src
parent021a1e78d7621bcd844a9bf22efca88960a8e28b (diff)
bus: implement 'unixexec:' protocol
Diffstat (limited to 'src')
-rw-r--r--src/libsystemd-bus/bus-internal.h5
-rw-r--r--src/libsystemd-bus/sd-bus.c540
-rw-r--r--src/libsystemd-bus/sd-bus.h3
-rw-r--r--src/shared/strv.c4
-rw-r--r--src/shared/strv.h6
5 files changed, 417 insertions, 141 deletions
diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h
index 19965083c..b7ed42ccd 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 df4010265..3bdbd1113 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 c97b2f4f4..e5990ad43 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 e57e0ee7b..a5ce7e959 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 910d15337..4cd3865e1 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;
}