X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibsystemd-bus%2Fsd-bus.c;h=b0730d495433dc3ddfccf60e9f64edafe24c5a0c;hp=fde39e0cac9dc12490d56d75987622200517a19b;hb=63edf05ed9c1d4cb5cf9364e734b2a96f84622d0;hpb=eba8617efc5ed1410acb14bee41433adac7011e2 diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c index fde39e0ca..b0730d495 100644 --- a/src/libsystemd-bus/sd-bus.c +++ b/src/libsystemd-bus/sd-bus.c @@ -26,21 +26,36 @@ #include #include #include +#include #include "util.h" #include "macro.h" #include "strv.h" #include "set.h" +#include "missing.h" #include "sd-bus.h" #include "bus-internal.h" #include "bus-message.h" #include "bus-type.h" #include "bus-socket.h" +#include "bus-kernel.h" #include "bus-control.h" static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec); +static void bus_close_fds(sd_bus *b) { + assert(b); + + if (b->input_fd >= 0) + close_nointr_nofail(b->input_fd); + + if (b->output_fd >= 0 && b->output_fd != b->input_fd) + close_nointr_nofail(b->output_fd); + + b->input_fd = b->output_fd = -1; +} + static void bus_free(sd_bus *b) { struct filter_callback *f; struct object_callback *c; @@ -48,12 +63,16 @@ static void bus_free(sd_bus *b) { assert(b); - sd_bus_close(b); + bus_close_fds(b); + + if (b->kdbus_buffer) + munmap(b->kdbus_buffer, KDBUS_POOL_SIZE); free(b->rbuffer); free(b->unique_name); free(b->auth_buffer); free(b->address); + free(b->kernel); free(b->exec_path); strv_free(b->exec_argv); @@ -83,9 +102,10 @@ static void bus_free(sd_bus *b) { } hashmap_free(b->object_callbacks); - bus_match_free(&b->match_callbacks); + bus_kernel_flush_memfd(b); + free(b); } @@ -99,7 +119,7 @@ int sd_bus_new(sd_bus **ret) { if (!r) return -ENOMEM; - r->n_ref = 1; + r->n_ref = REFCNT_INIT; r->input_fd = r->output_fd = -1; r->message_version = 1; r->negotiate_fds = true; @@ -225,18 +245,18 @@ int sd_bus_set_anonymous(sd_bus *bus, int b) { return 0; } -static int hello_callback(sd_bus *bus, int error, sd_bus_message *reply, void *userdata) { +static int hello_callback(sd_bus *bus, sd_bus_message *reply, void *userdata) { const char *s; int r; assert(bus); assert(bus->state == BUS_HELLO); - - if (error != 0) - return -error; - assert(reply); + r = bus_message_to_errno(reply); + if (r < 0) + return r; + r = sd_bus_message_read(reply, "s", &s); if (r < 0) return r; @@ -259,7 +279,7 @@ static int bus_send_hello(sd_bus *bus) { assert(bus); - if (!bus->bus_client) + if (!bus->bus_client || bus->is_kernel) return 0; r = sd_bus_message_new_method_call( @@ -278,7 +298,7 @@ static int bus_send_hello(sd_bus *bus) { int bus_start_running(sd_bus *bus) { assert(bus); - if (bus->bus_client) { + if (bus->bus_client && !bus->is_kernel) { bus->state = BUS_HELLO; return 1; } @@ -436,8 +456,11 @@ static int parse_unix_address(sd_bus *b, const char **p, char **guid) { 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; + struct addrinfo *result, hints = { + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_ADDRCONFIG, + }; assert(b); assert(p); @@ -475,10 +498,6 @@ static int parse_tcp_address(sd_bus *b, const char **p, char **guid) { 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; @@ -597,6 +616,41 @@ fail: return r; } +static int parse_kernel_address(sd_bus *b, const char **p, char **guid) { + _cleanup_free_ char *path = 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) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "path", &path); + if (r < 0) + return r; + else if (r > 0) + continue; + + skip_address_key(p); + } + + if (!path) + return -EINVAL; + + free(b->kernel); + b->kernel = path; + path = NULL; + + return 0; +} + static void bus_reset_parsed_address(sd_bus *b) { assert(b); @@ -607,6 +661,8 @@ static void bus_reset_parsed_address(sd_bus *b) { b->exec_path = NULL; b->exec_argv = NULL; b->server_id = SD_ID128_NULL; + free(b->kernel); + b->kernel = NULL; } static int bus_parse_next_address(sd_bus *b) { @@ -658,6 +714,14 @@ static int bus_parse_next_address(sd_bus *b) { break; + } else if (startswith(a, "kernel:")) { + + a += 7; + r = parse_kernel_address(b, &a, &guid); + if (r < 0) + return r; + + break; } a = strchr(a, ';'); @@ -697,6 +761,13 @@ static int bus_start_address(sd_bus *b) { if (r >= 0) return r; + b->last_connect_error = -r; + } else if (b->kernel) { + + r = bus_kernel_connect(b); + if (r >= 0) + return r; + b->last_connect_error = -r; } @@ -716,6 +787,7 @@ int bus_next_address(sd_bus *b) { } static int bus_start_fd(sd_bus *b) { + struct stat st; int r; assert(b); @@ -740,7 +812,13 @@ static int bus_start_fd(sd_bus *b) { return r; } - return bus_socket_take_fd(b); + if (fstat(b->input_fd, &st) < 0) + return -errno; + + if (S_ISCHR(b->input_fd)) + return bus_kernel_take_fd(b); + else + return bus_socket_take_fd(b); } int sd_bus_start(sd_bus *bus) { @@ -758,7 +836,7 @@ int sd_bus_start(sd_bus *bus) { if (bus->input_fd >= 0) r = bus_start_fd(bus); - else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path) + else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path || bus->kernel) r = bus_start_address(bus); else return -EINVAL; @@ -781,7 +859,7 @@ int sd_bus_open_system(sd_bus **ret) { if (r < 0) return r; - e = getenv("DBUS_SYSTEM_BUS_ADDRESS"); + e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS"); if (e) { r = sd_bus_set_address(b, e); if (r < 0) @@ -819,13 +897,13 @@ int sd_bus_open_user(sd_bus **ret) { if (r < 0) return r; - e = getenv("DBUS_SESSION_BUS_ADDRESS"); + e = secure_getenv("DBUS_SESSION_BUS_ADDRESS"); if (e) { r = sd_bus_set_address(b, e); if (r < 0) goto fail; } else { - e = getenv("XDG_RUNTIME_DIR"); + e = secure_getenv("XDG_RUNTIME_DIR"); if (!e) { r = -ENOENT; goto fail; @@ -860,21 +938,27 @@ void sd_bus_close(sd_bus *bus) { if (!bus) return; - if (bus->input_fd >= 0) - close_nointr_nofail(bus->input_fd); - if (bus->output_fd >= 0 && bus->output_fd != bus->input_fd) - close_nointr_nofail(bus->output_fd); + if (bus->state != BUS_CLOSED) + return; + + bus->state = BUS_CLOSED; - bus->input_fd = bus->output_fd = -1; + if (!bus->is_kernel) + bus_close_fds(bus); + + /* We'll leave the fd open in case this is a kernel bus, since + * there might still be memblocks around that reference this + * bus, and they might need to invoke the + * KDBUS_CMD_MSG_RELEASE ioctl on the fd when they are + * freed. */ } sd_bus *sd_bus_ref(sd_bus *bus) { if (!bus) return NULL; - assert(bus->n_ref > 0); + assert_se(REFCNT_INC(bus->n_ref) >= 2); - bus->n_ref++; return bus; } @@ -882,10 +966,7 @@ sd_bus *sd_bus_unref(sd_bus *bus) { if (!bus) return NULL; - assert(bus->n_ref > 0); - bus->n_ref--; - - if (bus->n_ref <= 0) + if (REFCNT_DEC(bus->n_ref) <= 0) bus_free(bus); return NULL; @@ -895,7 +976,7 @@ int sd_bus_is_open(sd_bus *bus) { if (!bus) return -EINVAL; - return bus->state != BUS_UNSET && bus->input_fd >= 0; + return BUS_IS_OPEN(bus->state); } int sd_bus_can_send(sd_bus *bus, char type) { @@ -903,7 +984,7 @@ int sd_bus_can_send(sd_bus *bus, char type) { if (!bus) return -EINVAL; - if (bus->output_fd < 0) + if (bus->state == BUS_UNSET) return -ENOTCONN; if (type == SD_BUS_TYPE_UNIX_FD) { @@ -954,19 +1035,20 @@ static int dispatch_wqueue(sd_bus *bus) { assert(bus); assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); - if (bus->output_fd < 0) - return -ENOTCONN; - while (bus->wqueue_size > 0) { - r = bus_socket_write_message(bus, bus->wqueue[0], &bus->windex); + if (bus->is_kernel) + r = bus_kernel_write_message(bus, bus->wqueue[0]); + else + r = bus_socket_write_message(bus, bus->wqueue[0], &bus->windex); + if (r < 0) { sd_bus_close(bus); return r; } else if (r == 0) /* Didn't do anything this time */ return ret; - else if (bus->windex >= bus->wqueue[0]->size) { + else if (bus->is_kernel || bus->windex >= BUS_MESSAGE_SIZE(bus->wqueue[0])) { /* Fully written. Let's drop the entry from * the queue. * @@ -997,9 +1079,6 @@ static int dispatch_rqueue(sd_bus *bus, sd_bus_message **m) { assert(m); assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); - if (bus->input_fd < 0) - return -ENOTCONN; - if (bus->rqueue_size > 0) { /* Dispatch a queued message */ @@ -1011,7 +1090,11 @@ static int dispatch_rqueue(sd_bus *bus, sd_bus_message **m) { /* Try to read a new message */ do { - r = bus_socket_read_message(bus, &z); + if (bus->is_kernel) + r = bus_kernel_read_message(bus, &z); + else + r = bus_socket_read_message(bus, &z); + if (r < 0) { sd_bus_close(bus); return r; @@ -1031,9 +1114,7 @@ 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->output_fd < 0) + if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; if (!m) return -EINVAL; @@ -1063,11 +1144,15 @@ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) { if ((bus->state == BUS_RUNNING || bus->state == BUS_HELLO) && bus->wqueue_size <= 0) { size_t idx = 0; - r = bus_socket_write_message(bus, m, &idx); + if (bus->is_kernel) + r = bus_kernel_write_message(bus, m); + else + r = bus_socket_write_message(bus, m, &idx); + if (r < 0) { sd_bus_close(bus); return r; - } else if (idx < m->size) { + } else if (!bus->is_kernel && idx < BUS_MESSAGE_SIZE(m)) { /* Wasn't fully written. So let's remember how * much was written. Note that the first entry * of the wqueue array is always allocated so @@ -1140,9 +1225,7 @@ int sd_bus_send_with_reply( if (!bus) return -EINVAL; - if (bus->state == BUS_UNSET) - return -ENOTCONN; - if (bus->output_fd < 0) + if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; if (!m) return -EINVAL; @@ -1224,11 +1307,8 @@ int bus_ensure_running(sd_bus *bus) { assert(bus); - if (bus->input_fd < 0) - return -ENOTCONN; - if (bus->state == BUS_UNSET) + if (bus->state == BUS_UNSET || bus->state == BUS_CLOSED) return -ENOTCONN; - if (bus->state == BUS_RUNNING) return 1; @@ -1261,9 +1341,7 @@ int sd_bus_send_with_reply_and_block( if (!bus) return -EINVAL; - if (bus->output_fd < 0) - return -ENOTCONN; - if (bus->state == BUS_UNSET) + if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; if (!m) return -EINVAL; @@ -1305,7 +1383,10 @@ int sd_bus_send_with_reply_and_block( room = true; } - r = bus_socket_read_message(bus, &incoming); + if (bus->is_kernel) + r = bus_kernel_read_message(bus, &incoming); + else + r = bus_socket_read_message(bus, &incoming); if (r < 0) return r; if (incoming) { @@ -1314,7 +1395,12 @@ int sd_bus_send_with_reply_and_block( /* Found a match! */ if (incoming->header->type == SD_BUS_MESSAGE_TYPE_METHOD_RETURN) { - *reply = incoming; + + if (reply) + *reply = incoming; + else + sd_bus_message_unref(incoming); + return 0; } @@ -1371,7 +1457,7 @@ int sd_bus_send_with_reply_and_block( int sd_bus_get_fd(sd_bus *bus) { if (!bus) return -EINVAL; - if (bus->input_fd < 0) + if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; if (bus->input_fd != bus->output_fd) return -EPERM; @@ -1384,9 +1470,7 @@ int sd_bus_get_events(sd_bus *bus) { if (!bus) return -EINVAL; - if (bus->state == BUS_UNSET) - return -ENOTCONN; - if (bus->input_fd < 0) + if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; if (bus->state == BUS_OPENING) @@ -1415,9 +1499,7 @@ 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->input_fd < 0) + if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; if (bus->state == BUS_AUTHENTICATING) { @@ -1441,6 +1523,7 @@ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) { } static int process_timeout(sd_bus *bus) { + _cleanup_bus_message_unref_ sd_bus_message* m = NULL; struct reply_callback *c; usec_t n; int r; @@ -1455,10 +1538,18 @@ static int process_timeout(sd_bus *bus) { if (c->timeout > n) return 0; + r = bus_message_new_synthetic_error( + bus, + c->serial, + &SD_BUS_ERROR_MAKE("org.freedesktop.DBus.Error.Timeout", "Timed out"), + &m); + if (r < 0) + return r; + assert_se(prioq_pop(bus->reply_callbacks_prioq) == c); hashmap_remove(bus->reply_callbacks, &c->serial); - r = c->callback(bus, ETIMEDOUT, NULL, c->userdata); + r = c->callback(bus, m, c->userdata); free(c); return r < 0 ? r : 1; @@ -1504,7 +1595,11 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) { if (c->timeout != 0) prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx); - r = c->callback(bus, 0, m, c->userdata); + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + r = c->callback(bus, m, c->userdata); free(c); return r; @@ -1531,7 +1626,11 @@ static int process_filter(sd_bus *bus, sd_bus_message *m) { l->last_iteration = bus->iteration_counter; - r = l->callback(bus, 0, m, l->userdata); + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + r = l->callback(bus, m, l->userdata); if (r != 0) return r; @@ -1551,7 +1650,7 @@ static int process_match(sd_bus *bus, sd_bus_message *m) { do { bus->match_callbacks_modified = false; - r = bus_match_run(bus, &bus->match_callbacks, 0, m); + r = bus_match_run(bus, &bus->match_callbacks, m); if (r != 0) return r; @@ -1640,7 +1739,11 @@ static int process_object(sd_bus *bus, sd_bus_message *m) { c->last_iteration = bus->iteration_counter; - r = c->callback(bus, 0, m, c->userdata); + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + r = c->callback(bus, m, c->userdata); if (r != 0) return r; @@ -1666,7 +1769,11 @@ static int process_object(sd_bus *bus, sd_bus_message *m) { c->last_iteration = bus->iteration_counter; - r = c->callback(bus, 0, m, c->userdata); + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + r = c->callback(bus, m, c->userdata); if (r != 0) return r; @@ -1744,13 +1851,9 @@ static int process_introspect(sd_bus *bus, sd_bus_message *m) { if (p) *p = 0; - r = set_put(s, a); - if (r < 0) { - free(a); - - if (r != -EEXIST) - return r; - } + r = set_consume(s, a); + if (r < 0 && r != -EEXIST) + return r; } f = open_memstream(&introspection, &size); @@ -1850,6 +1953,10 @@ static int process_running(sd_bus *bus, sd_bus_message **ret) { goto null_message; if (ret) { + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + *ret = m; m = NULL; return 1; @@ -1889,8 +1996,6 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) { if (!bus) return -EINVAL; - if (bus->input_fd < 0) - return -ENOTCONN; /* We don't allow recursively invoking sd_bus_process(). */ if (bus->processing) @@ -1899,6 +2004,7 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) { switch (bus->state) { case BUS_UNSET: + case BUS_CLOSED: return -ENOTCONN; case BUS_OPENING: @@ -1932,14 +2038,14 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) { } static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { - struct pollfd p[2]; + struct pollfd p[2] = {}; int r, e, n; struct timespec ts; usec_t until, m; assert(bus); - if (bus->input_fd < 0) + if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; e = sd_bus_get_events(bus); @@ -1963,9 +2069,7 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m)) m = timeout_usec; - zero(p); p[0].fd = bus->input_fd; - if (bus->output_fd == bus->input_fd) { p[0].events = e; n = 1; @@ -1987,9 +2091,7 @@ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) { if (!bus) return -EINVAL; - if (bus->state == BUS_UNSET) - return -ENOTCONN; - if (bus->input_fd < 0) + if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; if (bus->rqueue_size > 0) return 0; @@ -2002,9 +2104,7 @@ int sd_bus_flush(sd_bus *bus) { if (!bus) return -EINVAL; - if (bus->state == BUS_UNSET) - return -ENOTCONN; - if (bus->output_fd < 0) + if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; r = bus_ensure_running(bus); @@ -2265,3 +2365,66 @@ int sd_bus_call_method( return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply); } + +int sd_bus_reply_method_return( + sd_bus *bus, + sd_bus_message *call, + const char *types, ...) { + + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + va_list ap; + int r; + + if (!bus) + return -EINVAL; + if (!call) + return -EINVAL; + if (!call->sealed) + return -EPERM; + if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL) + return -EINVAL; + + if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) + return 0; + + r = sd_bus_message_new_method_return(bus, call, &m); + if (r < 0) + return r; + + va_start(ap, types); + r = bus_message_append_ap(m, types, ap); + va_end(ap); + if (r < 0) + return r; + + return sd_bus_send(bus, m, NULL); +} + +int sd_bus_reply_method_error( + sd_bus *bus, + sd_bus_message *call, + const sd_bus_error *e) { + + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + int r; + + if (!bus) + return -EINVAL; + if (!call) + return -EINVAL; + if (!call->sealed) + return -EPERM; + if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL) + return -EINVAL; + if (!sd_bus_error_is_set(e)) + return -EINVAL; + + if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) + return 0; + + r = sd_bus_message_new_method_error(bus, call, e, &m); + if (r < 0) + return r; + + return sd_bus_send(bus, m, NULL); +}