#include "util.h"
#include "macro.h"
-#include "missing.h"
#include "strv.h"
+#include "set.h"
#include "sd-bus.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-type.h"
+#include "bus-socket.h"
+#include "bus-control.h"
static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec);
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_uid);
+ free(b->auth_buffer);
free(b->address);
free(b->exec_path);
hashmap_free(b->object_callbacks);
+ bus_match_free(&b->match_callbacks);
+
free(b);
}
return -ENOMEM;
r->n_ref = 1;
- r->fd = -1;
+ r->input_fd = r->output_fd = -1;
r->message_version = 1;
r->negotiate_fds = true;
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;
}
return 0;
}
-int sd_bus_set_hello(sd_bus *bus, int b) {
+int sd_bus_set_bus_client(sd_bus *bus, int b) {
if (!bus)
return -EINVAL;
if (bus->state != BUS_UNSET)
return -EPERM;
- bus->send_hello = !!b;
+ bus->bus_client = !!b;
return 0;
}
return 0;
}
+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_id, SD_ID128_NULL))
+ return -EINVAL;
+ if (bus->state != BUS_UNSET)
+ return -EPERM;
+
+ bus->is_server = !!b;
+ bus->server_id = server_id;
+ return 0;
+}
+
+int sd_bus_set_anonymous(sd_bus *bus, int b) {
+ if (!bus)
+ return -EINVAL;
+ if (bus->state != BUS_UNSET)
+ return -EPERM;
+
+ bus->anonymous_auth = !!b;
+ return 0;
+}
+
static int hello_callback(sd_bus *bus, int error, sd_bus_message *reply, void *userdata) {
const char *s;
int r;
assert(bus);
- if (!bus->send_hello)
+ if (!bus->bus_client)
return 0;
r = sd_bus_message_new_method_call(
if (r < 0)
return r;
- r = sd_bus_send_with_reply(bus, m, hello_callback, NULL, 0, NULL);
- if (r < 0)
- return r;
-
- return r;
+ return sd_bus_send_with_reply(bus, m, hello_callback, NULL, 0, &bus->hello_serial);
}
-static int bus_start_running(sd_bus *bus) {
+int bus_start_running(sd_bus *bus) {
assert(bus);
- if (bus->send_hello) {
+ if (bus->bus_client) {
bus->state = BUS_HELLO;
return 1;
}
errno = 0;
ul = strtoul(*p + 4, (char**) p, 10);
- if (errno != 0 || **p != '=' || ul > 256) {
+ if (errno > 0 || **p != '=' || ul > 256) {
r = -EINVAL;
goto fail;
}
skip_address_key(p);
}
- if (!path)
+ if (!path) {
+ r = -EINVAL;
goto fail;
+ }
/* Make sure there are no holes in the array, with the
* exception of argv[0] */
free(b->exec_path);
b->exec_path = NULL;
b->exec_argv = NULL;
- b->peer = SD_ID128_NULL;
+ b->server_id = SD_ID128_NULL;
}
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;
}
return 1;
}
-static void iovec_advance(struct iovec *iov, unsigned *idx, size_t size) {
-
- while (size > 0) {
- struct iovec *i = iov + *idx;
-
- if (i->iov_len > size) {
- i->iov_base = (uint8_t*) i->iov_base + size;
- i->iov_len -= size;
- return;
- }
-
- size -= i->iov_len;
-
- i->iov_base = NULL;
- i->iov_len = 0;
-
- (*idx) ++;
- }
-}
-
-static int bus_write_auth(sd_bus *b) {
- struct msghdr mh;
- ssize_t k;
-
- assert(b);
- assert(b->state == BUS_AUTHENTICATING);
-
- if (b->auth_index >= ELEMENTSOF(b->auth_iovec))
- return 0;
-
- if (b->auth_timeout == 0)
- b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_DEFAULT_TIMEOUT;
-
- zero(mh);
- mh.msg_iov = b->auth_iovec + b->auth_index;
- mh.msg_iovlen = ELEMENTSOF(b->auth_iovec) - b->auth_index;
-
- k = sendmsg(b->fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
- if (k < 0)
- return errno == EAGAIN ? 0 : -errno;
-
- iovec_advance(b->auth_iovec, &b->auth_index, (size_t) k);
-
- return 1;
-}
-
-static int bus_auth_verify(sd_bus *b) {
- char *e, *f, *start;
- sd_id128_t peer;
- unsigned i;
- int r;
-
- /* We expect two response lines: "OK" and possibly
- * "AGREE_UNIX_FD" */
-
- e = memmem(b->rbuffer, b->rbuffer_size, "\r\n", 2);
- if (!e)
- return 0;
-
- if (b->negotiate_fds) {
- f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
- if (!f)
- return 0;
-
- start = f + 2;
- } else {
- f = NULL;
- start = e + 2;
- }
-
- /* Nice! We got all the lines we need. First check the OK
- * line */
-
- if (e - (char*) b->rbuffer != 3 + 32)
- return -EPERM;
-
- if (memcmp(b->rbuffer, "OK ", 3))
- return -EPERM;
-
- for (i = 0; i < 32; i += 2) {
- int x, y;
-
- x = unhexchar(((char*) b->rbuffer)[3 + i]);
- y = unhexchar(((char*) b->rbuffer)[3 + i + 1]);
-
- if (x < 0 || y < 0)
- return -EINVAL;
-
- peer.bytes[i/2] = ((uint8_t) x << 4 | (uint8_t) y);
- }
-
- if (!sd_id128_equal(b->peer, SD_ID128_NULL) &&
- !sd_id128_equal(b->peer, peer))
- return -EPERM;
-
- b->peer = peer;
-
- /* And possibly check the second line, too */
-
- if (f)
- b->can_fds =
- (f - e == sizeof("\r\nAGREE_UNIX_FD") - 1) &&
- memcmp(e + 2, "AGREE_UNIX_FD", sizeof("AGREE_UNIX_FD") - 1) == 0;
-
- b->rbuffer_size -= (start - (char*) b->rbuffer);
- memmove(b->rbuffer, start, b->rbuffer_size);
-
- r = bus_start_running(b);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int bus_read_auth(sd_bus *b) {
- struct msghdr mh;
- struct iovec iov;
- size_t n;
- ssize_t k;
- int r;
- void *p;
-
- assert(b);
-
- r = bus_auth_verify(b);
- if (r != 0)
- return r;
-
- n = MAX(3 + 32 + 2 + sizeof("AGREE_UNIX_FD") - 1 + 2, b->rbuffer_size * 2);
-
- if (n > BUS_AUTH_SIZE_MAX)
- n = BUS_AUTH_SIZE_MAX;
-
- if (b->rbuffer_size >= n)
- return -ENOBUFS;
-
- p = realloc(b->rbuffer, n);
- if (!p)
- return -ENOMEM;
-
- b->rbuffer = p;
-
- zero(iov);
- iov.iov_base = (uint8_t*) b->rbuffer + b->rbuffer_size;
- iov.iov_len = n - b->rbuffer_size;
-
- zero(mh);
- mh.msg_iov = &iov;
- mh.msg_iovlen = 1;
-
- k = recvmsg(b->fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
- if (k < 0)
- return errno == EAGAIN ? 0 : -errno;
- if (k == 0)
- return -ECONNRESET;
-
- b->rbuffer_size += k;
-
- r = bus_auth_verify(b);
- if (r != 0)
- return r;
-
- return 1;
-}
-
-static int bus_setup_fd(sd_bus *b) {
- int one;
-
- assert(b);
-
- /* Enable SO_PASSCRED + SO_PASSEC. We try this on any socket,
- * just in case. This is actually irrelavant for */
- one = 1;
- setsockopt(b->fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
- setsockopt(b->fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
-
- /* Increase the buffers to a MB */
- fd_inc_rcvbuf(b->fd, 1024*1024);
- fd_inc_sndbuf(b->fd, 1024*1024);
-
- return 0;
-}
-
-static int bus_start_auth(sd_bus *b) {
- static const char auth_prefix[] = "\0AUTH EXTERNAL ";
- static const char auth_suffix_with_unix_fd[] = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n";
- static const char auth_suffix_without_unix_fd[] = "\r\nBEGIN\r\n";
-
- 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);
-
- l = strlen(text);
- b->auth_uid = hexmem(text, l);
- if (!b->auth_uid)
- return -ENOMEM;
-
- auth_suffix = b->negotiate_fds ? auth_suffix_with_unix_fd : auth_suffix_without_unix_fd;
-
- b->auth_iovec[0].iov_base = (void*) auth_prefix;
- b->auth_iovec[0].iov_len = sizeof(auth_prefix) -1;
- b->auth_iovec[1].iov_base = (void*) b->auth_uid;
- b->auth_iovec[1].iov_len = l * 2;
- b->auth_iovec[2].iov_base = (void*) auth_suffix;
- b->auth_iovec[2].iov_len = strlen(auth_suffix);
- b->auth_size = sizeof(auth_prefix) - 1 + l * 2 + sizeof(auth_suffix) - 1;
-
- return bus_write_auth(b);
-}
-
-static int bus_connect(sd_bus *b) {
- int r;
-
- assert(b);
- assert(b->fd < 0);
- assert(b->sockaddr.sa.sa_family != AF_UNSPEC);
-
- b->fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (b->fd < 0)
- return -errno;
-
- r = bus_setup_fd(b);
- if (r < 0)
- return r;
-
- 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->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);
- }
-
- _exit(EXIT_FAILURE);
- }
-
- close_nointr_nofail(s[1]);
- b->fd = s[0];
-
- return bus_start_auth(b);
-}
-
-static int bus_start_connect(sd_bus *b) {
+static int bus_start_address(sd_bus *b) {
int r;
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) {
- r = bus_connect(b);
+
+ r = bus_socket_connect(b);
if (r >= 0)
return r;
} else if (b->exec_path) {
- r = bus_exec(b);
+ r = bus_socket_exec(b);
if (r >= 0)
return r;
}
}
+int bus_next_address(sd_bus *b) {
+ assert(b);
+
+ bus_reset_parsed_address(b);
+ return bus_start_address(b);
+}
+
static int bus_start_fd(sd_bus *b) {
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;
- r = bus_setup_fd(b);
- if (r < 0)
- return r;
+ 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;
+ }
- return bus_start_auth(b);
+ return bus_socket_take_fd(b);
}
int sd_bus_start(sd_bus *bus) {
bus->state = BUS_OPENING;
- if (bus->fd >= 0)
+ if (bus->is_server && bus->bus_client)
+ return -EINVAL;
+
+ if (bus->input_fd >= 0)
r = bus_start_fd(bus);
- else if (bus->address)
- r = bus_start_connect(bus);
+ else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path)
+ r = bus_start_address(bus);
else
return -EINVAL;
b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/dbus/system_bus_socket") - 1;
}
- b->send_hello = true;
+ b->bus_client = true;
r = sd_bus_start(b);
if (r < 0)
b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l + 4;
}
- b->send_hello = true;
+ b->bus_client = true;
r = sd_bus_start(b);
if (r < 0)
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) {
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) {
if (!bus)
return -EINVAL;
- if (bus->fd < 0)
+ if (bus->output_fd < 0)
return -ENOTCONN;
if (type == SD_BUS_TYPE_UNIX_FD) {
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;
}
return bus_message_seal(m, ++b->serial);
}
-static int message_write(sd_bus *bus, sd_bus_message *m, size_t *idx) {
- struct msghdr mh;
- struct iovec *iov;
- ssize_t k;
- size_t n;
- unsigned j;
+static int dispatch_wqueue(sd_bus *bus) {
+ int r, ret = 0;
assert(bus);
- assert(m);
- assert(idx);
assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
- 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);
- }
+ if (bus->output_fd < 0)
+ return -ENOTCONN;
- n = m->n_iovec * sizeof(struct iovec);
- iov = alloca(n);
- memcpy(iov, m->iovec, n);
+ while (bus->wqueue_size > 0) {
- j = 0;
- iovec_advance(iov, &j, *idx);
+ 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) {
+ /* Fully written. Let's drop the entry from
+ * the queue.
+ *
+ * This isn't particularly optimized, but
+ * well, this is supposed to be our worst-case
+ * buffer only, and the socket buffer is
+ * supposed to be our primary buffer, and if
+ * it got full, then all bets are off
+ * anyway. */
- mh.msg_iov = iov;
- mh.msg_iovlen = m->n_iovec;
+ sd_bus_message_unref(bus->wqueue[0]);
+ bus->wqueue_size --;
+ memmove(bus->wqueue, bus->wqueue + 1, sizeof(sd_bus_message*) * bus->wqueue_size);
+ bus->windex = 0;
- k = sendmsg(bus->fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
- if (k < 0)
- return errno == EAGAIN ? 0 : -errno;
+ ret = 1;
+ }
+ }
- *idx += (size_t) k;
- return 1;
+ return ret;
}
-static int message_read_need(sd_bus *bus, size_t *need) {
- uint32_t a, b;
- uint8_t e;
- uint64_t sum;
+static int dispatch_rqueue(sd_bus *bus, sd_bus_message **m) {
+ sd_bus_message *z = NULL;
+ int r, ret = 0;
assert(bus);
- assert(need);
+ assert(m);
assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
- if (bus->rbuffer_size < sizeof(struct bus_header)) {
- *need = sizeof(struct bus_header) + 8;
-
- /* Minimum message size:
- *
- * Header +
- *
- * Method Call: +2 string headers
- * Signal: +3 string headers
- * Method Error: +1 string headers
- * +1 uint32 headers
- * Method Reply: +1 uint32 headers
- *
- * A string header is at least 9 bytes
- * A uint32 header is at least 8 bytes
- *
- * Hence the minimum message size of a valid message
- * is header + 8 bytes */
+ if (bus->input_fd < 0)
+ return -ENOTCONN;
- return 0;
- }
+ if (bus->rqueue_size > 0) {
+ /* Dispatch a queued message */
- a = ((const uint32_t*) bus->rbuffer)[1];
- b = ((const uint32_t*) bus->rbuffer)[3];
+ *m = bus->rqueue[0];
+ bus->rqueue_size --;
+ memmove(bus->rqueue, bus->rqueue + 1, sizeof(sd_bus_message*) * bus->rqueue_size);
+ return 1;
+ }
- e = ((const uint8_t*) bus->rbuffer)[0];
- if (e == SD_BUS_LITTLE_ENDIAN) {
- a = le32toh(a);
- b = le32toh(b);
- } else if (e == SD_BUS_BIG_ENDIAN) {
- a = be32toh(a);
- b = be32toh(b);
- } else
- return -EBADMSG;
+ /* Try to read a new message */
+ do {
+ r = bus_socket_read_message(bus, &z);
+ if (r < 0) {
+ sd_bus_close(bus);
+ return r;
+ }
+ if (r == 0)
+ return ret;
- sum = (uint64_t) sizeof(struct bus_header) + (uint64_t) ALIGN_TO(b, 8) + (uint64_t) a;
- if (sum >= BUS_MESSAGE_SIZE_MAX)
- return -ENOBUFS;
+ r = 1;
+ } while (!z);
- *need = (size_t) sum;
- return 0;
+ *m = z;
+ return 1;
}
-static int message_make(sd_bus *bus, size_t size, sd_bus_message **m) {
- sd_bus_message *t;
- void *b;
- int r;
-
- assert(bus);
- assert(m);
- assert(bus->rbuffer_size >= size);
- assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
-
- if (bus->rbuffer_size > size) {
- b = memdup((const uint8_t*) bus->rbuffer + size,
- 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);
- if (r < 0) {
- free(b);
- return r;
- }
-
- bus->rbuffer = b;
- bus->rbuffer_size -= size;
-
- bus->fds = NULL;
- bus->n_fds = 0;
-
- *m = t;
- return 1;
-}
-
-static int message_read(sd_bus *bus, sd_bus_message **m) {
- struct msghdr mh;
- struct iovec iov;
- ssize_t k;
- size_t need;
- int r;
- void *b;
- union {
- struct cmsghdr cmsghdr;
- 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;
-
- assert(bus);
- assert(m);
- assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
-
- r = message_read_need(bus, &need);
- if (r < 0)
- return r;
-
- if (bus->rbuffer_size >= need)
- return message_make(bus, need, m);
-
- b = realloc(bus->rbuffer, need);
- if (!b)
- return -ENOMEM;
-
- bus->rbuffer = b;
-
- zero(iov);
- iov.iov_base = (uint8_t*) bus->rbuffer + bus->rbuffer_size;
- iov.iov_len = need - bus->rbuffer_size;
-
- zero(mh);
- mh.msg_iov = &iov;
- mh.msg_iovlen = 1;
- mh.msg_control = &control;
- mh.msg_controllen = sizeof(control);
-
- k = recvmsg(bus->fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
- if (k < 0)
- return errno == EAGAIN ? 0 : -errno;
- if (k == 0)
- return -ECONNRESET;
-
- bus->rbuffer_size += k;
-
- 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))) {
-
- memcpy(&bus->ucred, CMSG_DATA(cmsg), sizeof(struct ucred));
- bus->ucred_valid = true;
-
- } else if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_SECURITY) {
-
- size_t l;
- l = cmsg->cmsg_len - CMSG_LEN(0);
- memcpy(&bus->label, CMSG_DATA(cmsg), l);
- bus->label[l] = 0;
- }
- }
-
- r = message_read_need(bus, &need);
- if (r < 0)
- return r;
-
- if (bus->rbuffer_size >= need)
- return message_make(bus, need, m);
-
- return 1;
-}
-
-static int dispatch_wqueue(sd_bus *bus) {
- int r, ret = 0;
-
- assert(bus);
- assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
-
- if (bus->fd < 0)
- return -ENOTCONN;
-
- while (bus->wqueue_size > 0) {
-
- r = message_write(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) {
- /* Fully written. Let's drop the entry from
- * the queue.
- *
- * This isn't particularly optimized, but
- * well, this is supposed to be our worst-case
- * buffer only, and the socket buffer is
- * supposed to be our primary buffer, and if
- * it got full, then all bets are off
- * anyway. */
-
- sd_bus_message_unref(bus->wqueue[0]);
- bus->wqueue_size --;
- memmove(bus->wqueue, bus->wqueue + 1, sizeof(sd_bus_message*) * bus->wqueue_size);
- bus->windex = 0;
-
- ret = 1;
- }
- }
-
- return ret;
-}
-
-static int dispatch_rqueue(sd_bus *bus, sd_bus_message **m) {
- sd_bus_message *z = NULL;
- int r, ret = 0;
-
- assert(bus);
- assert(m);
- assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
-
- if (bus->fd < 0)
- return -ENOTCONN;
-
- if (bus->rqueue_size > 0) {
- /* Dispatch a queued message */
-
- *m = bus->rqueue[0];
- bus->rqueue_size --;
- memmove(bus->rqueue, bus->rqueue + 1, sizeof(sd_bus_message*) * bus->rqueue_size);
- return 1;
- }
-
- /* Try to read a new message */
- do {
- r = message_read(bus, &z);
- if (r < 0) {
- sd_bus_close(bus);
- return r;
- }
- if (r == 0)
- return ret;
-
- r = 1;
- } while (!z);
-
- *m = z;
- return 1;
-}
-
-int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) {
+int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) {
int r;
if (!bus)
return -EINVAL;
if (bus->state == BUS_UNSET)
return -ENOTCONN;
- if (bus->fd < 0)
+ if (bus->output_fd < 0)
return -ENOTCONN;
if (!m)
return -EINVAL;
if ((bus->state == BUS_RUNNING || bus->state == BUS_HELLO) && bus->wqueue_size <= 0) {
size_t idx = 0;
- r = message_write(bus, m, &idx);
+ r = bus_socket_write_message(bus, m, &idx);
if (r < 0) {
sd_bus_close(bus);
return r;
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) {
return -EINVAL;
if (bus->state == BUS_UNSET)
return -ENOTCONN;
- if (bus->fd < 0)
+ if (bus->output_fd < 0)
return -ENOTCONN;
if (!m)
return -EINVAL;
if (r < 0)
return r;
- c = new(struct reply_callback, 1);
+ c = new0(struct reply_callback, 1);
if (!c)
return -ENOMEM;
assert(bus);
- if (bus->fd < 0)
+ if (bus->input_fd < 0)
return -ENOTCONN;
if (bus->state == BUS_UNSET)
return -ENOTCONN;
if (!bus)
return -EINVAL;
- if (bus->fd < 0)
+ if (bus->output_fd < 0)
return -ENOTCONN;
if (bus->state == BUS_UNSET)
return -ENOTCONN;
room = true;
}
- r = message_read(bus, &incoming);
+ r = bus_socket_read_message(bus, &incoming);
if (r < 0)
return r;
if (incoming) {
/* 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;
}
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) {
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)
flags |= POLLOUT;
else if (bus->state == BUS_AUTHENTICATING) {
- if (bus->auth_index < ELEMENTSOF(bus->auth_iovec))
+ if (bus_socket_auth_needs_write(bus))
flags |= POLLOUT;
flags |= POLLIN;
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) {
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;
return r < 0 ? r : 1;
}
+static int process_hello(sd_bus *bus, sd_bus_message *m) {
+ assert(bus);
+ assert(m);
+
+ if (bus->state != BUS_HELLO)
+ return 0;
+
+ /* Let's make sure the first message on the bus is the HELLO
+ * reply. But note that we don't actually parse the message
+ * here (we leave that to the usual handling), we just verify
+ * we don't let any earlier msg through. */
+
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_RETURN &&
+ m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_ERROR)
+ return -EIO;
+
+ if (m->reply_serial != bus->hello_serial)
+ return -EIO;
+
+ return 0;
+}
+
static int process_reply(sd_bus *bus, sd_bus_message *m) {
struct reply_callback *c;
int r;
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;
}
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",
}
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);
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;
}
- }
- if (!found)
+ /* 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
+ * introspection minimally ourselves */
+ if (!found || sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect"))
return 0;
sd_bus_error_set(&error,
return 1;
}
+static int process_introspect(sd_bus *bus, sd_bus_message *m) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_free_ char *introspection = NULL;
+ _cleanup_set_free_free_ Set *s = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ struct object_callback *c;
+ Iterator i;
+ size_t size = 0;
+ char *node;
+ int r;
+
+ assert(bus);
+ assert(m);
+
+ if (!sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect"))
+ return 0;
+
+ if (!m->path)
+ return 0;
+
+ s = set_new(string_hash_func, string_compare_func);
+ if (!s)
+ return -ENOMEM;
+
+ HASHMAP_FOREACH(c, bus->object_callbacks, i) {
+ const char *e;
+ char *a, *p;
+
+ if (streq(c->path, "/"))
+ continue;
+
+ if (streq(m->path, "/"))
+ e = c->path;
+ else {
+ e = startswith(c->path, m->path);
+ if (!e || *e != '/')
+ continue;
+ }
+
+ a = strdup(e+1);
+ if (!a)
+ return -ENOMEM;
+
+ p = strchr(a, '/');
+ if (p)
+ *p = 0;
+
+ r = set_put(s, a);
+ if (r < 0) {
+ free(a);
+
+ if (r != -EEXIST)
+ return r;
+ }
+ }
+
+ f = open_memstream(&introspection, &size);
+ if (!f)
+ return -ENOMEM;
+
+ fputs(SD_BUS_INTROSPECT_DOCTYPE, f);
+ fputs("<node>\n", f);
+ fputs(SD_BUS_INTROSPECT_INTERFACE_PEER, f);
+ fputs(SD_BUS_INTROSPECT_INTERFACE_INTROSPECTABLE, f);
+
+ while ((node = set_steal_first(s))) {
+ fprintf(f, " <node name=\"%s\"/>\n", node);
+ free(node);
+ }
+
+ fputs("</node>\n", f);
+
+ fflush(f);
+
+ if (ferror(f))
+ return -ENOMEM;
+
+ r = sd_bus_message_new_method_return(bus, m, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "s", introspection);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_send(bus, reply, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
static int process_message(sd_bus *bus, sd_bus_message *m) {
int r;
assert(bus);
assert(m);
+ bus->iteration_counter++;
+
+ r = process_hello(bus, m);
+ if (r != 0)
+ return r;
+
r = process_reply(bus, m);
if (r != 0)
return r;
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;
- return process_object(bus, m);
+ r = process_object(bus, m);
+ if (r != 0)
+ return r;
+
+ return process_introspect(bus, m);
}
-int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
+static int process_running(sd_bus *bus, sd_bus_message **ret) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
int r;
- /* Returns 0 when we didn't do anything. This should cause the
- * caller to invoke sd_bus_wait() before returning the next
- * time. Returns > 0 when we did something, which possibly
- * means *ret is filled in with an unprocessed message. */
+ assert(bus);
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
- if (!bus)
- return -EINVAL;
- if (bus->state == BUS_UNSET)
- return -ENOTCONN;
- if (bus->fd < 0)
- return -ENOTCONN;
+ r = process_timeout(bus);
+ if (r != 0)
+ goto null_message;
- if (bus->state == BUS_OPENING) {
- struct pollfd p;
+ r = dispatch_wqueue(bus);
+ if (r != 0)
+ goto null_message;
- zero(p);
- p.fd = bus->fd;
- p.events = POLLOUT;
+ r = dispatch_rqueue(bus, &m);
+ if (r < 0)
+ return r;
+ if (!m)
+ goto null_message;
- r = poll(&p, 1, 0);
- if (r < 0)
- return -errno;
+ r = process_message(bus, m);
+ if (r != 0)
+ goto null_message;
- if (p.revents & (POLLOUT|POLLERR|POLLHUP)) {
- int error = 0;
- socklen_t slen = sizeof(error);
+ if (ret) {
+ *ret = m;
+ m = NULL;
+ return 1;
+ }
- r = getsockopt(bus->fd, SOL_SOCKET, SO_ERROR, &error, &slen);
- if (r < 0)
- bus->last_connect_error = errno;
- else if (error != 0)
- bus->last_connect_error = error;
- else if (p.revents & (POLLERR|POLLHUP))
- bus->last_connect_error = ECONNREFUSED;
- else {
- r = bus_start_auth(bus);
- goto null_message;
- }
+ 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_NULL;
- /* Try next address */
- bus_reset_parsed_address(bus);
- r = bus_start_connect(bus);
- goto null_message;
- }
+ sd_bus_error_set(&error, "org.freedesktop.DBus.Error.UnknownObject", "Unknown object '%s'.", m->path);
- r = 0;
- goto null_message;
+ r = sd_bus_message_new_method_error(bus, m, &error, &reply);
+ if (r < 0)
+ return r;
- } else if (bus->state == BUS_AUTHENTICATING) {
+ r = sd_bus_send(bus, reply, NULL);
+ if (r < 0)
+ return r;
+ }
- if (now(CLOCK_MONOTONIC) >= bus->auth_timeout)
- return -ETIMEDOUT;
+ return 1;
- r = bus_write_auth(bus);
- if (r != 0)
- goto null_message;
+null_message:
+ if (r >= 0 && ret)
+ *ret = NULL;
- r = bus_read_auth(bus);
- goto null_message;
+ return r;
+}
- } else if (bus->state == BUS_RUNNING || bus->state == BUS_HELLO) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int k;
+int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
+ int r;
- r = process_timeout(bus);
- if (r != 0)
- goto null_message;
+ /* Returns 0 when we didn't do anything. This should cause the
+ * caller to invoke sd_bus_wait() before returning the next
+ * time. Returns > 0 when we did something, which possibly
+ * means *ret is filled in with an unprocessed message. */
- r = dispatch_wqueue(bus);
- if (r != 0)
- goto null_message;
+ if (!bus)
+ return -EINVAL;
+ if (bus->input_fd < 0)
+ return -ENOTCONN;
- k = r;
- r = dispatch_rqueue(bus, &m);
- if (r < 0)
- return r;
- if (!m) {
- if (r == 0)
- r = k;
- goto null_message;
- }
+ /* We don't allow recursively invoking sd_bus_process(). */
+ if (bus->processing)
+ return -EBUSY;
- r = process_message(bus, m);
- if (r != 0)
- goto null_message;
+ switch (bus->state) {
- if (ret) {
- *ret = m;
- m = NULL;
- return 1;
- }
+ case BUS_UNSET:
+ return -ENOTCONN;
- 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;
+ case BUS_OPENING:
+ r = bus_socket_process_opening(bus);
+ if (r < 0)
+ return r;
+ if (ret)
+ *ret = NULL;
+ return r;
- sd_bus_error_set(&error, "org.freedesktop.DBus.Error.UnknownObject", "Unknown object '%s'.", m->path);
+ case BUS_AUTHENTICATING:
- r = sd_bus_message_new_method_error(bus, m, &error, &reply);
- if (r < 0)
- return r;
+ r = bus_socket_process_authenticating(bus);
+ if (r < 0)
+ return r;
+ if (ret)
+ *ret = NULL;
+ return r;
- r = sd_bus_send(bus, reply, NULL);
- if (r < 0)
- return r;
- }
+ case BUS_RUNNING:
+ case BUS_HELLO:
- return 1;
+ bus->processing = true;
+ r = process_running(bus, ret);
+ bus->processing = false;
+
+ return r;
}
assert_not_reached("Unknown state");
-
-null_message:
- if (r >= 0 && ret)
- *ret = NULL;
-
- return r;
}
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);
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;
- r = ppoll(&p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
+ 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, n, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
if (r < 0)
return -errno;
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;
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);
}
}
-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)
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)
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;
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;
if (r < 0)
return r;
- c = new(struct object_callback, 1);
+ c = new0(struct object_callback, 1);
if (!c)
return -ENOMEM;
c->path = strdup(path);
- if (!path) {
+ if (!c->path) {
free(c);
return -ENOMEM;
}
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);
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;
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);
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);
+}