summaryrefslogtreecommitdiff
path: root/src/libsystemd/sd-bus
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2018-01-28 22:49:17 +0100
committerMichael Biebl <biebl@debian.org>2018-01-28 22:49:17 +0100
commit1d42b86df9052528a8f56b2f52d8bc2faf87b2da (patch)
tree0d80f37a1ad6f02067261ee3e7ee62e1869fcd56 /src/libsystemd/sd-bus
parent52ad194e0b816b8273dd8d0fea3e6d467f6ca34e (diff)
downloadsystemd-1d42b86df9052528a8f56b2f52d8bc2faf87b2da.tar.gz
New upstream version 237
Diffstat (limited to 'src/libsystemd/sd-bus')
-rw-r--r--src/libsystemd/sd-bus/bus-bloom.c157
-rw-r--r--src/libsystemd/sd-bus/bus-bloom.h44
-rw-r--r--src/libsystemd/sd-bus/bus-container.c36
-rw-r--r--src/libsystemd/sd-bus/bus-control.c457
-rw-r--r--src/libsystemd/sd-bus/bus-control.h4
-rw-r--r--src/libsystemd/sd-bus/bus-convenience.c70
-rw-r--r--src/libsystemd/sd-bus/bus-gvariant.c5
-rw-r--r--src/libsystemd/sd-bus/bus-internal.c15
-rw-r--r--src/libsystemd/sd-bus/bus-internal.h103
-rw-r--r--src/libsystemd/sd-bus/bus-kernel.c46
-rw-r--r--src/libsystemd/sd-bus/bus-kernel.h2
-rw-r--r--src/libsystemd/sd-bus/bus-message.c18
-rw-r--r--src/libsystemd/sd-bus/bus-message.h4
-rw-r--r--src/libsystemd/sd-bus/bus-objects.c14
-rw-r--r--src/libsystemd/sd-bus/bus-signature.c2
-rw-r--r--src/libsystemd/sd-bus/bus-slot.c13
-rw-r--r--src/libsystemd/sd-bus/bus-socket.c335
-rw-r--r--src/libsystemd/sd-bus/bus-socket.h1
-rw-r--r--src/libsystemd/sd-bus/bus-track.c31
-rw-r--r--src/libsystemd/sd-bus/bus-type.c4
-rw-r--r--src/libsystemd/sd-bus/bus-type.h2
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c968
-rw-r--r--src/libsystemd/sd-bus/test-bus-benchmark.c2
-rw-r--r--src/libsystemd/sd-bus/test-bus-chat.c4
-rw-r--r--src/libsystemd/sd-bus/test-bus-track.c3
-rw-r--r--src/libsystemd/sd-bus/test-bus-watch-bind.c239
26 files changed, 1758 insertions, 821 deletions
diff --git a/src/libsystemd/sd-bus/bus-bloom.c b/src/libsystemd/sd-bus/bus-bloom.c
deleted file mode 100644
index ebda6516e2..0000000000
--- a/src/libsystemd/sd-bus/bus-bloom.c
+++ /dev/null
@@ -1,157 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "bus-bloom.h"
-#include "siphash24.h"
-#include "util.h"
-
-static inline void set_bit(uint64_t filter[], unsigned long b) {
- filter[b >> 6] |= 1ULL << (b & 63);
-}
-
-static const sd_id128_t hash_keys[] = {
- SD_ID128_ARRAY(b9,66,0b,f0,46,70,47,c1,88,75,c4,9c,54,b9,bd,15),
- SD_ID128_ARRAY(aa,a1,54,a2,e0,71,4b,39,bf,e1,dd,2e,9f,c5,4a,3b),
- SD_ID128_ARRAY(63,fd,ae,be,cd,82,48,12,a1,6e,41,26,cb,fa,a0,c8),
- SD_ID128_ARRAY(23,be,45,29,32,d2,46,2d,82,03,52,28,fe,37,17,f5),
- SD_ID128_ARRAY(56,3b,bf,ee,5a,4f,43,39,af,aa,94,08,df,f0,fc,10),
- SD_ID128_ARRAY(31,80,c8,73,c7,ea,46,d3,aa,25,75,0f,9e,4c,09,29),
- SD_ID128_ARRAY(7d,f7,18,4b,7b,a4,44,d5,85,3c,06,e0,65,53,96,6d),
- SD_ID128_ARRAY(f2,77,e9,6f,93,b5,4e,71,9a,0c,34,88,39,25,bf,35),
-};
-
-static void bloom_add_data(
- uint64_t filter[], /* The filter bits */
- size_t size, /* Size of the filter in bytes */
- unsigned k, /* Number of hash functions */
- const void *data, /* Data to hash */
- size_t n) { /* Size of data to hash in bytes */
-
- uint64_t h;
- uint64_t m;
- unsigned w, i, c = 0;
- unsigned hash_index;
-
- assert(size > 0);
- assert(k > 0);
-
- /* Determine bits in filter */
- m = size * 8;
-
- /* Determine how many bytes we need to generate a bit index 0..m for this filter */
- w = (u64log2(m) + 7) / 8;
-
- assert(w <= sizeof(uint64_t));
-
- /* Make sure we have enough hash keys to generate m * k bits
- * of hash value. Note that SipHash24 generates 64 bits of
- * hash value for each 128 bits of hash key. */
- assert(k * w <= ELEMENTSOF(hash_keys) * 8);
-
- for (i = 0, hash_index = 0; i < k; i++) {
- uint64_t p = 0;
- unsigned d;
-
- for (d = 0; d < w; d++) {
- if (c <= 0) {
- h = siphash24(data, n, hash_keys[hash_index++].bytes);
- c += 8;
- }
-
- p = (p << 8ULL) | (uint64_t) ((uint8_t *)&h)[8 - c];
- c--;
- }
-
- p &= m - 1;
- set_bit(filter, p);
- }
-
- /* log_debug("bloom: adding <%.*s>", (int) n, (char*) data); */
-}
-
-void bloom_add_pair(uint64_t filter[], size_t size, unsigned k, const char *a, const char *b) {
- size_t n;
- char *c;
-
- assert(filter);
- assert(a);
- assert(b);
-
- n = strlen(a) + 1 + strlen(b);
- c = alloca(n + 1);
- strcpy(stpcpy(stpcpy(c, a), ":"), b);
-
- bloom_add_data(filter, size, k, c, n);
-}
-
-void bloom_add_prefixes(uint64_t filter[], size_t size, unsigned k, const char *a, const char *b, char sep) {
- size_t n;
- char *c, *p;
-
- assert(filter);
- assert(a);
- assert(b);
-
- n = strlen(a) + 1 + strlen(b);
- c = alloca(n + 1);
-
- p = stpcpy(stpcpy(c, a), ":");
- strcpy(p, b);
-
- bloom_add_data(filter, size, k, c, n);
-
- for (;;) {
- char *e;
-
- e = strrchr(p, sep);
- if (!e)
- break;
-
- *(e + 1) = 0;
- bloom_add_data(filter, size, k, c, e - c + 1);
-
- if (e == p)
- break;
-
- *e = 0;
- bloom_add_data(filter, size, k, c, e - c);
- }
-}
-
-bool bloom_validate_parameters(size_t size, unsigned k) {
- uint64_t m;
- unsigned w;
-
- if (size <= 0)
- return false;
-
- if (k <= 0)
- return false;
-
- m = size * 8;
- w = (u64log2(m) + 7) / 8;
- if (w > sizeof(uint64_t))
- return false;
-
- if (k * w > ELEMENTSOF(hash_keys) * 8)
- return false;
-
- return true;
-}
diff --git a/src/libsystemd/sd-bus/bus-bloom.h b/src/libsystemd/sd-bus/bus-bloom.h
deleted file mode 100644
index 2650762145..0000000000
--- a/src/libsystemd/sd-bus/bus-bloom.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-
-/*
- * Our default bloom filter has the following parameters:
- *
- * m=512 (bits in the filter)
- * k=8 (hash functions)
- *
- * We use SipHash24 as hash function with a number of (originally
- * randomized) but fixed hash keys.
- *
- */
-
-#define DEFAULT_BLOOM_SIZE (512/8) /* m: filter size */
-#define DEFAULT_BLOOM_N_HASH 8 /* k: number of hash functions */
-
-void bloom_add_pair(uint64_t filter[], size_t size, unsigned n_hash, const char *a, const char *b);
-void bloom_add_prefixes(uint64_t filter[], size_t size, unsigned n_hash, const char *a, const char *b, char sep);
-
-bool bloom_validate_parameters(size_t size, unsigned n_hash);
diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c
index 8f6d34838e..16156d8823 100644
--- a/src/libsystemd/sd-bus/bus-container.c
+++ b/src/libsystemd/sd-bus/bus-container.c
@@ -31,9 +31,8 @@
int bus_container_connect_socket(sd_bus *b) {
_cleanup_close_pair_ int pair[2] = { -1, -1 };
_cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
- pid_t child;
- siginfo_t si;
int r, error_buf = 0;
+ pid_t child;
ssize_t n;
assert(b);
@@ -62,11 +61,10 @@ int bus_container_connect_socket(sd_bus *b) {
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
return -errno;
- child = fork();
- if (child < 0)
- return -errno;
-
- if (child == 0) {
+ r = safe_fork("(sd-buscntr)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ if (r < 0)
+ return r;
+ if (r == 0) {
pid_t grandchild;
pair[0] = safe_close(pair[0]);
@@ -82,11 +80,10 @@ int bus_container_connect_socket(sd_bus *b) {
* comes from a process from within the container, and
* not outside of it */
- grandchild = fork();
- if (grandchild < 0)
+ r = safe_fork("(sd-buscntr2)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &grandchild);
+ if (r < 0)
_exit(EXIT_FAILURE);
-
- if (grandchild == 0) {
+ if (r == 0) {
r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
if (r < 0) {
@@ -99,21 +96,20 @@ int bus_container_connect_socket(sd_bus *b) {
_exit(EXIT_SUCCESS);
}
- r = wait_for_terminate(grandchild, &si);
+ r = wait_for_terminate_and_check("(sd-buscntr2)", grandchild, 0);
if (r < 0)
_exit(EXIT_FAILURE);
- if (si.si_code != CLD_EXITED)
- _exit(EXIT_FAILURE);
-
- _exit(si.si_status);
+ _exit(r);
}
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate(child, &si);
+ r = wait_for_terminate_and_check("(sd-buscntr)", child, 0);
if (r < 0)
return r;
+ if (r != EXIT_SUCCESS)
+ return -EPROTO;
n = read(pair[0], &error_buf, sizeof(error_buf));
if (n < 0)
@@ -133,11 +129,5 @@ int bus_container_connect_socket(sd_bus *b) {
return -error_buf;
}
- if (si.si_code != CLD_EXITED)
- return -EIO;
-
- if (si.si_status != EXIT_SUCCESS)
- return -EIO;
-
return bus_socket_start_auth(b);
}
diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c
index 12478e7cc6..9dd5274bf6 100644
--- a/src/libsystemd/sd-bus/bus-control.c
+++ b/src/libsystemd/sd-bus/bus-control.c
@@ -28,12 +28,12 @@
#include "sd-bus.h"
#include "alloc-util.h"
-#include "bus-bloom.h"
#include "bus-control.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-util.h"
#include "capability-util.h"
+#include "process-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
@@ -43,6 +43,7 @@ _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(unique, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -57,13 +58,31 @@ _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
return 0;
}
-static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- uint32_t ret, param = 0;
- int r;
+static int validate_request_name_parameters(
+ sd_bus *bus,
+ const char *name,
+ uint64_t flags,
+ uint32_t *ret_param) {
+
+ uint32_t param = 0;
assert(bus);
assert(name);
+ assert(ret_param);
+
+ assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL);
+ assert_return(service_name_is_valid(name), -EINVAL);
+ assert_return(name[0] != ':', -EINVAL);
+
+ if (!bus->bus_client)
+ return -EINVAL;
+
+ /* Don't allow requesting the special driver and local names */
+ if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
+ return -EINVAL;
+
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT)
param |= BUS_NAME_ALLOW_REPLACEMENT;
@@ -72,6 +91,29 @@ static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags)
if (!(flags & SD_BUS_NAME_QUEUE))
param |= BUS_NAME_DO_NOT_QUEUE;
+ *ret_param = param;
+
+ return 0;
+}
+
+_public_ int sd_bus_request_name(
+ sd_bus *bus,
+ const char *name,
+ uint64_t flags) {
+
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ uint32_t ret, param = 0;
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(name, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = validate_request_name_parameters(bus, name, flags, &param);
+ if (r < 0)
+ return r;
+
r = sd_bus_call_method(
bus,
"org.freedesktop.DBus",
@@ -90,46 +132,145 @@ static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags)
if (r < 0)
return r;
- if (ret == BUS_NAME_ALREADY_OWNER)
+ switch (ret) {
+
+ case BUS_NAME_ALREADY_OWNER:
return -EALREADY;
- else if (ret == BUS_NAME_EXISTS)
+
+ case BUS_NAME_EXISTS:
return -EEXIST;
- else if (ret == BUS_NAME_IN_QUEUE)
+
+ case BUS_NAME_IN_QUEUE:
return 0;
- else if (ret == BUS_NAME_PRIMARY_OWNER)
+
+ case BUS_NAME_PRIMARY_OWNER:
return 1;
+ }
return -EIO;
}
-_public_ int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) {
+static int default_request_name_handler(
+ sd_bus_message *m,
+ void *userdata,
+ sd_bus_error *ret_error) {
+
+ uint32_t ret;
+ int r;
+
+ assert(m);
+
+ if (sd_bus_message_is_method_error(m, NULL)) {
+ log_debug_errno(sd_bus_message_get_errno(m),
+ "Unable to request name, failing connection: %s",
+ sd_bus_message_get_error(m)->message);
+
+ bus_enter_closing(sd_bus_message_get_bus(m));
+ return 1;
+ }
+
+ r = sd_bus_message_read(m, "u", &ret);
+ if (r < 0)
+ return r;
+
+ switch (ret) {
+
+ case BUS_NAME_ALREADY_OWNER:
+ log_debug("Already owner of requested service name, ignoring.");
+ return 1;
+
+ case BUS_NAME_IN_QUEUE:
+ log_debug("In queue for requested service name.");
+ return 1;
+
+ case BUS_NAME_PRIMARY_OWNER:
+ log_debug("Successfully acquired requested service name.");
+ return 1;
+
+ case BUS_NAME_EXISTS:
+ log_debug("Requested service name already owned, failing connection.");
+ bus_enter_closing(sd_bus_message_get_bus(m));
+ return 1;
+ }
+
+ log_debug("Unexpected response from RequestName(), failing connection.");
+ bus_enter_closing(sd_bus_message_get_bus(m));
+ return 1;
+}
+
+_public_ int sd_bus_request_name_async(
+ sd_bus *bus,
+ sd_bus_slot **ret_slot,
+ const char *name,
+ uint64_t flags,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ uint32_t param = 0;
+ int r;
+
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(name, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
- assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL);
+
+ r = validate_request_name_parameters(bus, name, flags, &param);
+ if (r < 0)
+ return r;
+
+ return sd_bus_call_method_async(
+ bus,
+ ret_slot,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "RequestName",
+ callback ?: default_request_name_handler,
+ userdata,
+ "su",
+ name,
+ param);
+}
+
+static int validate_release_name_parameters(
+ sd_bus *bus,
+ const char *name) {
+
+ assert(bus);
+ assert(name);
+
assert_return(service_name_is_valid(name), -EINVAL);
assert_return(name[0] != ':', -EINVAL);
if (!bus->bus_client)
return -EINVAL;
- /* Don't allow requesting the special driver and local names */
+ /* Don't allow releasing the special driver and local names */
if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
return -EINVAL;
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
- return bus_request_name_dbus1(bus, name, flags);
+ return 0;
}
-static int bus_release_name_dbus1(sd_bus *bus, const char *name) {
+_public_ int sd_bus_release_name(
+ sd_bus *bus,
+ const char *name) {
+
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
uint32_t ret;
int r;
- assert(bus);
- assert(name);
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(name, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ r = validate_release_name_parameters(bus, name);
+ if (r < 0)
+ return r;
r = sd_bus_call_method(
bus,
@@ -147,41 +288,112 @@ static int bus_release_name_dbus1(sd_bus *bus, const char *name) {
r = sd_bus_message_read(reply, "u", &ret);
if (r < 0)
return r;
- if (ret == BUS_NAME_NON_EXISTENT)
+
+ switch (ret) {
+
+ case BUS_NAME_NON_EXISTENT:
return -ESRCH;
- if (ret == BUS_NAME_NOT_OWNER)
+
+ case BUS_NAME_NOT_OWNER:
return -EADDRINUSE;
- if (ret == BUS_NAME_RELEASED)
+
+ case BUS_NAME_RELEASED:
return 0;
+ }
+
+ return -EIO;
+}
+
+static int default_release_name_handler(
+ sd_bus_message *m,
+ void *userdata,
+ sd_bus_error *ret_error) {
+
+ uint32_t ret;
+ int r;
+
+ assert(m);
+
+ if (sd_bus_message_is_method_error(m, NULL)) {
+ log_debug_errno(sd_bus_message_get_errno(m),
+ "Unable to release name, failing connection: %s",
+ sd_bus_message_get_error(m)->message);
+
+ bus_enter_closing(sd_bus_message_get_bus(m));
+ return 1;
+ }
- return -EINVAL;
+ r = sd_bus_message_read(m, "u", &ret);
+ if (r < 0)
+ return r;
+
+ switch (ret) {
+
+ case BUS_NAME_NON_EXISTENT:
+ log_debug("Name asked to release is not taken currently, ignoring.");
+ return 1;
+
+ case BUS_NAME_NOT_OWNER:
+ log_debug("Name asked to release is owned by somebody else, ignoring.");
+ return 1;
+
+ case BUS_NAME_RELEASED:
+ log_debug("Name successfully released.");
+ return 1;
+ }
+
+ log_debug("Unexpected response from ReleaseName(), failing connection.");
+ bus_enter_closing(sd_bus_message_get_bus(m));
+ return 1;
}
-_public_ int sd_bus_release_name(sd_bus *bus, const char *name) {
+_public_ int sd_bus_release_name_async(
+ sd_bus *bus,
+ sd_bus_slot **ret_slot,
+ const char *name,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ int r;
+
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(name, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
- assert_return(service_name_is_valid(name), -EINVAL);
- assert_return(name[0] != ':', -EINVAL);
- if (!bus->bus_client)
- return -EINVAL;
-
- /* Don't allow releasing the special driver and local names */
- if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
- return -EINVAL;
-
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
+ r = validate_release_name_parameters(bus, name);
+ if (r < 0)
+ return r;
- return bus_release_name_dbus1(bus, name);
+ return sd_bus_call_method_async(
+ bus,
+ ret_slot,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "ReleaseName",
+ callback ?: default_release_name_handler,
+ userdata,
+ "s",
+ name);
}
-static int bus_list_names_dbus1(sd_bus *bus, char ***acquired, char ***activatable) {
+_public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_strv_free_ char **x = NULL, **y = NULL;
int r;
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(acquired || activatable, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ if (!bus->bus_client)
+ return -EINVAL;
+
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
+
if (acquired) {
r = sd_bus_call_method(
bus,
@@ -231,21 +443,7 @@ static int bus_list_names_dbus1(sd_bus *bus, char ***acquired, char ***activatab
return 0;
}
-_public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) {
- assert_return(bus, -EINVAL);
- assert_return(acquired || activatable, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- if (!bus->bus_client)
- return -EINVAL;
-
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
-
- return bus_list_names_dbus1(bus, acquired, activatable);
-}
-
-static int bus_get_name_creds_dbus1(
+_public_ int sd_bus_get_name_creds(
sd_bus *bus,
const char *name,
uint64_t mask,
@@ -257,6 +455,31 @@ static int bus_get_name_creds_dbus1(
pid_t pid = 0;
int r;
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(name, -EINVAL);
+ assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
+ assert_return(mask == 0 || creds, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(service_name_is_valid(name), -EINVAL);
+
+ if (!bus->bus_client)
+ return -EINVAL;
+
+ /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not
+ * going to match. */
+ if (!bus->is_local)
+ mask &= ~SD_BUS_CREDS_AUGMENT;
+
+ if (streq(name, "org.freedesktop.DBus.Local"))
+ return -EINVAL;
+
+ if (streq(name, "org.freedesktop.DBus"))
+ return sd_bus_get_owner_creds(bus, mask, creds);
+
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
+
/* Only query the owner if the caller wants to know it or if
* the caller just wants to check whether a name exists */
if ((mask & SD_BUS_CREDS_UNIQUE_NAME) || mask == 0) {
@@ -519,51 +742,29 @@ static int bus_get_name_creds_dbus1(
return 0;
}
-_public_ int sd_bus_get_name_creds(
- sd_bus *bus,
- const char *name,
- uint64_t mask,
- sd_bus_creds **creds) {
+_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
+ bool do_label, do_groups;
+ pid_t pid = 0;
+ int r;
assert_return(bus, -EINVAL);
- assert_return(name, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
- assert_return(mask == 0 || creds, -EINVAL);
+ assert_return(ret, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
- assert_return(service_name_is_valid(name), -EINVAL);
-
- if (!bus->bus_client)
- return -EINVAL;
-
- /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not
- * going to match. */
- if (!bus->is_local)
- mask &= ~SD_BUS_CREDS_AUGMENT;
-
- if (streq(name, "org.freedesktop.DBus.Local"))
- return -EINVAL;
-
- if (streq(name, "org.freedesktop.DBus"))
- return sd_bus_get_owner_creds(bus, mask, creds);
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
- return bus_get_name_creds_dbus1(bus, name, mask, creds);
-}
-
-static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
- _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
- pid_t pid = 0;
- bool do_label;
- int r;
-
- assert(bus);
+ if (!bus->is_local)
+ mask &= ~SD_BUS_CREDS_AUGMENT;
do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT);
+ do_groups = bus->n_groups != (size_t) -1 && (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS);
/* Avoid allocating anything if we have no chance of returning useful data */
- if (!bus->ucred_valid && !do_label)
+ if (!bus->ucred_valid && !do_label && !do_groups)
return -ENODATA;
c = bus_creds_new();
@@ -571,17 +772,17 @@ static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **
return -ENOMEM;
if (bus->ucred_valid) {
- if (bus->ucred.pid > 0) {
+ if (pid_is_valid(bus->ucred.pid)) {
pid = c->pid = bus->ucred.pid;
c->mask |= SD_BUS_CREDS_PID & mask;
}
- if (bus->ucred.uid != UID_INVALID) {
+ if (uid_is_valid(bus->ucred.uid)) {
c->euid = bus->ucred.uid;
c->mask |= SD_BUS_CREDS_EUID & mask;
}
- if (bus->ucred.gid != GID_INVALID) {
+ if (gid_is_valid(bus->ucred.gid)) {
c->egid = bus->ucred.gid;
c->mask |= SD_BUS_CREDS_EGID & mask;
}
@@ -595,6 +796,16 @@ static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **
c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
}
+ if (do_groups) {
+ c->supplementary_gids = newdup(gid_t, bus->groups, bus->n_groups);
+ if (!c->supplementary_gids)
+ return -ENOMEM;
+
+ c->n_supplementary_gids = bus->n_groups;
+
+ c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS;
+ }
+
r = bus_creds_add_more(c, mask, pid, 0);
if (r < 0)
return r;
@@ -604,36 +815,23 @@ static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **
return 0;
}
-_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
- assert_return(bus, -EINVAL);
- assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
- assert_return(ret, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
-
- if (!bus->is_local)
- mask &= ~SD_BUS_CREDS_AUGMENT;
-
- return bus_get_owner_creds_dbus1(bus, mask, ret);
-}
-
-#define internal_match(bus, m) \
- ((bus)->hello_flags & KDBUS_HELLO_MONITOR \
+#define append_eavesdrop(bus, m) \
+ ((bus)->is_monitor \
? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \
: (m))
-static int bus_add_match_internal_dbus1(
+int bus_add_match_internal(
sd_bus *bus,
const char *match) {
const char *e;
assert(bus);
- assert(match);
- e = internal_match(bus, match);
+ if (!bus->bus_client)
+ return -EINVAL;
+
+ e = append_eavesdrop(bus, match);
return sd_bus_call_method(
bus,
@@ -646,22 +844,36 @@ static int bus_add_match_internal_dbus1(
"s",
e);
}
-
-int bus_add_match_internal(
+int bus_add_match_internal_async(
sd_bus *bus,
+ sd_bus_slot **ret_slot,
const char *match,
- struct bus_match_component *components,
- unsigned n_components) {
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ const char *e;
assert(bus);
if (!bus->bus_client)
return -EINVAL;
- return bus_add_match_internal_dbus1(bus, match);
+ e = append_eavesdrop(bus, match);
+
+ return sd_bus_call_method_async(
+ bus,
+ ret_slot,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "AddMatch",
+ callback,
+ userdata,
+ "s",
+ e);
}
-static int bus_remove_match_internal_dbus1(
+int bus_remove_match_internal(
sd_bus *bus,
const char *match) {
@@ -670,10 +882,16 @@ static int bus_remove_match_internal_dbus1(
assert(bus);
assert(match);
- e = internal_match(bus, match);
+ if (!bus->bus_client)
+ return -EINVAL;
- return sd_bus_call_method(
+ e = append_eavesdrop(bus, match);
+
+ /* Fire and forget */
+
+ return sd_bus_call_method_async(
bus,
+ NULL,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
@@ -684,24 +902,13 @@ static int bus_remove_match_internal_dbus1(
e);
}
-int bus_remove_match_internal(
- sd_bus *bus,
- const char *match) {
-
- assert(bus);
-
- if (!bus->bus_client)
- return -EINVAL;
-
- return bus_remove_match_internal_dbus1(bus, match);
-}
-
_public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
const char *mid;
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(name, -EINVAL);
assert_return(machine, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
diff --git a/src/libsystemd/sd-bus/bus-control.h b/src/libsystemd/sd-bus/bus-control.h
index c9d434c607..3d9acebaf6 100644
--- a/src/libsystemd/sd-bus/bus-control.h
+++ b/src/libsystemd/sd-bus/bus-control.h
@@ -22,7 +22,7 @@
#include "sd-bus.h"
-#include "bus-match.h"
+int bus_add_match_internal(sd_bus *bus, const char *match);
+int bus_add_match_internal_async(sd_bus *bus, sd_bus_slot **ret, const char *match, sd_bus_message_handler_t callback, void *userdata);
-int bus_add_match_internal(sd_bus *bus, const char *match, struct bus_match_component *components, unsigned n_components);
int bus_remove_match_internal(sd_bus *bus, const char *match);
diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c
index 9d3b596429..8da6640ca0 100644
--- a/src/libsystemd/sd-bus/bus-convenience.c
+++ b/src/libsystemd/sd-bus/bus-convenience.c
@@ -36,6 +36,7 @@ _public_ int sd_bus_emit_signal(
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
if (!BUS_IS_OPEN(bus->state))
@@ -73,6 +74,7 @@ _public_ int sd_bus_call_method_async(
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
if (!BUS_IS_OPEN(bus->state))
@@ -615,3 +617,71 @@ _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability)
return 0;
}
+
+#define make_expression(sender, path, interface, member) \
+ strjoina( \
+ "type='signal'", \
+ sender ? ",sender='" : "", \
+ sender ?: "", \
+ sender ? "'" : "", \
+ path ? ",path='" : "", \
+ path ?: "", \
+ path ? "'" : "", \
+ interface ? ",interface='" : "", \
+ interface ?: "", \
+ interface ? "'" : "", \
+ member ? ",member='" : "", \
+ member ?: "", \
+ member ? "'" : "" \
+ )
+
+_public_ int sd_bus_match_signal(
+ sd_bus *bus,
+ sd_bus_slot **ret,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ const char *expression;
+
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(!sender || service_name_is_valid(sender), -EINVAL);
+ assert_return(!path || object_path_is_valid(path), -EINVAL);
+ assert_return(!interface || interface_name_is_valid(interface), -EINVAL);
+ assert_return(!member || member_name_is_valid(member), -EINVAL);
+
+ expression = make_expression(sender, path, interface, member);
+
+ return sd_bus_add_match(bus, ret, expression, callback, userdata);
+}
+
+_public_ int sd_bus_match_signal_async(
+ sd_bus *bus,
+ sd_bus_slot **ret,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_message_handler_t callback,
+ sd_bus_message_handler_t install_callback,
+ void *userdata) {
+
+ const char *expression;
+
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(!sender || service_name_is_valid(sender), -EINVAL);
+ assert_return(!path || object_path_is_valid(path), -EINVAL);
+ assert_return(!interface || interface_name_is_valid(interface), -EINVAL);
+ assert_return(!member || member_name_is_valid(member), -EINVAL);
+
+ expression = make_expression(sender, path, interface, member);
+
+ return sd_bus_add_match_async(bus, ret, expression, callback, install_callback, userdata);
+}
diff --git a/src/libsystemd/sd-bus/bus-gvariant.c b/src/libsystemd/sd-bus/bus-gvariant.c
index 6a990a02c0..e6ab984d1f 100644
--- a/src/libsystemd/sd-bus/bus-gvariant.c
+++ b/src/libsystemd/sd-bus/bus-gvariant.c
@@ -18,6 +18,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <string.h>
+
+#include "sd-bus.h"
+
#include "bus-gvariant.h"
#include "bus-signature.h"
#include "bus-type.h"
diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c
index 3c381b0ffe..05a022fbf3 100644
--- a/src/libsystemd/sd-bus/bus-internal.c
+++ b/src/libsystemd/sd-bus/bus-internal.c
@@ -362,13 +362,18 @@ int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) {
} else
return r;
- log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s",
+ log_debug("Failed to process message type=%s sender=%s destination=%s path=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " signature=%s error-name=%s error-message=%s: %s",
bus_message_type_to_string(m->header->type),
- strna(m->sender),
- strna(m->path),
- strna(m->interface),
- strna(m->member),
+ strna(sd_bus_message_get_sender(m)),
+ strna(sd_bus_message_get_destination(m)),
+ strna(sd_bus_message_get_path(m)),
+ strna(sd_bus_message_get_interface(m)),
+ strna(sd_bus_message_get_member(m)),
+ BUS_MESSAGE_COOKIE(m),
+ m->reply_cookie,
strna(m->root_container.signature),
+ strna(m->error.name),
+ strna(m->error.message),
bus_error_message(error, r));
return 1;
diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h
index 378c408ea3..1b55cdafed 100644
--- a/src/libsystemd/sd-bus/bus-internal.h
+++ b/src/libsystemd/sd-bus/bus-internal.h
@@ -38,7 +38,7 @@
struct reply_callback {
sd_bus_message_handler_t callback;
- usec_t timeout;
+ usec_t timeout_usec; /* this is a relative timeout until we reach the BUS_HELLO state, and an absolute one right after */
uint64_t cookie;
unsigned prioq_idx;
};
@@ -53,6 +53,9 @@ struct filter_callback {
struct match_callback {
sd_bus_message_handler_t callback;
+ sd_bus_message_handler_t install_callback;
+
+ sd_bus_slot *install_slot; /* The AddMatch() call */
unsigned last_iteration;
@@ -157,12 +160,14 @@ struct sd_bus_slot {
enum bus_state {
BUS_UNSET,
- BUS_OPENING,
- BUS_AUTHENTICATING,
- BUS_HELLO,
+ BUS_WATCH_BIND, /* waiting for the socket to appear via inotify */
+ BUS_OPENING, /* the kernel's connect() is still not ready */
+ BUS_AUTHENTICATING, /* we are currently in the "SASL" authorization phase of dbus */
+ BUS_HELLO, /* we are waiting for the Hello() response */
BUS_RUNNING,
BUS_CLOSING,
- BUS_CLOSED
+ BUS_CLOSED,
+ _BUS_STATE_MAX,
};
static inline bool BUS_IS_OPEN(enum bus_state state) {
@@ -188,6 +193,7 @@ struct sd_bus {
enum bus_state state;
int input_fd, output_fd;
+ int inotify_fd;
int message_version;
int message_endian;
@@ -210,6 +216,11 @@ struct sd_bus {
bool exited:1;
bool exit_triggered:1;
bool is_local:1;
+ bool watch_bind:1;
+ bool is_monitor:1;
+ bool accept_fd:1;
+ bool attach_timestamp:1;
+ bool connected_signal:1;
int use_memfd;
@@ -261,6 +272,8 @@ struct sd_bus {
struct ucred ucred;
char *label;
+ gid_t *groups;
+ size_t n_groups;
uint64_t creds_mask;
@@ -284,13 +297,11 @@ struct sd_bus {
pid_t original_pid;
- uint64_t hello_flags;
- uint64_t attach_flags;
-
sd_event_source *input_io_event_source;
sd_event_source *output_io_event_source;
sd_event_source *time_event_source;
sd_event_source *quit_event_source;
+ sd_event_source *inotify_event_source;
sd_event *event;
int event_priority;
@@ -305,11 +316,15 @@ struct sd_bus {
char *cgroup_root;
char *description;
+ char *patch_sender;
sd_bus_track *track_queue;
LIST_HEAD(sd_bus_slot, slots);
LIST_HEAD(sd_bus_track, tracks);
+
+ int *inotify_watches;
+ size_t n_inotify_watches;
};
/* For method calls we time-out at 25s, like in the D-Bus reference implementation */
@@ -353,6 +368,8 @@ const char *bus_message_type_to_string(uint8_t u) _pure_;
#define error_name_is_valid interface_name_is_valid
+sd_bus *bus_resolve(sd_bus *bus);
+
int bus_ensure_running(sd_bus *bus);
int bus_start_running(sd_bus *bus);
int bus_next_address(sd_bus *bus);
@@ -365,6 +382,12 @@ bool bus_pid_changed(sd_bus *bus);
char *bus_address_escape(const char *v);
+int bus_attach_io_events(sd_bus *b);
+int bus_attach_inotify_event(sd_bus *b);
+
+void bus_close_inotify_fd(sd_bus *b);
+void bus_close_io_fds(sd_bus *b);
+
#define OBJECT_PATH_FOREACH_PREFIX(prefix, path) \
for (char *_slash = ({ strcpy((prefix), (path)); streq((prefix), "/") ? NULL : strrchr((prefix), '/'); }) ; \
_slash && !(_slash[(_slash) == (prefix)] = 0); \
@@ -381,8 +404,6 @@ int bus_set_address_user(sd_bus *bus);
int bus_set_address_system_remote(sd_bus *b, const char *host);
int bus_set_address_system_machine(sd_bus *b, const char *machine);
-int bus_remove_match_by_string(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata);
-
int bus_get_root_path(sd_bus *bus);
int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error);
@@ -393,64 +414,6 @@ int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error);
return sd_bus_error_set_errno(error, r); \
} while (false)
-/**
- * enum kdbus_attach_flags - flags for metadata attachments
- * @KDBUS_ATTACH_TIMESTAMP: Timestamp
- * @KDBUS_ATTACH_CREDS: Credentials
- * @KDBUS_ATTACH_PIDS: PIDs
- * @KDBUS_ATTACH_AUXGROUPS: Auxiliary groups
- * @KDBUS_ATTACH_NAMES: Well-known names
- * @KDBUS_ATTACH_TID_COMM: The "comm" process identifier of the TID
- * @KDBUS_ATTACH_PID_COMM: The "comm" process identifier of the PID
- * @KDBUS_ATTACH_EXE: The path of the executable
- * @KDBUS_ATTACH_CMDLINE: The process command line
- * @KDBUS_ATTACH_CGROUP: The croup membership
- * @KDBUS_ATTACH_CAPS: The process capabilities
- * @KDBUS_ATTACH_SECLABEL: The security label
- * @KDBUS_ATTACH_AUDIT: The audit IDs
- * @KDBUS_ATTACH_CONN_DESCRIPTION: The human-readable connection name
- * @_KDBUS_ATTACH_ALL: All of the above
- * @_KDBUS_ATTACH_ANY: Wildcard match to enable any kind of
- * metatdata.
- */
-enum kdbus_attach_flags {
- KDBUS_ATTACH_TIMESTAMP = 1ULL << 0,
- KDBUS_ATTACH_CREDS = 1ULL << 1,
- KDBUS_ATTACH_PIDS = 1ULL << 2,
- KDBUS_ATTACH_AUXGROUPS = 1ULL << 3,
- KDBUS_ATTACH_NAMES = 1ULL << 4,
- KDBUS_ATTACH_TID_COMM = 1ULL << 5,
- KDBUS_ATTACH_PID_COMM = 1ULL << 6,
- KDBUS_ATTACH_EXE = 1ULL << 7,
- KDBUS_ATTACH_CMDLINE = 1ULL << 8,
- KDBUS_ATTACH_CGROUP = 1ULL << 9,
- KDBUS_ATTACH_CAPS = 1ULL << 10,
- KDBUS_ATTACH_SECLABEL = 1ULL << 11,
- KDBUS_ATTACH_AUDIT = 1ULL << 12,
- KDBUS_ATTACH_CONN_DESCRIPTION = 1ULL << 13,
- _KDBUS_ATTACH_ALL = (1ULL << 14) - 1,
- _KDBUS_ATTACH_ANY = ~0ULL
-};
+void bus_enter_closing(sd_bus *bus);
-/**
- * enum kdbus_hello_flags - flags for struct kdbus_cmd_hello
- * @KDBUS_HELLO_ACCEPT_FD: The connection allows the reception of
- * any passed file descriptors
- * @KDBUS_HELLO_ACTIVATOR: Special-purpose connection which registers
- * a well-know name for a process to be started
- * when traffic arrives
- * @KDBUS_HELLO_POLICY_HOLDER: Special-purpose connection which registers
- * policy entries for a name. The provided name
- * is not activated and not registered with the
- * name database, it only allows unprivileged
- * connections to acquire a name, talk or discover
- * a service
- * @KDBUS_HELLO_MONITOR: Special-purpose connection to monitor
- * bus traffic
- */
-enum kdbus_hello_flags {
- KDBUS_HELLO_ACCEPT_FD = 1ULL << 0,
- KDBUS_HELLO_ACTIVATOR = 1ULL << 1,
- KDBUS_HELLO_POLICY_HOLDER = 1ULL << 2,
- KDBUS_HELLO_MONITOR = 1ULL << 3,
-};
+void bus_set_state(sd_bus *bus, enum bus_state state);
diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c
index c6179b4d95..b27b9d7d86 100644
--- a/src/libsystemd/sd-bus/bus-kernel.c
+++ b/src/libsystemd/sd-bus/bus-kernel.c
@@ -66,49 +66,3 @@ void bus_flush_memfd(sd_bus *b) {
for (i = 0; i < b->n_memfd_cache; i++)
close_and_munmap(b->memfd_cache[i].fd, b->memfd_cache[i].address, b->memfd_cache[i].mapped);
}
-
-uint64_t attach_flags_to_kdbus(uint64_t mask) {
- uint64_t m = 0;
-
- if (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
- SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID))
- m |= KDBUS_ATTACH_CREDS;
-
- if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID))
- m |= KDBUS_ATTACH_PIDS;
-
- if (mask & SD_BUS_CREDS_COMM)
- m |= KDBUS_ATTACH_PID_COMM;
-
- if (mask & SD_BUS_CREDS_TID_COMM)
- m |= KDBUS_ATTACH_TID_COMM;
-
- if (mask & SD_BUS_CREDS_EXE)
- m |= KDBUS_ATTACH_EXE;
-
- if (mask & SD_BUS_CREDS_CMDLINE)
- m |= KDBUS_ATTACH_CMDLINE;
-
- if (mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID))
- m |= KDBUS_ATTACH_CGROUP;
-
- if (mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS))
- m |= KDBUS_ATTACH_CAPS;
-
- if (mask & SD_BUS_CREDS_SELINUX_CONTEXT)
- m |= KDBUS_ATTACH_SECLABEL;
-
- if (mask & (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))
- m |= KDBUS_ATTACH_AUDIT;
-
- if (mask & SD_BUS_CREDS_WELL_KNOWN_NAMES)
- m |= KDBUS_ATTACH_NAMES;
-
- if (mask & SD_BUS_CREDS_DESCRIPTION)
- m |= KDBUS_ATTACH_CONN_DESCRIPTION;
-
- if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS)
- m |= KDBUS_ATTACH_AUXGROUPS;
-
- return m;
-}
diff --git a/src/libsystemd/sd-bus/bus-kernel.h b/src/libsystemd/sd-bus/bus-kernel.h
index d9f80935fe..fa78e5c80d 100644
--- a/src/libsystemd/sd-bus/bus-kernel.h
+++ b/src/libsystemd/sd-bus/bus-kernel.h
@@ -41,5 +41,3 @@ struct memfd_cache {
void close_and_munmap(int fd, void *address, size_t size);
void bus_flush_memfd(sd_bus *bus);
-
-uint64_t attach_flags_to_kdbus(uint64_t sd_bus_flags);
diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c
index 219cff1f6e..95a87da08b 100644
--- a/src/libsystemd/sd-bus/bus-message.c
+++ b/src/libsystemd/sd-bus/bus-message.c
@@ -127,7 +127,6 @@ static void message_free(sd_bus_message *m) {
if (m->iovec != m->iovec_fixed)
free(m->iovec);
- m->destination_ptr = mfree(m->destination_ptr);
message_reset_containers(m);
free(m->root_container.signature);
free(m->root_container.offsets);
@@ -1321,7 +1320,9 @@ static void *message_extend_body(
m->n_body_parts <= 0 ||
m->body_end->sealed ||
(padding != ALIGN_TO(m->body_end->size, align) - m->body_end->size) ||
- (force_inline && m->body_end->size > MEMFD_MIN_SIZE); /* if this must be an inlined extension, let's create a new part if the previous part is large enough to be inlined */
+ (force_inline && m->body_end->size > MEMFD_MIN_SIZE);
+ /* If this must be an inlined extension, let's create a new part if
+ * the previous part is large enough to be inlined. */
if (add_new_part) {
if (padding > 0) {
@@ -1368,7 +1369,7 @@ static void *message_extend_body(
}
} else
/* Return something that is not NULL and is aligned */
- p = (uint8_t *) NULL + align;
+ p = (uint8_t*) align;
m->body_size = end_body;
message_extend_containers(m, added);
@@ -4779,7 +4780,7 @@ _public_ int sd_bus_message_read_array(
if (sz == 0)
/* Zero length array, let's return some aligned
* pointer that is not NULL */
- p = (uint8_t*) NULL + align;
+ p = (uint8_t*) align;
else {
r = message_peek_body(m, &m->rindex, align, sz, &p);
if (r < 0)
@@ -5488,6 +5489,15 @@ _public_ int sd_bus_message_set_destination(sd_bus_message *m, const char *desti
return message_append_field_string(m, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &m->destination);
}
+_public_ int sd_bus_message_set_sender(sd_bus_message *m, const char *sender) {
+ assert_return(m, -EINVAL);
+ assert_return(sender, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(!m->sender, -EEXIST);
+
+ return message_append_field_string(m, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, sender, &m->sender);
+}
+
int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz) {
size_t total;
void *p, *e;
diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h
index 1e4b20926d..88998700d6 100644
--- a/src/libsystemd/sd-bus/bus-message.h
+++ b/src/libsystemd/sd-bus/bus-message.h
@@ -136,10 +136,6 @@ struct sd_bus_message {
usec_t timeout;
- char sender_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1];
- char destination_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1];
- char *destination_ptr;
-
size_t header_offsets[_BUS_MESSAGE_HEADER_MAX];
unsigned n_header_offsets;
};
diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c
index 121197bbcb..6e00255b20 100644
--- a/src/libsystemd/sd-bus/bus-objects.c
+++ b/src/libsystemd/sd-bus/bus-objects.c
@@ -1369,7 +1369,7 @@ int bus_process_object(sd_bus *bus, sd_bus_message *m) {
assert(bus);
assert(m);
- if (bus->hello_flags & KDBUS_HELLO_MONITOR)
+ if (bus->is_monitor)
return 0;
if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
@@ -1547,6 +1547,7 @@ static int bus_add_object(
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(callback, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -1650,6 +1651,7 @@ static int add_object_vtable_internal(
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(interface_name_is_valid(interface), -EINVAL);
assert_return(vtable, -EINVAL);
@@ -1859,6 +1861,7 @@ _public_ int sd_bus_add_node_enumerator(
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(callback, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2110,6 +2113,7 @@ _public_ int sd_bus_emit_properties_changed_strv(
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(interface_name_is_valid(interface), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2156,6 +2160,7 @@ _public_ int sd_bus_emit_properties_changed(
char **names;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(interface_name_is_valid(interface), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2340,6 +2345,7 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
*/
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2510,6 +2516,7 @@ _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
*/
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2663,6 +2670,7 @@ _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, ch
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2729,6 +2737,7 @@ _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const c
char **interfaces;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2746,6 +2755,7 @@ _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path,
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2780,6 +2790,7 @@ _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const
char **interfaces;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2797,6 +2808,7 @@ _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const ch
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(object_path_is_valid(path), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
diff --git a/src/libsystemd/sd-bus/bus-signature.c b/src/libsystemd/sd-bus/bus-signature.c
index d16461f4ae..f3cd9bd0fa 100644
--- a/src/libsystemd/sd-bus/bus-signature.c
+++ b/src/libsystemd/sd-bus/bus-signature.c
@@ -20,6 +20,8 @@
#include <util.h>
+#include "sd-bus.h"
+
#include "bus-signature.h"
#include "bus-type.h"
diff --git a/src/libsystemd/sd-bus/bus-slot.c b/src/libsystemd/sd-bus/bus-slot.c
index 756761c3ed..9a56371715 100644
--- a/src/libsystemd/sd-bus/bus-slot.c
+++ b/src/libsystemd/sd-bus/bus-slot.c
@@ -81,7 +81,7 @@ void bus_slot_disconnect(sd_bus_slot *slot) {
if (slot->reply_callback.cookie != 0)
ordered_hashmap_remove(slot->bus->reply_callbacks, &slot->reply_callback.cookie);
- if (slot->reply_callback.timeout != 0)
+ if (slot->reply_callback.timeout_usec != 0)
prioq_remove(slot->bus->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx);
break;
@@ -94,12 +94,17 @@ void bus_slot_disconnect(sd_bus_slot *slot) {
case BUS_MATCH_CALLBACK:
if (slot->match_added)
- bus_remove_match_internal(slot->bus, slot->match_callback.match_string);
+ (void) bus_remove_match_internal(slot->bus, slot->match_callback.match_string);
+
+ if (slot->match_callback.install_slot) {
+ bus_slot_disconnect(slot->match_callback.install_slot);
+ slot->match_callback.install_slot = sd_bus_slot_unref(slot->match_callback.install_slot);
+ }
slot->bus->match_callbacks_modified = true;
bus_match_remove(&slot->bus->match_callbacks, &slot->match_callback);
- free(slot->match_callback.match_string);
+ slot->match_callback.match_string = mfree(slot->match_callback.match_string);
break;
@@ -174,7 +179,7 @@ void bus_slot_disconnect(sd_bus_slot *slot) {
}
}
- free(slot->node_vtable.interface);
+ slot->node_vtable.interface = mfree(slot->node_vtable.interface);
if (slot->node_vtable.node) {
LIST_REMOVE(vtables, slot->node_vtable.node->vtables, &slot->node_vtable);
diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c
index 07a9c8affd..2fe86b61c4 100644
--- a/src/libsystemd/sd-bus/bus-socket.c
+++ b/src/libsystemd/sd-bus/bus-socket.c
@@ -32,9 +32,13 @@
#include "bus-socket.h"
#include "fd-util.h"
#include "format-util.h"
+#include "fs-util.h"
#include "hexdecoct.h"
+#include "io-util.h"
#include "macro.h"
#include "missing.h"
+#include "path-util.h"
+#include "process-util.h"
#include "selinux-util.h"
#include "signal-util.h"
#include "stdio-util.h"
@@ -188,7 +192,7 @@ static int bus_socket_auth_verify_client(sd_bus *b) {
if (!e)
return 0;
- if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD) {
+ if (b->accept_fd) {
f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
if (!f)
return 0;
@@ -475,7 +479,7 @@ static int bus_socket_auth_verify_server(sd_bus *b) {
r = bus_socket_auth_write_ok(b);
}
} else if (line_equals(line, l, "NEGOTIATE_UNIX_FD")) {
- if (b->auth == _BUS_AUTH_INVALID || !(b->hello_flags & KDBUS_HELLO_ACCEPT_FD))
+ if (b->auth == _BUS_AUTH_INVALID || !b->accept_fd)
r = bus_socket_auth_write(b, "ERROR\r\n");
else {
b->can_fds = true;
@@ -592,8 +596,8 @@ void bus_socket_setup(sd_bus *b) {
assert(b);
/* Increase the buffers to 8 MB */
- fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE);
- fd_inc_sndbuf(b->output_fd, SNDBUF_SIZE);
+ (void) fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE);
+ (void) fd_inc_sndbuf(b->output_fd, SNDBUF_SIZE);
b->message_version = 1;
b->message_endian = 0;
@@ -603,16 +607,24 @@ static void bus_get_peercred(sd_bus *b) {
int r;
assert(b);
+ assert(!b->ucred_valid);
+ assert(!b->label);
+ assert(b->n_groups == (size_t) -1);
/* Get the peer for socketpair() sockets */
b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0;
/* Get the SELinux context of the peer */
- if (mac_selinux_use()) {
- r = getpeersec(b->input_fd, &b->label);
- if (r < 0 && r != -EOPNOTSUPP)
- log_debug_errno(r, "Failed to determine peer security context: %m");
- }
+ r = getpeersec(b->input_fd, &b->label);
+ if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT))
+ log_debug_errno(r, "Failed to determine peer security context: %m");
+
+ /* Get the list of auxiliary groups of the peer */
+ r = getpeergroups(b->input_fd, &b->groups);
+ if (r >= 0)
+ b->n_groups = (size_t) r;
+ else if (!IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT))
+ log_debug_errno(r, "Failed to determine peer's group list: %m");
}
static int bus_socket_start_auth_client(sd_bus *b) {
@@ -641,7 +653,7 @@ static int bus_socket_start_auth_client(sd_bus *b) {
if (!b->auth_buffer)
return -ENOMEM;
- if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD)
+ if (b->accept_fd)
auth_suffix = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n";
else
auth_suffix = "\r\nBEGIN\r\n";
@@ -661,15 +673,15 @@ int bus_socket_start_auth(sd_bus *b) {
bus_get_peercred(b);
- b->state = BUS_AUTHENTICATING;
+ bus_set_state(b, BUS_AUTHENTICATING);
b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_AUTH_TIMEOUT;
if (sd_is_socket(b->input_fd, AF_UNIX, 0, 0) <= 0)
- b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD;
+ b->accept_fd = false;
if (b->output_fd != b->input_fd)
if (sd_is_socket(b->output_fd, AF_UNIX, 0, 0) <= 0)
- b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD;
+ b->accept_fd = false;
if (b->is_server)
return bus_socket_read_auth(b);
@@ -677,30 +689,249 @@ int bus_socket_start_auth(sd_bus *b) {
return bus_socket_start_auth_client(b);
}
+static int bus_socket_inotify_setup(sd_bus *b) {
+ _cleanup_free_ int *new_watches = NULL;
+ _cleanup_free_ char *absolute = NULL;
+ size_t n_allocated = 0, n = 0, done = 0, i;
+ unsigned max_follow = 32;
+ const char *p;
+ int wd, r;
+
+ assert(b);
+ assert(b->watch_bind);
+ assert(b->sockaddr.sa.sa_family == AF_UNIX);
+ assert(b->sockaddr.un.sun_path[0] != 0);
+
+ /* Sets up an inotify fd in case watch_bind is enabled: wait until the configured AF_UNIX file system socket
+ * appears before connecting to it. The implemented is pretty simplistic: we just subscribe to relevant changes
+ * to all prefix components of the path, and every time we get an event for that we try to reconnect again,
+ * without actually caring what precisely the event we got told us. If we still can't connect we re-subscribe
+ * to all relevant changes of anything in the path, so that our watches include any possibly newly created path
+ * components. */
+
+ if (b->inotify_fd < 0) {
+ b->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
+ if (b->inotify_fd < 0)
+ return -errno;
+ }
+
+ /* Make sure the path is NUL terminated */
+ p = strndupa(b->sockaddr.un.sun_path, sizeof(b->sockaddr.un.sun_path));
+
+ /* Make sure the path is absolute */
+ r = path_make_absolute_cwd(p, &absolute);
+ if (r < 0)
+ goto fail;
+
+ /* Watch all parent directories, and don't mind any prefix that doesn't exist yet. For the innermost directory
+ * that exists we want to know when files are created or moved into it. For all parents of it we just care if
+ * they are removed or renamed. */
+
+ if (!GREEDY_REALLOC(new_watches, n_allocated, n + 1)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ /* Start with the top-level directory, which is a bit simpler than the rest, since it can't be a symlink, and
+ * always exists */
+ wd = inotify_add_watch(b->inotify_fd, "/", IN_CREATE|IN_MOVED_TO);
+ if (wd < 0) {
+ r = log_debug_errno(errno, "Failed to add inotify watch on /: %m");
+ goto fail;
+ } else
+ new_watches[n++] = wd;
+
+ for (;;) {
+ _cleanup_free_ char *component = NULL, *prefix = NULL, *destination = NULL;
+ size_t n_slashes, n_component;
+ char *c = NULL;
+
+ n_slashes = strspn(absolute + done, "/");
+ n_component = n_slashes + strcspn(absolute + done + n_slashes, "/");
+
+ if (n_component == 0) /* The end */
+ break;
+
+ component = strndup(absolute + done, n_component);
+ if (!component) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ /* A trailing slash? That's a directory, and not a socket then */
+ if (path_equal(component, "/")) {
+ r = -EISDIR;
+ goto fail;
+ }
+
+ /* A single dot? Let's eat this up */
+ if (path_equal(component, "/.")) {
+ done += n_component;
+ continue;
+ }
+
+ prefix = strndup(absolute, done + n_component);
+ if (!prefix) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!GREEDY_REALLOC(new_watches, n_allocated, n + 1)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ wd = inotify_add_watch(b->inotify_fd, prefix, IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO|IN_DONT_FOLLOW);
+ log_debug("Added inotify watch for %s on bus %s: %i", prefix, strna(b->description), wd);
+
+ if (wd < 0) {
+ if (IN_SET(errno, ENOENT, ELOOP))
+ break; /* This component doesn't exist yet, or the path contains a cyclic symlink right now */
+
+ r = log_debug_errno(errno, "Failed to add inotify watch on %s: %m", isempty(prefix) ? "/" : prefix);
+ goto fail;
+ } else
+ new_watches[n++] = wd;
+
+ /* Check if this is possibly a symlink. If so, let's follow it and watch it too. */
+ r = readlink_malloc(prefix, &destination);
+ if (r == -EINVAL) { /* not a symlink */
+ done += n_component;
+ continue;
+ }
+ if (r < 0)
+ goto fail;
+
+ if (isempty(destination)) { /* Empty symlink target? Yuck! */
+ r = -EINVAL;
+ goto fail;
+ }
+
+ if (max_follow <= 0) { /* Let's make sure we don't follow symlinks forever */
+ r = -ELOOP;
+ goto fail;
+ }
+
+ if (path_is_absolute(destination)) {
+ /* For absolute symlinks we build the new path and start anew */
+ c = strjoin(destination, absolute + done + n_component);
+ done = 0;
+ } else {
+ _cleanup_free_ char *t = NULL;
+
+ /* For relative symlinks we replace the last component, and try again */
+ t = strndup(absolute, done);
+ if (!t)
+ return -ENOMEM;
+
+ c = strjoin(t, "/", destination, absolute + done + n_component);
+ }
+ if (!c) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ free(absolute);
+ absolute = c;
+
+ max_follow--;
+ }
+
+ /* And now, let's remove all watches from the previous iteration we don't need anymore */
+ for (i = 0; i < b->n_inotify_watches; i++) {
+ bool found = false;
+ size_t j;
+
+ for (j = 0; j < n; j++)
+ if (new_watches[j] == b->inotify_watches[i]) {
+ found = true;
+ break;
+ }
+
+ if (found)
+ continue;
+
+ (void) inotify_rm_watch(b->inotify_fd, b->inotify_watches[i]);
+ }
+
+ free_and_replace(b->inotify_watches, new_watches);
+ b->n_inotify_watches = n;
+
+ return 0;
+
+fail:
+ bus_close_inotify_fd(b);
+ return r;
+}
+
int bus_socket_connect(sd_bus *b) {
+ bool inotify_done = false;
int r;
assert(b);
- assert(b->input_fd < 0);
- assert(b->output_fd < 0);
- assert(b->sockaddr.sa.sa_family != AF_UNSPEC);
- b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (b->input_fd < 0)
- return -errno;
+ for (;;) {
+ assert(b->input_fd < 0);
+ assert(b->output_fd < 0);
+ assert(b->sockaddr.sa.sa_family != AF_UNSPEC);
- b->output_fd = b->input_fd;
+ b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (b->input_fd < 0)
+ return -errno;
- bus_socket_setup(b);
+ b->output_fd = b->input_fd;
+ bus_socket_setup(b);
- r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
- if (r < 0) {
- if (errno == EINPROGRESS)
- return 1;
+ if (connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size) < 0) {
+ if (errno == EINPROGRESS) {
- return -errno;
+ /* If we have any inotify watches open, close them now, we don't need them anymore, as
+ * we have successfully initiated a connection */
+ bus_close_inotify_fd(b);
+
+ /* Note that very likely we are already in BUS_OPENING state here, as we enter it when
+ * we start parsing the address string. The only reason we set the state explicitly
+ * here, is to undo BUS_WATCH_BIND, in case we did the inotify magic. */
+ bus_set_state(b, BUS_OPENING);
+ return 1;
+ }
+
+ if (IN_SET(errno, ENOENT, ECONNREFUSED) && /* ENOENT → unix socket doesn't exist at all; ECONNREFUSED → unix socket stale */
+ b->watch_bind &&
+ b->sockaddr.sa.sa_family == AF_UNIX &&
+ b->sockaddr.un.sun_path[0] != 0) {
+
+ /* This connection attempt failed, let's release the socket for now, and start with a
+ * fresh one when reconnecting. */
+ bus_close_io_fds(b);
+
+ if (inotify_done) {
+ /* inotify set up already, don't do it again, just return now, and remember
+ * that we are waiting for inotify events now. */
+ bus_set_state(b, BUS_WATCH_BIND);
+ return 1;
+ }
+
+ /* This is a file system socket, and the inotify logic is enabled. Let's create the necessary inotify fd. */
+ r = bus_socket_inotify_setup(b);
+ if (r < 0)
+ return r;
+
+ /* Let's now try to connect a second time, because in theory there's otherwise a race
+ * here: the socket might have been created in the time between our first connect() and
+ * the time we set up the inotify logic. But let's remember that we set up inotify now,
+ * so that we don't do the connect() more than twice. */
+ inotify_done = true;
+
+ } else
+ return -errno;
+ } else
+ break;
}
+ /* Yay, established, we don't need no inotify anymore! */
+ bus_close_inotify_fd(b);
+
return bus_socket_start_auth(b);
}
@@ -717,29 +948,24 @@ int bus_socket_exec(sd_bus *b) {
if (r < 0)
return -errno;
- pid = fork();
- if (pid < 0) {
+ r = safe_fork_full("(sd-busexec)", s+1, 1, FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, &pid);
+ if (r < 0) {
safe_close_pair(s);
- return -errno;
+ return r;
}
- if (pid == 0) {
+ if (r == 0) {
/* Child */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- close_all_fds(s+1, 1);
-
assert_se(dup3(s[1], STDIN_FILENO, 0) == STDIN_FILENO);
assert_se(dup3(s[1], STDOUT_FILENO, 0) == STDOUT_FILENO);
if (!IN_SET(s[1], STDIN_FILENO, STDOUT_FILENO))
safe_close(s[1]);
- fd_cloexec(STDIN_FILENO, false);
- fd_cloexec(STDOUT_FILENO, false);
- fd_nonblock(STDIN_FILENO, false);
- fd_nonblock(STDOUT_FILENO, false);
+ (void) fd_cloexec(STDIN_FILENO, false);
+ (void) fd_cloexec(STDOUT_FILENO, false);
+ (void) fd_nonblock(STDIN_FILENO, false);
+ (void) fd_nonblock(STDOUT_FILENO, false);
if (b->exec_argv)
execvp(b->exec_path, b->exec_argv);
@@ -1063,3 +1289,34 @@ int bus_socket_process_authenticating(sd_bus *b) {
return bus_socket_read_auth(b);
}
+
+int bus_socket_process_watch_bind(sd_bus *b) {
+ int r, q;
+
+ assert(b);
+ assert(b->state == BUS_WATCH_BIND);
+ assert(b->inotify_fd >= 0);
+
+ r = flush_fd(b->inotify_fd);
+ if (r <= 0)
+ return r;
+
+ log_debug("Got inotify event on bus %s.", strna(b->description));
+
+ /* We flushed events out of the inotify fd. In that case, maybe the socket is valid now? Let's try to connect
+ * to it again */
+
+ r = bus_socket_connect(b);
+ if (r < 0)
+ return r;
+
+ q = bus_attach_io_events(b);
+ if (q < 0)
+ return q;
+
+ q = bus_attach_inotify_event(b);
+ if (q < 0)
+ return q;
+
+ return r;
+}
diff --git a/src/libsystemd/sd-bus/bus-socket.h b/src/libsystemd/sd-bus/bus-socket.h
index 915a283f5a..c180562f98 100644
--- a/src/libsystemd/sd-bus/bus-socket.h
+++ b/src/libsystemd/sd-bus/bus-socket.h
@@ -34,5 +34,6 @@ int bus_socket_read_message(sd_bus *bus);
int bus_socket_process_opening(sd_bus *b);
int bus_socket_process_authenticating(sd_bus *b);
+int bus_socket_process_watch_bind(sd_bus *b);
bool bus_socket_auth_needs_write(sd_bus *b);
diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c
index ab22d6e4de..5482d39a01 100644
--- a/src/libsystemd/sd-bus/bus-track.c
+++ b/src/libsystemd/sd-bus/bus-track.c
@@ -48,25 +48,13 @@ struct sd_bus_track {
LIST_FIELDS(sd_bus_track, tracks);
};
-#define MATCH_PREFIX \
- "type='signal'," \
- "sender='org.freedesktop.DBus'," \
- "path='/org/freedesktop/DBus'," \
- "interface='org.freedesktop.DBus'," \
- "member='NameOwnerChanged'," \
- "arg0='"
-
-#define MATCH_SUFFIX \
- "'"
-
-#define MATCH_FOR_NAME(name) \
- ({ \
- char *_x; \
- size_t _l = strlen(name); \
- _x = alloca(STRLEN(MATCH_PREFIX)+_l+STRLEN(MATCH_SUFFIX)+1); \
- strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \
- _x; \
- })
+#define MATCH_FOR_NAME(name) \
+ strjoina("type='signal'," \
+ "sender='org.freedesktop.DBus'," \
+ "path='/org/freedesktop/DBus'," \
+ "interface='org.freedesktop.DBus'," \
+ "member='NameOwnerChanged'," \
+ "arg0='", name, "'")
static struct track_item* track_item_free(struct track_item *i) {
@@ -148,6 +136,7 @@ _public_ int sd_bus_track_new(
sd_bus_track *t;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(track, -EINVAL);
if (!bus->bus_client)
@@ -259,9 +248,7 @@ _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */
- track->n_adding++; /* make sure we aren't dispatched while we synchronously add this match */
- r = sd_bus_add_match(track->bus, &n->slot, match, on_name_owner_changed, track);
- track->n_adding--;
+ r = sd_bus_add_match_async(track->bus, &n->slot, match, on_name_owner_changed, NULL, track);
if (r < 0) {
bus_track_add_to_queue(track);
return r;
diff --git a/src/libsystemd/sd-bus/bus-type.c b/src/libsystemd/sd-bus/bus-type.c
index fe486f441d..980b35d8ea 100644
--- a/src/libsystemd/sd-bus/bus-type.c
+++ b/src/libsystemd/sd-bus/bus-type.c
@@ -18,6 +18,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+
+#include "sd-bus.h"
+
#include "bus-type.h"
bool bus_type_is_valid(char c) {
diff --git a/src/libsystemd/sd-bus/bus-type.h b/src/libsystemd/sd-bus/bus-type.h
index ae272b1e6a..834f09777a 100644
--- a/src/libsystemd/sd-bus/bus-type.h
+++ b/src/libsystemd/sd-bus/bus-type.h
@@ -22,8 +22,6 @@
#include <stdbool.h>
-#include "sd-bus.h"
-
#include "macro.h"
bool bus_type_is_valid(char c) _const_;
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index 5fb15a7aaf..7e7ebb27a7 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -50,6 +50,7 @@
#include "macro.h"
#include "missing.h"
#include "parse-util.h"
+#include "process-util.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
@@ -57,7 +58,7 @@
#define log_debug_bus_message(m) \
do { \
sd_bus_message *_mm = (m); \
- log_debug("Got message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " error-name=%s error-message=%s", \
+ log_debug("Got message type=%s sender=%s destination=%s path=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " signature=%s error-name=%s error-message=%s", \
bus_message_type_to_string(_mm->header->type), \
strna(sd_bus_message_get_sender(_mm)), \
strna(sd_bus_message_get_destination(_mm)), \
@@ -66,28 +67,97 @@
strna(sd_bus_message_get_member(_mm)), \
BUS_MESSAGE_COOKIE(_mm), \
_mm->reply_cookie, \
+ strna(_mm->root_container.signature), \
strna(_mm->error.name), \
strna(_mm->error.message)); \
} while (false)
static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec);
-static int attach_io_events(sd_bus *b);
-static void detach_io_events(sd_bus *b);
+static void bus_detach_io_events(sd_bus *b);
+static void bus_detach_inotify_event(sd_bus *b);
static thread_local sd_bus *default_system_bus = NULL;
static thread_local sd_bus *default_user_bus = NULL;
static thread_local sd_bus *default_starter_bus = NULL;
-static void bus_close_fds(sd_bus *b) {
+static sd_bus **bus_choose_default(int (**bus_open)(sd_bus **)) {
+ const char *e;
+
+ /* Let's try our best to reuse another cached connection. If
+ * the starter bus type is set, connect via our normal
+ * connection logic, ignoring $DBUS_STARTER_ADDRESS, so that
+ * we can share the connection with the user/system default
+ * bus. */
+
+ e = secure_getenv("DBUS_STARTER_BUS_TYPE");
+ if (e) {
+ if (streq(e, "system")) {
+ if (bus_open)
+ *bus_open = sd_bus_open_system;
+ return &default_system_bus;
+ } else if (STR_IN_SET(e, "user", "session")) {
+ if (bus_open)
+ *bus_open = sd_bus_open_user;
+ return &default_user_bus;
+ }
+ }
+
+ /* No type is specified, so we have not other option than to
+ * use the starter address if it is set. */
+ e = secure_getenv("DBUS_STARTER_ADDRESS");
+ if (e) {
+ if (bus_open)
+ *bus_open = sd_bus_open;
+ return &default_starter_bus;
+ }
+
+ /* Finally, if nothing is set use the cached connection for
+ * the right scope */
+
+ if (cg_pid_get_owner_uid(0, NULL) >= 0) {
+ if (bus_open)
+ *bus_open = sd_bus_open_user;
+ return &default_user_bus;
+ } else {
+ if (bus_open)
+ *bus_open = sd_bus_open_system;
+ return &default_system_bus;
+ }
+}
+
+sd_bus *bus_resolve(sd_bus *bus) {
+ switch ((uintptr_t) bus) {
+ case (uintptr_t) SD_BUS_DEFAULT:
+ return *(bus_choose_default(NULL));
+ case (uintptr_t) SD_BUS_DEFAULT_USER:
+ return default_user_bus;
+ case (uintptr_t) SD_BUS_DEFAULT_SYSTEM:
+ return default_system_bus;
+ default:
+ return bus;
+ }
+}
+
+void bus_close_io_fds(sd_bus *b) {
assert(b);
- detach_io_events(b);
+ bus_detach_io_events(b);
if (b->input_fd != b->output_fd)
safe_close(b->output_fd);
b->output_fd = b->input_fd = safe_close(b->input_fd);
}
+void bus_close_inotify_fd(sd_bus *b) {
+ assert(b);
+
+ bus_detach_inotify_event(b);
+
+ b->inotify_fd = safe_close(b->inotify_fd);
+ b->inotify_watches = mfree(b->inotify_watches);
+ b->n_inotify_watches = 0;
+}
+
static void bus_reset_queues(sd_bus *b) {
assert(b);
@@ -131,9 +201,11 @@ static void bus_free(sd_bus *b) {
if (b->default_bus_ptr)
*b->default_bus_ptr = NULL;
- bus_close_fds(b);
+ bus_close_io_fds(b);
+ bus_close_inotify_fd(b);
free(b->label);
+ free(b->groups);
free(b->rbuffer);
free(b->unique_name);
free(b->auth_buffer);
@@ -141,6 +213,7 @@ static void bus_free(sd_bus *b) {
free(b->machine);
free(b->cgroup_root);
free(b->description);
+ free(b->patch_sender);
free(b->exec_path);
strv_free(b->exec_argv);
@@ -180,11 +253,12 @@ _public_ int sd_bus_new(sd_bus **ret) {
r->n_ref = REFCNT_INIT;
r->input_fd = r->output_fd = -1;
+ r->inotify_fd = -1;
r->message_version = 1;
r->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME;
- r->hello_flags |= KDBUS_HELLO_ACCEPT_FD;
- r->attach_flags |= KDBUS_ATTACH_NAMES;
+ r->accept_fd = true;
r->original_pid = getpid_cached();
+ r->n_groups = (size_t) -1;
assert_se(pthread_mutex_init(&r->memfd_cache_mutex, NULL) == 0);
@@ -203,6 +277,7 @@ _public_ int sd_bus_set_address(sd_bus *bus, const char *address) {
char *a;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(address, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -219,6 +294,7 @@ _public_ int sd_bus_set_address(sd_bus *bus, const char *address) {
_public_ int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(input_fd >= 0, -EBADF);
assert_return(output_fd >= 0, -EBADF);
@@ -233,6 +309,7 @@ _public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[])
char *p, **a;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(path, -EINVAL);
assert_return(!strv_isempty(argv), -EINVAL);
@@ -259,7 +336,9 @@ _public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[])
_public_ int sd_bus_set_bus_client(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus->patch_sender, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
bus->bus_client = !!b;
@@ -268,43 +347,40 @@ _public_ int sd_bus_set_bus_client(sd_bus *bus, int b) {
_public_ int sd_bus_set_monitor(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
- SET_FLAG(bus->hello_flags, KDBUS_HELLO_MONITOR, b);
+ bus->is_monitor = b;
return 0;
}
_public_ int sd_bus_negotiate_fds(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
- SET_FLAG(bus->hello_flags, KDBUS_HELLO_ACCEPT_FD, b);
+ bus->accept_fd = b;
return 0;
}
_public_ int sd_bus_negotiate_timestamp(sd_bus *bus, int b) {
- uint64_t new_flags;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
- new_flags = bus->attach_flags;
- SET_FLAG(new_flags, KDBUS_ATTACH_TIMESTAMP, b);
-
- if (bus->attach_flags == new_flags)
- return 0;
-
- bus->attach_flags = new_flags;
+ /* This is not actually supported by any of our transports these days, but we do honour it for synthetic
+ * replies, and maybe one day classic D-Bus learns this too */
+ bus->attach_timestamp = b;
return 0;
}
_public_ int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t mask) {
- uint64_t new_flags;
-
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(mask <= _SD_BUS_CREDS_ALL, -EINVAL);
assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -314,18 +390,12 @@ _public_ int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t mask) {
/* The well knowns we need unconditionally, so that matches can work */
bus->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME;
- /* Make sure we don't lose the timestamp flag */
- new_flags = (bus->attach_flags & KDBUS_ATTACH_TIMESTAMP) | attach_flags_to_kdbus(bus->creds_mask);
- if (bus->attach_flags == new_flags)
- return 0;
-
- bus->attach_flags = new_flags;
-
return 0;
}
_public_ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(b || sd_id128_equal(server_id, SD_ID128_NULL), -EINVAL);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -337,6 +407,7 @@ _public_ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) {
_public_ int sd_bus_set_anonymous(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -346,6 +417,7 @@ _public_ int sd_bus_set_anonymous(sd_bus *bus, int b) {
_public_ int sd_bus_set_trusted(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -355,6 +427,7 @@ _public_ int sd_bus_set_trusted(sd_bus *bus, int b) {
_public_ int sd_bus_set_description(sd_bus *bus, const char *description) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -363,6 +436,7 @@ _public_ int sd_bus_set_description(sd_bus *bus, const char *description) {
_public_ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
bus->allow_interactive_authorization = !!b;
@@ -371,11 +445,115 @@ _public_ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b) {
_public_ int sd_bus_get_allow_interactive_authorization(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return bus->allow_interactive_authorization;
}
+_public_ int sd_bus_set_watch_bind(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ bus->watch_bind = b;
+ return 0;
+}
+
+_public_ int sd_bus_get_watch_bind(sd_bus *bus) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ return bus->watch_bind;
+}
+
+_public_ int sd_bus_set_connected_signal(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ bus->connected_signal = b;
+ return 0;
+}
+
+_public_ int sd_bus_get_connected_signal(sd_bus *bus) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ return bus->connected_signal;
+}
+
+static int synthesize_connected_signal(sd_bus *bus) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ int r;
+
+ assert(bus);
+
+ /* If enabled, synthesizes a local "Connected" signal mirroring the local "Disconnected" signal. This is called
+ * whenever we fully established a connection, i.e. after the authorization phase, and after receiving the
+ * Hello() reply. Or in other words, whenver we enter BUS_RUNNING state.
+ *
+ * This is useful so that clients can start doing stuff whenver the connection is fully established in a way
+ * that works independently from whether we connected to a full bus or just a direct connection. */
+
+ if (!bus->connected_signal)
+ return 0;
+
+ r = sd_bus_message_new_signal(
+ bus,
+ &m,
+ "/org/freedesktop/DBus/Local",
+ "org.freedesktop.DBus.Local",
+ "Connected");
+ if (r < 0)
+ return r;
+
+ bus_message_set_sender_local(bus, m);
+
+ r = bus_seal_synthetic_message(bus, m);
+ if (r < 0)
+ return r;
+
+ r = bus_rqueue_make_room(bus);
+ if (r < 0)
+ return r;
+
+ /* Insert at the very front */
+ memmove(bus->rqueue + 1, bus->rqueue, sizeof(sd_bus_message*) * bus->rqueue_size);
+ bus->rqueue[0] = m;
+ m = NULL;
+ bus->rqueue_size++;
+
+ return 0;
+}
+
+void bus_set_state(sd_bus *bus, enum bus_state state) {
+
+ static const char * const table[_BUS_STATE_MAX] = {
+ [BUS_UNSET] = "UNSET",
+ [BUS_WATCH_BIND] = "WATCH_BIND",
+ [BUS_OPENING] = "OPENING",
+ [BUS_AUTHENTICATING] = "AUTHENTICATING",
+ [BUS_HELLO] = "HELLO",
+ [BUS_RUNNING] = "RUNNING",
+ [BUS_CLOSING] = "CLOSING",
+ [BUS_CLOSED] = "CLOSED",
+ };
+
+ assert(bus);
+ assert(state < _BUS_STATE_MAX);
+
+ if (state == bus->state)
+ return;
+
+ log_debug("Bus %s: changing state %s → %s", strna(bus->description), table[bus->state], table[state]);
+ bus->state = state;
+}
+
static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
const char *s;
sd_bus *bus;
@@ -401,8 +579,13 @@ static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *e
if (!bus->unique_name)
return -ENOMEM;
- if (bus->state == BUS_HELLO)
- bus->state = BUS_RUNNING;
+ if (bus->state == BUS_HELLO) {
+ bus_set_state(bus, BUS_RUNNING);
+
+ r = synthesize_connected_signal(bus);
+ if (r < 0)
+ return r;
+ }
return 1;
}
@@ -430,14 +613,37 @@ static int bus_send_hello(sd_bus *bus) {
}
int bus_start_running(sd_bus *bus) {
+ struct reply_callback *c;
+ Iterator i;
+ usec_t n;
+ int r;
+
assert(bus);
+ assert(bus->state < BUS_HELLO);
+
+ /* We start all method call timeouts when we enter BUS_HELLO or BUS_RUNNING mode. At this point let's convert
+ * all relative to absolute timestamps. Note that we do not reshuffle the reply callback priority queue since
+ * adding a fixed value to all entries should not alter the internal order. */
+
+ n = now(CLOCK_MONOTONIC);
+ ORDERED_HASHMAP_FOREACH(c, bus->reply_callbacks, i) {
+ if (c->timeout_usec == 0)
+ continue;
+
+ c->timeout_usec = usec_add(n, c->timeout_usec);
+ }
if (bus->bus_client) {
- bus->state = BUS_HELLO;
+ bus_set_state(bus, BUS_HELLO);
return 1;
}
- bus->state = BUS_RUNNING;
+ bus_set_state(bus, BUS_RUNNING);
+
+ r = synthesize_connected_signal(bus);
+ if (r < 0)
+ return r;
+
return 1;
}
@@ -799,6 +1005,7 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid)
b->nspid = 0;
b->sockaddr.un.sun_family = AF_UNIX;
+ /* Note that we use the old /var/run prefix here, to increase compatibility with really old containers */
strncpy(b->sockaddr.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path));
b->sockaddr_size = SOCKADDR_UN_LEN(b->sockaddr.un);
b->is_local = false;
@@ -898,7 +1105,8 @@ static int bus_start_address(sd_bus *b) {
assert(b);
for (;;) {
- bus_close_fds(b);
+ bus_close_io_fds(b);
+ bus_close_inotify_fd(b);
/* If you provide multiple different bus-addresses, we
* try all of them in order and use the first one that
@@ -906,20 +1114,25 @@ static int bus_start_address(sd_bus *b) {
if (b->exec_path)
r = bus_socket_exec(b);
-
else if ((b->nspid > 0 || b->machine) && b->sockaddr.sa.sa_family != AF_UNSPEC)
r = bus_container_connect_socket(b);
-
else if (b->sockaddr.sa.sa_family != AF_UNSPEC)
r = bus_socket_connect(b);
-
else
goto next;
if (r >= 0) {
- r = attach_io_events(b);
- if (r >= 0)
- return r;
+ int q;
+
+ q = bus_attach_io_events(b);
+ if (q < 0)
+ return q;
+
+ q = bus_attach_inotify_event(b);
+ if (q < 0)
+ return q;
+
+ return r;
}
b->last_connect_error = -r;
@@ -976,10 +1189,11 @@ _public_ int sd_bus_start(sd_bus *bus) {
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
- bus->state = BUS_OPENING;
+ bus_set_state(bus, BUS_OPENING);
if (bus->is_server && bus->bus_client)
return -EINVAL;
@@ -1040,7 +1254,6 @@ _public_ int sd_bus_open(sd_bus **ret) {
* be safe, and authenticate everything */
b->trusted = false;
b->is_local = false;
- b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS;
b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS;
r = sd_bus_start(b);
@@ -1086,7 +1299,6 @@ _public_ int sd_bus_open_system(sd_bus **ret) {
/* Let's do per-method access control on the system bus. We
* need the caller's UID and capability set for that. */
b->trusted = false;
- b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS;
b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS;
b->is_local = true;
@@ -1120,7 +1332,7 @@ int bus_set_address_user(sd_bus *b) {
if (!ee)
return -ENOMEM;
- if (asprintf(&s, UNIX_USER_BUS_ADDRESS_FMT, ee) < 0)
+ if (asprintf(&s, DEFAULT_USER_BUS_ADDRESS_FMT, ee) < 0)
return -ENOMEM;
b->address = s;
@@ -1294,7 +1506,7 @@ _public_ void sd_bus_close(sd_bus *bus) {
if (bus_pid_changed(bus))
return;
- bus->state = BUS_CLOSED;
+ bus_set_state(bus, BUS_CLOSED);
sd_bus_detach_event(bus);
@@ -1302,7 +1514,8 @@ _public_ void sd_bus_close(sd_bus *bus) {
* the bus object and the bus may be freed */
bus_reset_queues(bus);
- bus_close_fds(bus);
+ bus_close_io_fds(bus);
+ bus_close_inotify_fd(bus);
}
_public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) {
@@ -1316,13 +1529,13 @@ _public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) {
return sd_bus_unref(bus);
}
-static void bus_enter_closing(sd_bus *bus) {
+void bus_enter_closing(sd_bus *bus) {
assert(bus);
- if (!IN_SET(bus->state, BUS_OPENING, BUS_AUTHENTICATING, BUS_HELLO, BUS_RUNNING))
+ if (!IN_SET(bus->state, BUS_WATCH_BIND, BUS_OPENING, BUS_AUTHENTICATING, BUS_HELLO, BUS_RUNNING))
return;
- bus->state = BUS_CLOSING;
+ bus_set_state(bus, BUS_CLOSING);
}
_public_ sd_bus *sd_bus_ref(sd_bus *bus) {
@@ -1352,23 +1565,33 @@ _public_ sd_bus *sd_bus_unref(sd_bus *bus) {
_public_ int sd_bus_is_open(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return BUS_IS_OPEN(bus->state);
}
+_public_ int sd_bus_is_ready(sd_bus *bus) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ return bus->state == BUS_RUNNING;
+}
+
_public_ int sd_bus_can_send(sd_bus *bus, char type) {
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state != BUS_UNSET, -ENOTCONN);
assert_return(!bus_pid_changed(bus), -ECHILD);
- if (bus->hello_flags & KDBUS_HELLO_MONITOR)
+ if (bus->is_monitor)
return 0;
if (type == SD_BUS_TYPE_UNIX_FD) {
- if (!(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD))
+ if (!bus->accept_fd)
return 0;
r = bus_ensure_running(bus);
@@ -1385,6 +1608,7 @@ _public_ int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id) {
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(id, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -1397,6 +1621,8 @@ _public_ int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id) {
}
static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) {
+ int r;
+
assert(b);
assert(m);
@@ -1411,6 +1637,12 @@ static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) {
if (timeout == 0)
timeout = BUS_DEFAULT_TIMEOUT;
+ if (!m->sender && b->patch_sender) {
+ r = sd_bus_message_set_sender(m, b->patch_sender);
+ if (r < 0)
+ return r;
+ }
+
return sd_bus_message_seal(m, ++b->cookie, timeout);
}
@@ -1436,7 +1668,7 @@ int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) {
/* Fake some timestamps, if they were requested, and not
* already initialized */
- if (b->attach_flags & KDBUS_ATTACH_TIMESTAMP) {
+ if (b->attach_timestamp) {
if (m->realtime <= 0)
m->realtime = now(CLOCK_REALTIME);
@@ -1454,7 +1686,7 @@ int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) {
return sd_bus_message_seal(m, 0xFFFFFFFFULL, 0);
}
-static int bus_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call, size_t *idx) {
+static int bus_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
int r;
assert(bus);
@@ -1465,7 +1697,7 @@ static int bus_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call
return r;
if (*idx >= BUS_MESSAGE_SIZE(m))
- log_debug("Sent message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " error-name=%s error-message=%s",
+ log_debug("Sent message type=%s sender=%s destination=%s path=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " signature=%s error-name=%s error-message=%s",
bus_message_type_to_string(m->header->type),
strna(sd_bus_message_get_sender(m)),
strna(sd_bus_message_get_destination(m)),
@@ -1474,6 +1706,7 @@ static int bus_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call
strna(sd_bus_message_get_member(m)),
BUS_MESSAGE_COOKIE(m),
m->reply_cookie,
+ strna(m->root_container.signature),
strna(m->error.name),
strna(m->error.message));
@@ -1488,7 +1721,7 @@ static int dispatch_wqueue(sd_bus *bus) {
while (bus->wqueue_size > 0) {
- r = bus_write_message(bus, bus->wqueue[0], false, &bus->windex);
+ r = bus_write_message(bus, bus->wqueue[0], &bus->windex);
if (r < 0)
return r;
else if (r == 0)
@@ -1567,7 +1800,7 @@ static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd
}
}
-static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, bool hint_sync_call) {
+_public_ int sd_bus_send(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m);
int r;
@@ -1612,7 +1845,7 @@ static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie,
if (IN_SET(bus->state, BUS_RUNNING, BUS_HELLO) && bus->wqueue_size <= 0) {
size_t idx = 0;
- r = bus_write_message(bus, m, hint_sync_call, &idx);
+ r = bus_write_message(bus, m, &idx);
if (r < 0) {
if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) {
bus_enter_closing(bus);
@@ -1652,10 +1885,6 @@ finish:
return 1;
}
-_public_ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) {
- return bus_send_internal(bus, m, cookie, false);
-}
-
_public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie) {
int r;
@@ -1682,26 +1911,35 @@ _public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destinat
return sd_bus_send(bus, m, cookie);
}
-static usec_t calc_elapse(uint64_t usec) {
+static usec_t calc_elapse(sd_bus *bus, uint64_t usec) {
+ assert(bus);
+
if (usec == (uint64_t) -1)
return 0;
- return now(CLOCK_MONOTONIC) + usec;
+ /* We start all timeouts the instant we enter BUS_HELLO/BUS_RUNNING state, so that the don't run in parallel
+ * with any connection setup states. Hence, if a method callback is started earlier than that we just store the
+ * relative timestamp, and afterwards the absolute one. */
+
+ if (IN_SET(bus->state, BUS_WATCH_BIND, BUS_OPENING, BUS_AUTHENTICATING))
+ return usec;
+ else
+ return now(CLOCK_MONOTONIC) + usec;
}
static int timeout_compare(const void *a, const void *b) {
const struct reply_callback *x = a, *y = b;
- if (x->timeout != 0 && y->timeout == 0)
+ if (x->timeout_usec != 0 && y->timeout_usec == 0)
return -1;
- if (x->timeout == 0 && y->timeout != 0)
+ if (x->timeout_usec == 0 && y->timeout_usec != 0)
return 1;
- if (x->timeout < y->timeout)
+ if (x->timeout_usec < y->timeout_usec)
return -1;
- if (x->timeout > y->timeout)
+ if (x->timeout_usec > y->timeout_usec)
return 1;
return 0;
@@ -1721,8 +1959,7 @@ _public_ int sd_bus_call_async(
assert_return(m, -EINVAL);
assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
- assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL);
- assert_return(callback, -EINVAL);
+ assert_return(!m->sealed || (!!callback == !(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)), -EINVAL);
if (!bus)
bus = m->bus;
@@ -1732,6 +1969,10 @@ _public_ int sd_bus_call_async(
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
+ /* If no callback is specified and there's no interest in a slot, then there's no reason to ask for a reply */
+ if (!callback && !slot && !m->sealed)
+ m->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED;
+
r = ordered_hashmap_ensure_allocated(&bus->reply_callbacks, &uint64_hash_ops);
if (r < 0)
return r;
@@ -1748,29 +1989,31 @@ _public_ int sd_bus_call_async(
if (r < 0)
return r;
- s = bus_slot_allocate(bus, !slot, BUS_REPLY_CALLBACK, sizeof(struct reply_callback), userdata);
- if (!s)
- return -ENOMEM;
+ if (slot || callback) {
+ s = bus_slot_allocate(bus, !slot, BUS_REPLY_CALLBACK, sizeof(struct reply_callback), userdata);
+ if (!s)
+ return -ENOMEM;
- s->reply_callback.callback = callback;
+ s->reply_callback.callback = callback;
- s->reply_callback.cookie = BUS_MESSAGE_COOKIE(m);
- r = ordered_hashmap_put(bus->reply_callbacks, &s->reply_callback.cookie, &s->reply_callback);
- if (r < 0) {
- s->reply_callback.cookie = 0;
- return r;
- }
-
- s->reply_callback.timeout = calc_elapse(m->timeout);
- if (s->reply_callback.timeout != 0) {
- r = prioq_put(bus->reply_callbacks_prioq, &s->reply_callback, &s->reply_callback.prioq_idx);
+ s->reply_callback.cookie = BUS_MESSAGE_COOKIE(m);
+ r = ordered_hashmap_put(bus->reply_callbacks, &s->reply_callback.cookie, &s->reply_callback);
if (r < 0) {
- s->reply_callback.timeout = 0;
+ s->reply_callback.cookie = 0;
return r;
}
+
+ s->reply_callback.timeout_usec = calc_elapse(bus, m->timeout);
+ if (s->reply_callback.timeout_usec != 0) {
+ r = prioq_put(bus->reply_callbacks_prioq, &s->reply_callback, &s->reply_callback.prioq_idx);
+ if (r < 0) {
+ s->reply_callback.timeout_usec = 0;
+ return r;
+ }
+ }
}
- r = sd_bus_send(bus, m, &s->reply_callback.cookie);
+ r = sd_bus_send(bus, m, s ? &s->reply_callback.cookie : NULL);
if (r < 0)
return r;
@@ -1848,11 +2091,11 @@ _public_ int sd_bus_call(
if (r < 0)
goto fail;
- r = bus_send_internal(bus, m, &cookie, true);
+ r = sd_bus_send(bus, m, &cookie);
if (r < 0)
goto fail;
- timeout = calc_elapse(m->timeout);
+ timeout = calc_elapse(bus, m->timeout);
for (;;) {
usec_t left;
@@ -1871,7 +2114,7 @@ _public_ int sd_bus_call(
if (incoming->header->type == SD_BUS_MESSAGE_METHOD_RETURN) {
- if (incoming->n_fds <= 0 || (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) {
+ if (incoming->n_fds <= 0 || bus->accept_fd) {
if (reply)
*reply = incoming;
else
@@ -1966,35 +2209,63 @@ fail:
_public_ int sd_bus_get_fd(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->input_fd == bus->output_fd, -EPERM);
assert_return(!bus_pid_changed(bus), -ECHILD);
- return bus->input_fd;
+ if (bus->state == BUS_CLOSED)
+ return -ENOTCONN;
+
+ if (bus->inotify_fd >= 0)
+ return bus->inotify_fd;
+
+ if (bus->input_fd >= 0)
+ return bus->input_fd;
+
+ return -ENOTCONN;
}
_public_ int sd_bus_get_events(sd_bus *bus) {
int flags = 0;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
- if (!BUS_IS_OPEN(bus->state) && bus->state != BUS_CLOSING)
+ switch (bus->state) {
+
+ case BUS_UNSET:
+ case BUS_CLOSED:
return -ENOTCONN;
- if (bus->state == BUS_OPENING)
+ case BUS_WATCH_BIND:
+ flags |= POLLIN;
+ break;
+
+ case BUS_OPENING:
flags |= POLLOUT;
- else if (bus->state == BUS_AUTHENTICATING) {
+ break;
+ case BUS_AUTHENTICATING:
if (bus_socket_auth_needs_write(bus))
flags |= POLLOUT;
flags |= POLLIN;
+ break;
- } else if (IN_SET(bus->state, BUS_RUNNING, BUS_HELLO)) {
+ case BUS_RUNNING:
+ case BUS_HELLO:
if (bus->rqueue_size <= 0)
flags |= POLLIN;
if (bus->wqueue_size > 0)
flags |= POLLOUT;
+ break;
+
+ case BUS_CLOSING:
+ break;
+
+ default:
+ assert_not_reached("Unknown state");
}
return flags;
@@ -2004,6 +2275,7 @@ _public_ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) {
struct reply_callback *c;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(timeout_usec, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2015,39 +2287,45 @@ _public_ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) {
return 1;
}
- if (bus->state == BUS_CLOSING) {
- *timeout_usec = 0;
- return 1;
- }
+ switch (bus->state) {
- if (bus->state == BUS_AUTHENTICATING) {
+ case BUS_AUTHENTICATING:
*timeout_usec = bus->auth_timeout;
return 1;
- }
- if (!IN_SET(bus->state, BUS_RUNNING, BUS_HELLO)) {
- *timeout_usec = (uint64_t) -1;
- return 0;
- }
+ case BUS_RUNNING:
+ case BUS_HELLO:
+ if (bus->rqueue_size > 0) {
+ *timeout_usec = 0;
+ return 1;
+ }
+
+ c = prioq_peek(bus->reply_callbacks_prioq);
+ if (!c) {
+ *timeout_usec = (uint64_t) -1;
+ return 0;
+ }
+
+ if (c->timeout_usec == 0) {
+ *timeout_usec = (uint64_t) -1;
+ return 0;
+ }
+
+ *timeout_usec = c->timeout_usec;
+ return 1;
- if (bus->rqueue_size > 0) {
+ case BUS_CLOSING:
*timeout_usec = 0;
return 1;
- }
- c = prioq_peek(bus->reply_callbacks_prioq);
- if (!c) {
+ case BUS_WATCH_BIND:
+ case BUS_OPENING:
*timeout_usec = (uint64_t) -1;
return 0;
- }
- if (c->timeout == 0) {
- *timeout_usec = (uint64_t) -1;
- return 0;
+ default:
+ assert_not_reached("Unknown or unexpected stat");
}
-
- *timeout_usec = c->timeout;
- return 1;
}
static int process_timeout(sd_bus *bus) {
@@ -2055,17 +2333,19 @@ static int process_timeout(sd_bus *bus) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message* m = NULL;
struct reply_callback *c;
sd_bus_slot *slot;
+ bool is_hello;
usec_t n;
int r;
assert(bus);
+ assert(IN_SET(bus->state, BUS_RUNNING, BUS_HELLO));
c = prioq_peek(bus->reply_callbacks_prioq);
if (!c)
return 0;
n = now(CLOCK_MONOTONIC);
- if (c->timeout > n)
+ if (c->timeout_usec > n)
return 0;
r = bus_message_new_synthetic_error(
@@ -2081,7 +2361,7 @@ static int process_timeout(sd_bus *bus) {
return r;
assert_se(prioq_pop(bus->reply_callbacks_prioq) == c);
- c->timeout = 0;
+ c->timeout_usec = 0;
ordered_hashmap_remove(bus->reply_callbacks, &c->cookie);
c->cookie = 0;
@@ -2090,6 +2370,8 @@ static int process_timeout(sd_bus *bus) {
bus->iteration_counter++;
+ is_hello = bus->state == BUS_HELLO && c->callback == hello_callback;
+
bus->current_message = m;
bus->current_slot = sd_bus_slot_ref(slot);
bus->current_handler = c->callback;
@@ -2107,6 +2389,11 @@ static int process_timeout(sd_bus *bus) {
sd_bus_slot_unref(slot);
+ /* When this is the hello message and it timed out, then make sure to propagate the error up, don't just log
+ * and ignore the callback handler's return value. */
+ if (is_hello)
+ return r;
+
return bus_maybe_reply_error(m, r, &error_buffer);
}
@@ -2136,6 +2423,7 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
_cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
struct reply_callback *c;
sd_bus_slot *slot;
+ bool is_hello;
int r;
assert(bus);
@@ -2155,7 +2443,7 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
slot = container_of(c, sd_bus_slot, reply_callback);
- if (m->n_fds > 0 && !(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) {
+ if (m->n_fds > 0 && !bus->accept_fd) {
/* If the reply contained a file descriptor which we
* didn't want we pass an error instead. */
@@ -2184,11 +2472,13 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
return r;
}
- if (c->timeout != 0) {
+ if (c->timeout_usec != 0) {
prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
- c->timeout = 0;
+ c->timeout_usec = 0;
}
+ is_hello = bus->state == BUS_HELLO && c->callback == hello_callback;
+
bus->current_slot = sd_bus_slot_ref(slot);
bus->current_handler = c->callback;
bus->current_userdata = slot->userdata;
@@ -2204,6 +2494,11 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
sd_bus_slot_unref(slot);
+ /* When this is the hello message and it failed, then make sure to propagate the error up, don't just log and
+ * ignore the callback handler's return value. */
+ if (is_hello)
+ return r;
+
return bus_maybe_reply_error(m, r, &error_buffer);
}
@@ -2280,7 +2575,7 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) {
assert(bus);
assert(m);
- if (bus->hello_flags & KDBUS_HELLO_MONITOR)
+ if (bus->is_monitor)
return 0;
if (bus->manual_peer_interface)
@@ -2338,13 +2633,13 @@ static int process_fd_check(sd_bus *bus, sd_bus_message *m) {
* delivered to us later even though we ourselves did not
* negotiate it. */
- if (bus->hello_flags & KDBUS_HELLO_MONITOR)
+ if (bus->is_monitor)
return 0;
if (m->n_fds <= 0)
return 0;
- if (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)
+ if (bus->accept_fd)
return 0;
if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
@@ -2515,9 +2810,9 @@ static int process_closing_reply_callback(sd_bus *bus, struct reply_callback *c)
if (r < 0)
return r;
- if (c->timeout != 0) {
+ if (c->timeout_usec != 0) {
prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
- c->timeout = 0;
+ c->timeout_usec = 0;
}
ordered_hashmap_remove(bus->reply_callbacks, &c->cookie);
@@ -2622,6 +2917,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit
* means *ret is filled in with an unprocessed message. */
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
/* We don't allow recursively invoking sd_bus_process(). */
@@ -2636,48 +2932,44 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit
case BUS_CLOSED:
return -ECONNRESET;
+ case BUS_WATCH_BIND:
+ r = bus_socket_process_watch_bind(bus);
+ break;
+
case BUS_OPENING:
r = bus_socket_process_opening(bus);
- if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) {
- bus_enter_closing(bus);
- r = 1;
- } else if (r < 0)
- return r;
- if (ret)
- *ret = NULL;
- return r;
+ break;
case BUS_AUTHENTICATING:
r = bus_socket_process_authenticating(bus);
- if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) {
- bus_enter_closing(bus);
- r = 1;
- } else if (r < 0)
- return r;
-
- if (ret)
- *ret = NULL;
-
- return r;
+ break;
case BUS_RUNNING:
case BUS_HELLO:
r = process_running(bus, hint_priority, priority, ret);
- if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) {
- bus_enter_closing(bus);
- r = 1;
-
- if (ret)
- *ret = NULL;
- }
+ if (r >= 0)
+ return r;
- return r;
+ /* This branch initializes *ret, hence we don't use the generic error checking below */
+ break;
case BUS_CLOSING:
return process_closing(bus, ret);
+
+ default:
+ assert_not_reached("Unknown state");
}
- assert_not_reached("Unknown state");
+ if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) {
+ bus_enter_closing(bus);
+ r = 1;
+ } else if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = NULL;
+
+ return r;
}
_public_ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
@@ -2690,7 +2982,7 @@ _public_ int sd_bus_process_priority(sd_bus *bus, int64_t priority, sd_bus_messa
static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
struct pollfd p[2] = {};
- int r, e, n;
+ int r, n;
struct timespec ts;
usec_t m = USEC_INFINITY;
@@ -2702,45 +2994,52 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
- e = sd_bus_get_events(bus);
- if (e < 0)
- return e;
-
- if (need_more)
- /* The caller really needs some more data, he doesn't
- * care about what's already read, or any timeouts
- * except its own. */
- e |= POLLIN;
- else {
- usec_t until;
- /* The caller wants to process if there's something to
- * process, but doesn't care otherwise */
-
- r = sd_bus_get_timeout(bus, &until);
- if (r < 0)
- return r;
- if (r > 0) {
- usec_t nw;
- nw = now(CLOCK_MONOTONIC);
- m = until > nw ? until - nw : 0;
- }
- }
+ if (bus->state == BUS_WATCH_BIND) {
+ assert(bus->inotify_fd >= 0);
- if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
- m = timeout_usec;
-
- p[0].fd = bus->input_fd;
- if (bus->output_fd == bus->input_fd) {
- p[0].events = e;
+ p[0].events = POLLIN;
+ p[0].fd = bus->inotify_fd;
n = 1;
} else {
- p[0].events = e & POLLIN;
- p[1].fd = bus->output_fd;
- p[1].events = e & POLLOUT;
- n = 2;
+ int e;
+
+ e = sd_bus_get_events(bus);
+ if (e < 0)
+ return e;
+
+ if (need_more)
+ /* The caller really needs some more data, he doesn't
+ * care about what's already read, or any timeouts
+ * except its own. */
+ e |= POLLIN;
+ else {
+ usec_t until;
+ /* The caller wants to process if there's something to
+ * process, but doesn't care otherwise */
+
+ r = sd_bus_get_timeout(bus, &until);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ m = usec_sub_unsigned(until, now(CLOCK_MONOTONIC));
+ }
+
+ 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, n, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
+ if (timeout_usec != (uint64_t) -1 && (m == USEC_INFINITY || timeout_usec < m))
+ m = timeout_usec;
+
+ r = ppoll(p, n, m == USEC_INFINITY ? NULL : timespec_store(&ts, m), NULL);
if (r < 0)
return -errno;
@@ -2750,6 +3049,7 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
_public_ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
if (bus->state == BUS_CLOSING)
@@ -2768,6 +3068,7 @@ _public_ int sd_bus_flush(sd_bus *bus) {
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
if (bus->state == BUS_CLOSING)
@@ -2776,6 +3077,10 @@ _public_ int sd_bus_flush(sd_bus *bus) {
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
+ /* We never were connected? Don't hang in inotify for good, as there's no timeout set for it */
+ if (bus->state == BUS_WATCH_BIND)
+ return -EUNATCH;
+
r = bus_ensure_running(bus);
if (r < 0)
return r;
@@ -2812,6 +3117,7 @@ _public_ int sd_bus_add_filter(
sd_bus_slot *s;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(callback, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2830,11 +3136,78 @@ _public_ int sd_bus_add_filter(
return 0;
}
-_public_ int sd_bus_add_match(
+static int add_match_callback(
+ sd_bus_message *m,
+ void *userdata,
+ sd_bus_error *ret_error) {
+
+ sd_bus_slot *match_slot = userdata;
+ bool failed = false;
+ int r;
+
+ assert(m);
+ assert(match_slot);
+
+ sd_bus_slot_ref(match_slot);
+
+ if (sd_bus_message_is_method_error(m, NULL)) {
+ log_debug_errno(sd_bus_message_get_errno(m),
+ "Unable to add match %s, failing connection: %s",
+ match_slot->match_callback.match_string,
+ sd_bus_message_get_error(m)->message);
+
+ failed = true;
+ } else
+ log_debug("Match %s successfully installed.", match_slot->match_callback.match_string);
+
+ if (match_slot->match_callback.install_callback) {
+ sd_bus *bus;
+
+ bus = sd_bus_message_get_bus(m);
+
+ /* This function has been called as slot handler, and we want to call another slot handler. Let's
+ * update the slot callback metadata temporarily with our own data, and then revert back to the old
+ * values. */
+
+ assert(bus->current_slot == match_slot->match_callback.install_slot);
+ assert(bus->current_handler == add_match_callback);
+ assert(bus->current_userdata == userdata);
+
+ bus->current_slot = match_slot;
+ bus->current_handler = match_slot->match_callback.install_callback;
+ bus->current_userdata = match_slot->userdata;
+
+ r = match_slot->match_callback.install_callback(m, match_slot->userdata, ret_error);
+
+ bus->current_slot = match_slot->match_callback.install_slot;
+ bus->current_handler = add_match_callback;
+ bus->current_userdata = userdata;
+
+ match_slot->match_callback.install_slot = sd_bus_slot_unref(match_slot->match_callback.install_slot);
+ } else {
+ if (failed) /* Generic failure handling: destroy the connection */
+ bus_enter_closing(sd_bus_message_get_bus(m));
+
+ r = 1;
+ }
+
+ if (failed && match_slot->floating) {
+ bus_slot_disconnect(match_slot);
+ sd_bus_slot_unref(match_slot);
+ }
+
+ sd_bus_slot_unref(match_slot);
+
+ return r;
+}
+
+static int bus_add_match_full(
sd_bus *bus,
sd_bus_slot **slot,
+ bool asynchronous,
const char *match,
sd_bus_message_handler_t callback,
+ sd_bus_message_handler_t install_callback,
void *userdata) {
struct bus_match_component *components = NULL;
@@ -2843,6 +3216,7 @@ _public_ int sd_bus_add_match(
int r = 0;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(match, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2857,18 +3231,17 @@ _public_ int sd_bus_add_match(
}
s->match_callback.callback = callback;
+ s->match_callback.install_callback = install_callback;
if (bus->bus_client) {
enum bus_match_scope scope;
scope = bus_match_get_scope(components, n_components);
- /* Do not install server-side matches for matches
- * against the local service, interface or bus path. */
+ /* Do not install server-side matches for matches against the local service, interface or bus path. */
if (scope != BUS_MATCH_LOCAL) {
- /* We store the original match string, so that
- * we can use it to remove the match again. */
+ /* We store the original match string, so that we can use it to remove the match again. */
s->match_callback.match_string = strdup(match);
if (!s->match_callback.match_string) {
@@ -2876,7 +3249,14 @@ _public_ int sd_bus_add_match(
goto finish;
}
- r = bus_add_match_internal(bus, s->match_callback.match_string, components, n_components);
+ if (asynchronous)
+ r = bus_add_match_internal_async(bus,
+ &s->match_callback.install_slot,
+ s->match_callback.match_string,
+ add_match_callback,
+ s);
+ else
+ r = bus_add_match_internal(bus, s->match_callback.match_string);
if (r < 0)
goto finish;
@@ -2900,35 +3280,25 @@ finish:
return r;
}
-int bus_remove_match_by_string(
+_public_ int sd_bus_add_match(
sd_bus *bus,
+ sd_bus_slot **slot,
const char *match,
sd_bus_message_handler_t callback,
void *userdata) {
- struct bus_match_component *components = NULL;
- unsigned n_components = 0;
- struct match_callback *c;
- int r = 0;
-
- assert_return(bus, -EINVAL);
- assert_return(match, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
-
- r = bus_match_parse(match, &components, &n_components);
- if (r < 0)
- goto finish;
-
- r = bus_match_find(&bus->match_callbacks, components, n_components, NULL, NULL, &c);
- if (r <= 0)
- goto finish;
-
- sd_bus_slot_unref(container_of(c, sd_bus_slot, match_callback));
+ return bus_add_match_full(bus, slot, false, match, callback, NULL, userdata);
+}
-finish:
- bus_match_parse_free(components, n_components);
+_public_ int sd_bus_add_match_async(
+ sd_bus *bus,
+ sd_bus_slot **slot,
+ const char *match,
+ sd_bus_message_handler_t callback,
+ sd_bus_message_handler_t install_callback,
+ void *userdata) {
- return r;
+ return bus_add_match_full(bus, slot, true, match, callback, install_callback, userdata);
}
bool bus_pid_changed(sd_bus *bus) {
@@ -2946,9 +3316,13 @@ static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userd
assert(bus);
+ /* Note that this is called both on input_fd, output_fd as well as inotify_fd events */
+
r = sd_bus_process(bus, NULL);
- if (r < 0)
- return r;
+ if (r < 0) {
+ log_debug_errno(r, "Processing of bus failed, closing down: %m");
+ bus_enter_closing(bus);
+ }
return 1;
}
@@ -2960,8 +3334,10 @@ static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
assert(bus);
r = sd_bus_process(bus, NULL);
- if (r < 0)
- return r;
+ if (r < 0) {
+ log_debug_errno(r, "Processing of bus failed, closing down: %m");
+ bus_enter_closing(bus);
+ }
return 1;
}
@@ -2975,38 +3351,45 @@ static int prepare_callback(sd_event_source *s, void *userdata) {
assert(bus);
e = sd_bus_get_events(bus);
- if (e < 0)
- return e;
+ if (e < 0) {
+ r = e;
+ goto fail;
+ }
if (bus->output_fd != bus->input_fd) {
r = sd_event_source_set_io_events(bus->input_io_event_source, e & POLLIN);
if (r < 0)
- return r;
+ goto fail;
r = sd_event_source_set_io_events(bus->output_io_event_source, e & POLLOUT);
- if (r < 0)
- return r;
- } else {
+ } else
r = sd_event_source_set_io_events(bus->input_io_event_source, e);
- if (r < 0)
- return r;
- }
+ if (r < 0)
+ goto fail;
r = sd_bus_get_timeout(bus, &until);
if (r < 0)
- return r;
+ goto fail;
if (r > 0) {
int j;
j = sd_event_source_set_time(bus->time_event_source, until);
- if (j < 0)
- return j;
+ if (j < 0) {
+ r = j;
+ goto fail;
+ }
}
r = sd_event_source_set_enabled(bus->time_event_source, r > 0);
if (r < 0)
- return r;
+ goto fail;
+
+ return 1;
+
+fail:
+ log_debug_errno(r, "Preparing of bus events failed, closing down: %m");
+ bus_enter_closing(bus);
return 1;
}
@@ -3022,7 +3405,7 @@ static int quit_callback(sd_event_source *event, void *userdata) {
return 1;
}
-static int attach_io_events(sd_bus *bus) {
+int bus_attach_io_events(sd_bus *bus) {
int r;
assert(bus);
@@ -3076,7 +3459,7 @@ static int attach_io_events(sd_bus *bus) {
return 0;
}
-static void detach_io_events(sd_bus *bus) {
+static void bus_detach_io_events(sd_bus *bus) {
assert(bus);
if (bus->input_io_event_source) {
@@ -3090,10 +3473,49 @@ static void detach_io_events(sd_bus *bus) {
}
}
+int bus_attach_inotify_event(sd_bus *bus) {
+ int r;
+
+ assert(bus);
+
+ if (bus->inotify_fd < 0)
+ return 0;
+
+ if (!bus->event)
+ return 0;
+
+ if (!bus->inotify_event_source) {
+ r = sd_event_add_io(bus->event, &bus->inotify_event_source, bus->inotify_fd, EPOLLIN, io_callback, bus);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(bus->inotify_event_source, bus->event_priority);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_description(bus->inotify_event_source, "bus-inotify");
+ } else
+ r = sd_event_source_set_io_fd(bus->inotify_event_source, bus->inotify_fd);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static void bus_detach_inotify_event(sd_bus *bus) {
+ assert(bus);
+
+ if (bus->inotify_event_source) {
+ sd_event_source_set_enabled(bus->inotify_event_source, SD_EVENT_OFF);
+ bus->inotify_event_source = sd_event_source_unref(bus->inotify_event_source);
+ }
+}
+
_public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) {
int r;
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus->event, -EBUSY);
assert(!bus->input_io_event_source);
@@ -3130,7 +3552,11 @@ _public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) {
if (r < 0)
goto fail;
- r = attach_io_events(bus);
+ r = bus_attach_io_events(bus);
+ if (r < 0)
+ goto fail;
+
+ r = bus_attach_inotify_event(bus);
if (r < 0)
goto fail;
@@ -3143,11 +3569,13 @@ fail:
_public_ int sd_bus_detach_event(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
if (!bus->event)
return 0;
- detach_io_events(bus);
+ bus_detach_io_events(bus);
+ bus_detach_inotify_event(bus);
if (bus->time_event_source) {
sd_event_source_set_enabled(bus->time_event_source, SD_EVENT_OFF);
@@ -3230,39 +3658,11 @@ _public_ int sd_bus_default_user(sd_bus **ret) {
}
_public_ int sd_bus_default(sd_bus **ret) {
+ int (*bus_open)(sd_bus **) = NULL;
+ sd_bus **busp;
- const char *e;
-
- /* Let's try our best to reuse another cached connection. If
- * the starter bus type is set, connect via our normal
- * connection logic, ignoring $DBUS_STARTER_ADDRESS, so that
- * we can share the connection with the user/system default
- * bus. */
-
- e = secure_getenv("DBUS_STARTER_BUS_TYPE");
- if (e) {
- if (streq(e, "system"))
- return sd_bus_default_system(ret);
- else if (STR_IN_SET(e, "user", "session"))
- return sd_bus_default_user(ret);
- }
-
- /* No type is specified, so we have not other option than to
- * use the starter address if it is set. */
-
- e = secure_getenv("DBUS_STARTER_ADDRESS");
- if (e) {
-
- return bus_default(sd_bus_open, &default_starter_bus, ret);
- }
-
- /* Finally, if nothing is set use the cached connection for
- * the right scope */
-
- if (cg_pid_get_owner_uid(0, NULL) >= 0)
- return sd_bus_default_user(ret);
- else
- return sd_bus_default_system(ret);
+ busp = bus_choose_default(&bus_open);
+ return bus_default(bus_open, busp, ret);
}
_public_ int sd_bus_get_tid(sd_bus *b, pid_t *tid) {
@@ -3483,13 +3883,13 @@ _public_ int sd_bus_path_decode_many(const char *path, const char *path_template
}
va_end(list);
- free(labels);
- labels = NULL;
+ labels = mfree(labels);
return 1;
}
_public_ int sd_bus_try_close(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return -EOPNOTSUPP;
@@ -3497,6 +3897,7 @@ _public_ int sd_bus_try_close(sd_bus *bus) {
_public_ int sd_bus_get_description(sd_bus *bus, const char **description) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(description, -EINVAL);
assert_return(bus->description, -ENXIO);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -3525,6 +3926,7 @@ int bus_get_root_path(sd_bus *bus) {
_public_ int sd_bus_get_scope(sd_bus *bus, const char **scope) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(scope, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -3544,6 +3946,7 @@ _public_ int sd_bus_get_scope(sd_bus *bus, const char **scope) {
_public_ int sd_bus_get_address(sd_bus *bus, const char **address) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(address, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -3557,6 +3960,7 @@ _public_ int sd_bus_get_address(sd_bus *bus, const char **address) {
_public_ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(mask, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -3566,6 +3970,7 @@ _public_ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) {
_public_ int sd_bus_is_bus_client(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return bus->bus_client;
@@ -3573,6 +3978,7 @@ _public_ int sd_bus_is_bus_client(sd_bus *bus) {
_public_ int sd_bus_is_server(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return bus->is_server;
@@ -3580,6 +3986,7 @@ _public_ int sd_bus_is_server(sd_bus *bus) {
_public_ int sd_bus_is_anonymous(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return bus->anonymous_auth;
@@ -3587,6 +3994,7 @@ _public_ int sd_bus_is_anonymous(sd_bus *bus) {
_public_ int sd_bus_is_trusted(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return bus->trusted;
@@ -3594,9 +4002,10 @@ _public_ int sd_bus_is_trusted(sd_bus *bus) {
_public_ int sd_bus_is_monitor(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
- return !!(bus->hello_flags & KDBUS_HELLO_MONITOR);
+ return bus->is_monitor;
}
static void flush_close(sd_bus *bus) {
@@ -3618,6 +4027,7 @@ _public_ void sd_bus_default_flush_close(void) {
_public_ int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
/* Turns on exit-on-disconnect, and triggers it immediately if the bus connection was already
* disconnected. Note that this is triggered exclusively on disconnections triggered by the server side, never
@@ -3630,6 +4040,28 @@ _public_ int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b) {
_public_ int sd_bus_get_exit_on_disconnect(sd_bus *bus) {
assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
return bus->exit_on_disconnect;
}
+
+_public_ int sd_bus_set_sender(sd_bus *bus, const char *sender) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(!bus->bus_client, -EPERM);
+ assert_return(!sender || service_name_is_valid(sender), -EINVAL);
+
+ return free_and_strdup(&bus->patch_sender, sender);
+}
+
+_public_ int sd_bus_get_sender(sd_bus *bus, const char **ret) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(ret, -EINVAL);
+
+ if (!bus->patch_sender)
+ return -ENODATA;
+
+ *ret = bus->patch_sender;
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-benchmark.c b/src/libsystemd/sd-bus/test-bus-benchmark.c
index a466cddd75..bfd0f39372 100644
--- a/src/libsystemd/sd-bus/test-bus-benchmark.c
+++ b/src/libsystemd/sd-bus/test-bus-benchmark.c
@@ -319,7 +319,7 @@ int main(int argc, char *argv[]) {
break;
}
- _exit(0);
+ _exit(EXIT_SUCCESS);
}
CPU_ZERO(&cpuset);
diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c
index 1b2efb9bb4..bd6721946a 100644
--- a/src/libsystemd/sd-bus/test-bus-chat.c
+++ b/src/libsystemd/sd-bus/test-bus-chat.c
@@ -102,9 +102,9 @@ static int server_init(sd_bus **_bus) {
goto fail;
}
- r = sd_bus_add_match(bus, NULL, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL);
+ r = sd_bus_match_signal(bus, NULL, NULL, NULL, "foo.bar", "Notify", match_callback, NULL);
if (r < 0) {
- log_error_errno(r, "Failed to add match: %m");
+ log_error_errno(r, "Failed to request match: %m");
goto fail;
}
diff --git a/src/libsystemd/sd-bus/test-bus-track.c b/src/libsystemd/sd-bus/test-bus-track.c
index 320e8347f4..94c9d09de3 100644
--- a/src/libsystemd/sd-bus/test-bus-track.c
+++ b/src/libsystemd/sd-bus/test-bus-track.c
@@ -18,6 +18,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
+#include <sys/socket.h>
+
#include "sd-bus.h"
#include "macro.h"
diff --git a/src/libsystemd/sd-bus/test-bus-watch-bind.c b/src/libsystemd/sd-bus/test-bus-watch-bind.c
new file mode 100644
index 0000000000..aef5ba9486
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-watch-bind.c
@@ -0,0 +1,239 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/***
+ This file is part of systemd.
+
+ Copyright 2017 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pthread.h>
+
+#include "sd-bus.h"
+#include "sd-event.h"
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "random-util.h"
+#include "rm-rf.h"
+#include "socket-util.h"
+#include "string-util.h"
+
+static int method_foobar(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ log_info("Got Foobar() call.");
+
+ assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 0) >= 0);
+ return sd_bus_reply_method_return(m, NULL);
+}
+
+static int method_exit(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ log_info("Got Exit() call");
+ assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 1) >= 0);
+ return sd_bus_reply_method_return(m, NULL);
+}
+
+static const sd_bus_vtable vtable[] = {
+ SD_BUS_VTABLE_START(0),
+ SD_BUS_METHOD("Foobar", NULL, NULL, method_foobar, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Exit", NULL, NULL, method_exit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_VTABLE_END,
+};
+
+static void* thread_server(void *p) {
+ _cleanup_free_ char *suffixed = NULL, *suffixed2 = NULL, *d = NULL;
+ _cleanup_close_ int fd = -1;
+ union sockaddr_union u = {
+ .un.sun_family = AF_UNIX,
+ };
+ const char *path = p;
+
+ log_debug("Initializing server");
+
+ /* Let's play some games, by slowly creating the socket directory, and renaming it in the middle */
+ (void) usleep(100 * USEC_PER_MSEC);
+
+ assert_se(mkdir_parents(path, 0755) >= 0);
+ (void) usleep(100 * USEC_PER_MSEC);
+
+ d = dirname_malloc(path);
+ assert_se(d);
+ assert_se(asprintf(&suffixed, "%s.%" PRIx64, d, random_u64()) >= 0);
+ assert_se(rename(d, suffixed) >= 0);
+ (void) usleep(100 * USEC_PER_MSEC);
+
+ assert_se(asprintf(&suffixed2, "%s.%" PRIx64, d, random_u64()) >= 0);
+ assert_se(symlink(suffixed2, d) >= 0);
+ (void) usleep(100 * USEC_PER_MSEC);
+
+ assert_se(symlink(basename(suffixed), suffixed2) >= 0);
+ (void) usleep(100 * USEC_PER_MSEC);
+
+ strncpy(u.un.sun_path, path, sizeof(u.un.sun_path));
+
+ fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+ assert_se(fd >= 0);
+
+ assert_se(bind(fd, &u.sa, SOCKADDR_UN_LEN(u.un)) >= 0);
+ usleep(100 * USEC_PER_MSEC);
+
+ assert_se(listen(fd, SOMAXCONN) >= 0);
+ usleep(100 * USEC_PER_MSEC);
+
+ assert_se(touch(path) >= 0);
+ usleep(100 * USEC_PER_MSEC);
+
+ log_debug("Initialized server");
+
+ for (;;) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ sd_id128_t id;
+ int bus_fd, code;
+
+ assert_se(sd_id128_randomize(&id) >= 0);
+
+ assert_se(sd_event_new(&event) >= 0);
+
+ bus_fd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
+ assert_se(bus_fd >= 0);
+
+ log_debug("Accepted server connection");
+
+ assert_se(sd_bus_new(&bus) >= 0);
+ assert_se(sd_bus_set_description(bus, "server") >= 0);
+ assert_se(sd_bus_set_fd(bus, bus_fd, bus_fd) >= 0);
+ assert_se(sd_bus_set_server(bus, true, id) >= 0);
+ /* assert_se(sd_bus_set_anonymous(bus, true) >= 0); */
+
+ assert_se(sd_bus_attach_event(bus, event, 0) >= 0);
+
+ assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "foo.TestInterface", vtable, NULL) >= 0);
+
+ assert_se(sd_bus_start(bus) >= 0);
+
+ assert_se(sd_event_loop(event) >= 0);
+
+ assert_se(sd_event_get_exit_code(event, &code) >= 0);
+
+ if (code > 0)
+ break;
+ }
+
+ log_debug("Server done");
+
+ return NULL;
+}
+
+static void* thread_client1(void *p) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ const char *path = p, *t;
+ int r;
+
+ log_debug("Initializing client1");
+
+ assert_se(sd_bus_new(&bus) >= 0);
+ assert_se(sd_bus_set_description(bus, "client1") >= 0);
+
+ t = strjoina("unix:path=", path);
+ assert_se(sd_bus_set_address(bus, t) >= 0);
+ assert_se(sd_bus_set_watch_bind(bus, true) >= 0);
+ assert_se(sd_bus_start(bus) >= 0);
+
+ r = sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Foobar", &error, NULL, NULL);
+ assert_se(r >= 0);
+
+ log_debug("Client1 done");
+
+ return NULL;
+}
+
+static int client2_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ assert_se(sd_bus_message_is_method_error(m, NULL) == 0);
+ assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 0) >= 0);
+ return 0;
+}
+
+static void* thread_client2(void *p) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ const char *path = p, *t;
+
+ log_debug("Initializing client2");
+
+ assert_se(sd_event_new(&event) >= 0);
+ assert_se(sd_bus_new(&bus) >= 0);
+ assert_se(sd_bus_set_description(bus, "client2") >= 0);
+
+ t = strjoina("unix:path=", path);
+ assert_se(sd_bus_set_address(bus, t) >= 0);
+ assert_se(sd_bus_set_watch_bind(bus, true) >= 0);
+ assert_se(sd_bus_attach_event(bus, event, 0) >= 0);
+ assert_se(sd_bus_start(bus) >= 0);
+
+ assert_se(sd_bus_call_method_async(bus, NULL, "foo.bar", "/foo", "foo.TestInterface", "Foobar", client2_callback, NULL, NULL) >= 0);
+
+ assert_se(sd_event_loop(event) >= 0);
+
+ log_debug("Client2 done");
+
+ return NULL;
+}
+
+static void request_exit(const char *path) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ const char *t;
+
+ assert_se(sd_bus_new(&bus) >= 0);
+
+ t = strjoina("unix:path=", path);
+ assert_se(sd_bus_set_address(bus, t) >= 0);
+ assert_se(sd_bus_set_watch_bind(bus, true) >= 0);
+ assert_se(sd_bus_set_description(bus, "request-exit") >= 0);
+ assert_se(sd_bus_start(bus) >= 0);
+
+ assert_se(sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Exit", NULL, NULL, NULL) >= 0);
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
+ pthread_t server, client1, client2;
+ char *path;
+
+ log_set_max_level(LOG_DEBUG);
+
+ /* We use /dev/shm here rather than /tmp, since some weird distros might set up /tmp as some weird fs that
+ * doesn't support inotify properly. */
+ assert_se(mkdtemp_malloc("/dev/shm/systemd-watch-bind-XXXXXX", &d) >= 0);
+
+ path = strjoina(d, "/this/is/a/socket");
+
+ assert_se(pthread_create(&server, NULL, thread_server, path) == 0);
+ assert_se(pthread_create(&client1, NULL, thread_client1, path) == 0);
+ assert_se(pthread_create(&client2, NULL, thread_client2, path) == 0);
+
+ assert_se(pthread_join(client1, NULL) == 0);
+ assert_se(pthread_join(client2, NULL) == 0);
+
+ request_exit(path);
+
+ assert_se(pthread_join(server, NULL) == 0);
+
+ return 0;
+}