From 2c93b4efeca3ccf38d604d85490b796e875e2c31 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 24 Mar 2013 22:02:05 +0100 Subject: bus: implement support for FD passing --- src/libsystemd-bus/bus-internal.h | 5 + src/libsystemd-bus/bus-message.c | 190 ++++++++++++++++++++++++++++------ src/libsystemd-bus/bus-message.h | 14 ++- src/libsystemd-bus/sd-bus.c | 50 +++++++-- src/libsystemd-bus/sd-bus.h | 1 - src/libsystemd-bus/test-bus-chat.c | 71 +++++++++++++ src/libsystemd-bus/test-bus-marshal.c | 2 +- src/shared/util.c | 2 + 8 files changed, 291 insertions(+), 44 deletions(-) diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h index 82f60849d5..cdd7b2164f 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 01213e31ad..22e6404209 100644 --- a/src/libsystemd-bus/bus-message.c +++ b/src/libsystemd-bus/bus-message.c @@ -20,6 +20,7 @@ ***/ #include +#include #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 1a6c614611..3289b378f8 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 0acc6b2d1e..72c790bd59 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 b82caec5f6..bffce10c1f 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 92fdd13faf..bdcca18c84 100644 --- a/src/libsystemd-bus/test-bus-chat.c +++ b/src/libsystemd-bus/test-bus-chat.c @@ -23,6 +23,7 @@ #include #include #include +#include #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 32bf44fffe..e7d0cc7d72 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 260c100868..03d6f00622 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]); } -- cgit v1.2.3-54-g00ecf