X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibsystemd-bus%2Fsd-bus.c;h=2424ee14465bb69737f8b4281ec226c6839672fa;hp=047f286f041d92bbe3c495ed5864a8c25ff64974;hb=f9be01f3b4784affa269694a4f5cdcb87f06f2f7;hpb=2181a7f558eb52a22f09f8add9ac0abb4f2ee016 diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c index 047f286f0..2424ee144 100644 --- a/src/libsystemd-bus/sd-bus.c +++ b/src/libsystemd-bus/sd-bus.c @@ -37,6 +37,8 @@ #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); @@ -47,13 +49,13 @@ static void bus_free(sd_bus *b) { assert(b); - if (b->fd >= 0) - close_nointr_nofail(b->fd); + sd_bus_close(b); 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); @@ -84,6 +86,8 @@ static void bus_free(sd_bus *b) { hashmap_free(b->object_callbacks); + bus_match_free(&b->match_callbacks); + free(b); } @@ -98,7 +102,7 @@ int sd_bus_new(sd_bus **ret) { return -ENOMEM; r->n_ref = 1; - r->fd = -1; + r->input_fd = r->output_fd = -1; r->message_version = 1; r->negotiate_fds = true; @@ -134,15 +138,18 @@ int sd_bus_set_address(sd_bus *bus, const char *address) { return 0; } -int sd_bus_set_fd(sd_bus *bus, int fd) { +int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) { if (!bus) return -EINVAL; if (bus->state != BUS_UNSET) return -EPERM; - if (fd < 0) + if (input_fd < 0) + return -EINVAL; + if (output_fd < 0) return -EINVAL; - bus->fd = fd; + bus->input_fd = input_fd; + bus->output_fd = output_fd; return 0; } @@ -197,16 +204,16 @@ int sd_bus_set_negotiate_fds(sd_bus *bus, int b) { return 0; } -int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server) { +int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) { if (!bus) return -EINVAL; - if (!b && !sd_id128_equal(server, SD_ID128_NULL)) + if (!b && !sd_id128_equal(server_id, SD_ID128_NULL)) return -EINVAL; if (bus->state != BUS_UNSET) return -EPERM; bus->is_server = !!b; - bus->peer = server; + bus->server_id = server_id; return 0; } @@ -254,7 +261,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( @@ -431,8 +438,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); @@ -470,10 +480,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; @@ -592,6 +598,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); @@ -601,7 +642,9 @@ static void bus_reset_parsed_address(sd_bus *b) { free(b->exec_path); b->exec_path = NULL; b->exec_argv = NULL; - b->peer = SD_ID128_NULL; + b->server_id = SD_ID128_NULL; + free(b->kernel); + b->kernel = NULL; } static int bus_parse_next_address(sd_bus *b) { @@ -653,6 +696,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, ';'); @@ -661,7 +712,7 @@ static int bus_parse_next_address(sd_bus *b) { } if (guid) { - r = sd_id128_from_string(guid, &b->peer); + r = sd_id128_from_string(guid, &b->server_id); if (r < 0) return r; } @@ -676,10 +727,7 @@ static int bus_start_address(sd_bus *b) { assert(b); for (;;) { - if (b->fd >= 0) { - close_nointr_nofail(b->fd); - b->fd = -1; - } + sd_bus_close(b); if (b->sockaddr.sa.sa_family != AF_UNSPEC) { @@ -695,6 +743,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; } @@ -714,19 +769,38 @@ int bus_next_address(sd_bus *b) { } static int bus_start_fd(sd_bus *b) { + struct stat st; int r; assert(b); + assert(b->input_fd >= 0); + assert(b->output_fd >= 0); - r = fd_nonblock(b->fd, true); + r = fd_nonblock(b->input_fd, true); if (r < 0) return r; - r = fd_cloexec(b->fd, true); + r = fd_cloexec(b->input_fd, true); if (r < 0) return r; - return bus_socket_take_fd(b); + if (b->input_fd != b->output_fd) { + r = fd_nonblock(b->output_fd, true); + if (r < 0) + return r; + + r = fd_cloexec(b->output_fd, true); + if (r < 0) + return r; + } + + 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) { @@ -742,9 +816,9 @@ int sd_bus_start(sd_bus *bus) { if (bus->is_server && bus->bus_client) return -EINVAL; - if (bus->fd >= 0) + 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; @@ -845,11 +919,13 @@ fail: void sd_bus_close(sd_bus *bus) { if (!bus) return; - if (bus->fd < 0) - return; - close_nointr_nofail(bus->fd); - bus->fd = -1; + 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); + + bus->input_fd = bus->output_fd = -1; } sd_bus *sd_bus_ref(sd_bus *bus) { @@ -879,7 +955,7 @@ int sd_bus_is_open(sd_bus *bus) { if (!bus) return -EINVAL; - return bus->state != BUS_UNSET && bus->fd >= 0; + return bus->state != BUS_UNSET && bus->input_fd >= 0; } int sd_bus_can_send(sd_bus *bus, char type) { @@ -887,7 +963,7 @@ int sd_bus_can_send(sd_bus *bus, char type) { if (!bus) return -EINVAL; - if (bus->fd < 0) + if (bus->output_fd < 0) return -ENOTCONN; if (type == SD_BUS_TYPE_UNIX_FD) { @@ -904,19 +980,19 @@ int sd_bus_can_send(sd_bus *bus, char type) { return bus_type_is_valid(type); } -int sd_bus_get_peer(sd_bus *bus, sd_id128_t *peer) { +int sd_bus_get_server_id(sd_bus *bus, sd_id128_t *server_id) { int r; if (!bus) return -EINVAL; - if (!peer) + if (!server_id) return -EINVAL; r = bus_ensure_running(bus); if (r < 0) return r; - *peer = bus->peer; + *server_id = bus->server_id; return 0; } @@ -938,19 +1014,23 @@ static int dispatch_wqueue(sd_bus *bus) { assert(bus); assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); - if (bus->fd < 0) + 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. * @@ -981,7 +1061,7 @@ static int dispatch_rqueue(sd_bus *bus, sd_bus_message **m) { assert(m); assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); - if (bus->fd < 0) + if (bus->input_fd < 0) return -ENOTCONN; if (bus->rqueue_size > 0) { @@ -995,7 +1075,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; @@ -1017,7 +1101,7 @@ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) { return -EINVAL; if (bus->state == BUS_UNSET) return -ENOTCONN; - if (bus->fd < 0) + if (bus->output_fd < 0) return -ENOTCONN; if (!m) return -EINVAL; @@ -1047,11 +1131,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 @@ -1114,7 +1202,7 @@ static int timeout_compare(const void *a, const void *b) { int sd_bus_send_with_reply( sd_bus *bus, sd_bus_message *m, - sd_message_handler_t callback, + sd_bus_message_handler_t callback, void *userdata, uint64_t usec, uint64_t *serial) { @@ -1126,7 +1214,7 @@ int sd_bus_send_with_reply( return -EINVAL; if (bus->state == BUS_UNSET) return -ENOTCONN; - if (bus->fd < 0) + if (bus->output_fd < 0) return -ENOTCONN; if (!m) return -EINVAL; @@ -1151,7 +1239,7 @@ int sd_bus_send_with_reply( if (r < 0) return r; - c = new(struct reply_callback, 1); + c = new0(struct reply_callback, 1); if (!c) return -ENOMEM; @@ -1208,7 +1296,7 @@ int bus_ensure_running(sd_bus *bus) { assert(bus); - if (bus->fd < 0) + if (bus->input_fd < 0) return -ENOTCONN; if (bus->state == BUS_UNSET) return -ENOTCONN; @@ -1245,7 +1333,7 @@ int sd_bus_send_with_reply_and_block( if (!bus) return -EINVAL; - if (bus->fd < 0) + if (bus->output_fd < 0) return -ENOTCONN; if (bus->state == BUS_UNSET) return -ENOTCONN; @@ -1289,7 +1377,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) { @@ -1298,7 +1389,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; } @@ -1355,11 +1451,12 @@ int sd_bus_send_with_reply_and_block( int sd_bus_get_fd(sd_bus *bus) { if (!bus) return -EINVAL; - - if (bus->fd < 0) + if (bus->input_fd < 0) return -ENOTCONN; + if (bus->input_fd != bus->output_fd) + return -EPERM; - return bus->fd; + return bus->input_fd; } int sd_bus_get_events(sd_bus *bus) { @@ -1369,7 +1466,7 @@ int sd_bus_get_events(sd_bus *bus) { return -EINVAL; if (bus->state == BUS_UNSET) return -ENOTCONN; - if (bus->fd < 0) + if (bus->input_fd < 0) return -ENOTCONN; if (bus->state == BUS_OPENING) @@ -1400,7 +1497,7 @@ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) { return -EINVAL; if (bus->state == BUS_UNSET) return -ENOTCONN; - if (bus->fd < 0) + if (bus->input_fd < 0) return -ENOTCONN; if (bus->state == BUS_AUTHENTICATING) { @@ -1408,12 +1505,16 @@ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) { return 1; } - if (bus->state != BUS_RUNNING && bus->state != BUS_HELLO) + if (bus->state != BUS_RUNNING && bus->state != BUS_HELLO) { + *timeout_usec = (uint64_t) -1; return 0; + } c = prioq_peek(bus->reply_callbacks_prioq); - if (!c) + if (!c) { + *timeout_usec = (uint64_t) -1; return 0; + } *timeout_usec = c->timeout; return 1; @@ -1493,11 +1594,48 @@ static int process_filter(sd_bus *bus, sd_bus_message *m) { struct filter_callback *l; int r; - LIST_FOREACH(callbacks, l, bus->filter_callbacks) { - r = l->callback(bus, 0, m, l->userdata); + assert(bus); + assert(m); + + do { + bus->filter_callbacks_modified = false; + + LIST_FOREACH(callbacks, l, bus->filter_callbacks) { + + if (bus->filter_callbacks_modified) + break; + + /* Don't run this more than once per iteration */ + if (l->last_iteration == bus->iteration_counter) + continue; + + l->last_iteration = bus->iteration_counter; + + r = l->callback(bus, 0, m, l->userdata); + if (r != 0) + return r; + + } + + } while (bus->filter_callbacks_modified); + + return 0; +} + +static int process_match(sd_bus *bus, sd_bus_message *m) { + int r; + + assert(bus); + assert(m); + + do { + bus->match_callbacks_modified = false; + + r = bus_match_run(bus, &bus->match_callbacks, 0, m); if (r != 0) return r; - } + + } while (bus->match_callbacks_modified); return 0; } @@ -1534,7 +1672,7 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) { r = sd_bus_message_append(reply, "s", sd_id128_to_string(id, sid)); } else { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_INIT; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus_error_set(&error, "org.freedesktop.DBus.Error.UnknownMethod", @@ -1554,12 +1692,12 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) { } static int process_object(sd_bus *bus, sd_bus_message *m) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_INIT; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; struct object_callback *c; - char *p; int r; bool found = false; + size_t pl; assert(bus); assert(m); @@ -1570,35 +1708,53 @@ static int process_object(sd_bus *bus, sd_bus_message *m) { if (hashmap_isempty(bus->object_callbacks)) return 0; - c = hashmap_get(bus->object_callbacks, m->path); - if (c) { - r = c->callback(bus, 0, m, c->userdata); - if (r != 0) - return r; + pl = strlen(m->path); - found = true; - } + do { + char p[pl+1]; - /* Look for fallback prefixes */ - p = strdupa(m->path); - for (;;) { - char *e; + bus->object_callbacks_modified = false; - e = strrchr(p, '/'); - if (e == p || !e) - break; + c = hashmap_get(bus->object_callbacks, m->path); + if (c && c->last_iteration != bus->iteration_counter) { - *e = 0; + c->last_iteration = bus->iteration_counter; - c = hashmap_get(bus->object_callbacks, p); - if (c && c->is_fallback) { r = c->callback(bus, 0, m, c->userdata); if (r != 0) return r; found = true; } - } + + /* Look for fallback prefixes */ + strcpy(p, m->path); + for (;;) { + char *e; + + if (bus->object_callbacks_modified) + break; + + e = strrchr(p, '/'); + if (e == p || !e) + break; + + *e = 0; + + c = hashmap_get(bus->object_callbacks, p); + if (c && c->last_iteration != bus->iteration_counter && c->is_fallback) { + + c->last_iteration = bus->iteration_counter; + + r = c->callback(bus, 0, m, c->userdata); + if (r != 0) + return r; + + found = true; + } + } + + } while (bus->object_callbacks_modified); /* We found some handlers but none wanted to take this, then * return this -- with one exception, we can handle @@ -1719,6 +1875,8 @@ static int process_message(sd_bus *bus, sd_bus_message *m) { assert(bus); assert(m); + bus->iteration_counter++; + r = process_hello(bus, m); if (r != 0) return r; @@ -1731,6 +1889,10 @@ static int process_message(sd_bus *bus, sd_bus_message *m) { if (r != 0) return r; + r = process_match(bus, m); + if (r != 0) + return r; + r = process_builtin(bus, m); if (r != 0) return r; @@ -1775,7 +1937,7 @@ static int process_running(sd_bus *bus, sd_bus_message **ret) { if (m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_INIT; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus_error_set(&error, "org.freedesktop.DBus.Error.UnknownObject", "Unknown object '%s'.", m->path); @@ -1807,9 +1969,13 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) { if (!bus) return -EINVAL; - if (bus->fd < 0) + if (bus->input_fd < 0) return -ENOTCONN; + /* We don't allow recursively invoking sd_bus_process(). */ + if (bus->processing) + return -EBUSY; + switch (bus->state) { case BUS_UNSET: @@ -1835,21 +2001,25 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) { case BUS_RUNNING: case BUS_HELLO: - return process_running(bus, ret); + bus->processing = true; + r = process_running(bus, ret); + bus->processing = false; + + return r; } assert_not_reached("Unknown state"); } static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { - struct pollfd p; - int r, e; + struct pollfd p[2] = {}; + int r, e, n; struct timespec ts; usec_t until, m; assert(bus); - if (bus->fd < 0) + if (bus->input_fd < 0) return -ENOTCONN; e = sd_bus_get_events(bus); @@ -1865,19 +2035,26 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { if (r == 0) m = (uint64_t) -1; else { - usec_t n; - n = now(CLOCK_MONOTONIC); - m = until > n ? until - n : 0; + usec_t nw; + nw = now(CLOCK_MONOTONIC); + m = until > nw ? until - nw : 0; } if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m)) m = timeout_usec; - zero(p); - p.fd = bus->fd; - p.events = e; + p[0].fd = bus->input_fd; + if (bus->output_fd == bus->input_fd) { + p[0].events = e; + n = 1; + } else { + p[0].events = e & POLLIN; + p[1].fd = bus->output_fd; + p[1].events = e & POLLOUT; + n = 2; + } - r = ppoll(&p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL); + r = ppoll(p, n, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL); if (r < 0) return -errno; @@ -1890,7 +2067,7 @@ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) { return -EINVAL; if (bus->state == BUS_UNSET) return -ENOTCONN; - if (bus->fd < 0) + if (bus->input_fd < 0) return -ENOTCONN; if (bus->rqueue_size > 0) return 0; @@ -1905,7 +2082,7 @@ int sd_bus_flush(sd_bus *bus) { return -EINVAL; if (bus->state == BUS_UNSET) return -ENOTCONN; - if (bus->fd < 0) + if (bus->output_fd < 0) return -ENOTCONN; r = bus_ensure_running(bus); @@ -1929,7 +2106,7 @@ int sd_bus_flush(sd_bus *bus) { } } -int sd_bus_add_filter(sd_bus *bus, sd_message_handler_t callback, void *userdata) { +int sd_bus_add_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata) { struct filter_callback *f; if (!bus) @@ -1937,17 +2114,18 @@ int sd_bus_add_filter(sd_bus *bus, sd_message_handler_t callback, void *userdata if (!callback) return -EINVAL; - f = new(struct filter_callback, 1); + f = new0(struct filter_callback, 1); if (!f) return -ENOMEM; f->callback = callback; f->userdata = userdata; + bus->filter_callbacks_modified = true; LIST_PREPEND(struct filter_callback, callbacks, bus->filter_callbacks, f); return 0; } -int sd_bus_remove_filter(sd_bus *bus, sd_message_handler_t callback, void *userdata) { +int sd_bus_remove_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata) { struct filter_callback *f; if (!bus) @@ -1957,6 +2135,7 @@ int sd_bus_remove_filter(sd_bus *bus, sd_message_handler_t callback, void *userd LIST_FOREACH(callbacks, f, bus->filter_callbacks) { if (f->callback == callback && f->userdata == userdata) { + bus->filter_callbacks_modified = true; LIST_REMOVE(struct filter_callback, callbacks, bus->filter_callbacks, f); free(f); return 1; @@ -1970,7 +2149,7 @@ static int bus_add_object( sd_bus *bus, bool fallback, const char *path, - sd_message_handler_t callback, + sd_bus_message_handler_t callback, void *userdata) { struct object_callback *c; @@ -1987,7 +2166,7 @@ static int bus_add_object( if (r < 0) return r; - c = new(struct object_callback, 1); + c = new0(struct object_callback, 1); if (!c) return -ENOMEM; @@ -2001,6 +2180,7 @@ static int bus_add_object( c->userdata = userdata; c->is_fallback = fallback; + bus->object_callbacks_modified = true; r = hashmap_put(bus->object_callbacks, c->path, c); if (r < 0) { free(c->path); @@ -2015,7 +2195,7 @@ static int bus_remove_object( sd_bus *bus, bool fallback, const char *path, - sd_message_handler_t callback, + sd_bus_message_handler_t callback, void *userdata) { struct object_callback *c; @@ -2034,6 +2214,7 @@ static int bus_remove_object( if (c->callback != callback || c->userdata != userdata || c->is_fallback != fallback) return 0; + bus->object_callbacks_modified = true; assert_se(c == hashmap_remove(bus->object_callbacks, c->path)); free(c->path); @@ -2042,18 +2223,186 @@ static int bus_remove_object( return 1; } -int sd_bus_add_object(sd_bus *bus, const char *path, sd_message_handler_t callback, void *userdata) { +int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) { return bus_add_object(bus, false, path, callback, userdata); } -int sd_bus_remove_object(sd_bus *bus, const char *path, sd_message_handler_t callback, void *userdata) { +int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) { return bus_remove_object(bus, false, path, callback, userdata); } -int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_message_handler_t callback, void *userdata) { +int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) { return bus_add_object(bus, true, prefix, callback, userdata); } -int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_message_handler_t callback, void *userdata) { +int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) { return bus_remove_object(bus, true, prefix, callback, userdata); } + +int sd_bus_add_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata) { + int r = 0; + + if (!bus) + return -EINVAL; + if (!match) + return -EINVAL; + + if (bus->bus_client) { + r = bus_add_match_internal(bus, match); + if (r < 0) + return r; + } + + if (callback) { + bus->match_callbacks_modified = true; + r = bus_match_add(&bus->match_callbacks, match, callback, userdata, NULL); + if (r < 0) { + + if (bus->bus_client) + bus_remove_match_internal(bus, match); + } + } + + return r; +} + +int sd_bus_remove_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata) { + int r = 0, q = 0; + + if (!bus) + return -EINVAL; + if (!match) + return -EINVAL; + + if (bus->bus_client) + r = bus_remove_match_internal(bus, match); + + if (callback) { + bus->match_callbacks_modified = true; + q = bus_match_remove(&bus->match_callbacks, match, callback, userdata); + } + + if (r < 0) + return r; + return q; +} + +int sd_bus_emit_signal( + sd_bus *bus, + const char *path, + const char *interface, + const char *member, + const char *types, ...) { + + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + va_list ap; + int r; + + if (!bus) + return -EINVAL; + + r = sd_bus_message_new_signal(bus, path, interface, member, &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_call_method( + sd_bus *bus, + const char *destination, + const char *path, + const char *interface, + const char *member, + sd_bus_error *error, + sd_bus_message **reply, + const char *types, ...) { + + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + va_list ap; + int r; + + if (!bus) + return -EINVAL; + + r = sd_bus_message_new_method_call(bus, destination, path, interface, member, &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_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); +}