]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/libsystemd/sd-bus/sd-bus.c
tree-wide: use CMP() macros where applicable
[thirdparty/systemd.git] / src / libsystemd / sd-bus / sd-bus.c
index 8c022fce1bec2dfc9998611f9fe8c350cc50e1c7..1437df6cf36ced4a8029343b40680875dcf7982e 100644 (file)
@@ -1,22 +1,4 @@
 /* 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 <endian.h>
 #include <netdb.h>
@@ -176,7 +158,7 @@ static void bus_reset_queues(sd_bus *b) {
         b->wqueue_allocated = 0;
 }
 
-static void bus_free(sd_bus *b) {
+static sd_bus* bus_free(sd_bus *b) {
         sd_bus_slot *s;
 
         assert(b);
@@ -196,8 +178,7 @@ static void bus_free(sd_bus *b) {
                  * apps, but are dead. */
 
                 assert(s->floating);
-                bus_slot_disconnect(s);
-                sd_bus_slot_unref(s);
+                bus_slot_disconnect(s, true);
         }
 
         if (b->default_bus_ptr)
@@ -241,56 +222,47 @@ static void bus_free(sd_bus *b) {
 
         assert_se(pthread_mutex_destroy(&b->memfd_cache_mutex) == 0);
 
-        free(b);
+        return mfree(b);
 }
 
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus*, bus_free);
+
 _public_ int sd_bus_new(sd_bus **ret) {
-        sd_bus *r;
+        _cleanup_free_ sd_bus *b = NULL;
 
         assert_return(ret, -EINVAL);
 
-        r = new0(sd_bus, 1);
-        if (!r)
+        b = new0(sd_bus, 1);
+        if (!b)
                 return -ENOMEM;
 
-        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->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);
-
-        /* We guarantee that wqueue always has space for at least one
-         * entry */
-        if (!GREEDY_REALLOC(r->wqueue, r->wqueue_allocated, 1)) {
-                free(r);
+        b->n_ref = REFCNT_INIT;
+        b->input_fd = b->output_fd = -1;
+        b->inotify_fd = -1;
+        b->message_version = 1;
+        b->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME;
+        b->accept_fd = true;
+        b->original_pid = getpid_cached();
+        b->n_groups = (size_t) -1;
+
+        assert_se(pthread_mutex_init(&b->memfd_cache_mutex, NULL) == 0);
+
+        /* We guarantee that wqueue always has space for at least one entry */
+        if (!GREEDY_REALLOC(b->wqueue, b->wqueue_allocated, 1))
                 return -ENOMEM;
-        }
 
-        *ret = r;
+        *ret = TAKE_PTR(b);
         return 0;
 }
 
 _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);
 
-        a = strdup(address);
-        if (!a)
-                return -ENOMEM;
-
-        free_and_replace(bus->address, a);
-
-        return 0;
+        return free_and_strdup(&bus->address, address);
 }
 
 _public_ int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) {
@@ -307,7 +279,8 @@ _public_ int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) {
 }
 
 _public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) {
-        char *p, **a;
+        _cleanup_strv_free_ char **a = NULL;
+        int r;
 
         assert_return(bus, -EINVAL);
         assert_return(bus = bus_resolve(bus), -ENOPKG);
@@ -316,22 +289,15 @@ _public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[])
         assert_return(!strv_isempty(argv), -EINVAL);
         assert_return(!bus_pid_changed(bus), -ECHILD);
 
-        p = strdup(path);
-        if (!p)
-                return -ENOMEM;
-
         a = strv_copy(argv);
-        if (!a) {
-                free(p);
+        if (!a)
                 return -ENOMEM;
-        }
-
-        free_and_replace(bus->exec_path, p);
 
-        strv_free(bus->exec_argv);
-        bus->exec_argv = a;
+        r = free_and_strdup(&bus->exec_path, path);
+        if (r < 0)
+                return r;
 
-        return 0;
+        return strv_free_and_replace(bus->exec_argv, a);
 }
 
 _public_ int sd_bus_set_bus_client(sd_bus *bus, int b) {
@@ -524,8 +490,7 @@ static int synthesize_connected_signal(sd_bus *bus) {
 
         /* 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[0] = TAKE_PTR(m);
         bus->rqueue_size++;
 
         return 0;
@@ -556,7 +521,6 @@ void bus_set_state(sd_bus *bus, enum bus_state state) {
 
 static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
         const char *s;
-        char *t;
         sd_bus *bus;
         int r;
 
@@ -576,11 +540,9 @@ static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *e
         if (!service_name_is_valid(s) || s[0] != ':')
                 return -EBADMSG;
 
-        t = strdup(s);
-        if (!t)
-                return -ENOMEM;
-
-        free_and_replace(bus->unique_name, t);
+        r = free_and_strdup(&bus->unique_name, s);
+        if (r < 0)
+                return r;
 
         if (bus->state == BUS_HELLO) {
                 bus_set_state(bus, BUS_RUNNING);
@@ -987,7 +949,7 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid)
                 return -EINVAL;
 
         if (machine) {
-                if (!machine_name_is_valid(machine))
+                if (!streq(machine, ".host") && !machine_name_is_valid(machine))
                         return -EINVAL;
 
                 free_and_replace(b->machine, machine);
@@ -1220,9 +1182,9 @@ _public_ int sd_bus_start(sd_bus *bus) {
         return bus_send_hello(bus);
 }
 
-_public_ int sd_bus_open(sd_bus **ret) {
+_public_ int sd_bus_open_with_description(sd_bus **ret, const char *description) {
         const char *e;
-        sd_bus *b;
+        _cleanup_(bus_freep) sd_bus *b = NULL;
         int r;
 
         assert_return(ret, -EINVAL);
@@ -1234,17 +1196,17 @@ _public_ int sd_bus_open(sd_bus **ret) {
         e = secure_getenv("DBUS_STARTER_BUS_TYPE");
         if (e) {
                 if (streq(e, "system"))
-                        return sd_bus_open_system(ret);
+                        return sd_bus_open_system_with_description(ret, description);
                 else if (STR_IN_SET(e, "session", "user"))
-                        return sd_bus_open_user(ret);
+                        return sd_bus_open_user_with_description(ret, description);
         }
 
         e = secure_getenv("DBUS_STARTER_ADDRESS");
         if (!e) {
                 if (cg_pid_get_owner_uid(0, NULL) >= 0)
-                        return sd_bus_open_user(ret);
+                        return sd_bus_open_user_with_description(ret, description);
                 else
-                        return sd_bus_open_system(ret);
+                        return sd_bus_open_system_with_description(ret, description);
         }
 
         r = sd_bus_new(&b);
@@ -1253,7 +1215,7 @@ _public_ int sd_bus_open(sd_bus **ret) {
 
         r = sd_bus_set_address(b, e);
         if (r < 0)
-                goto fail;
+                return r;
 
         b->bus_client = true;
 
@@ -1265,14 +1227,14 @@ _public_ int sd_bus_open(sd_bus **ret) {
 
         r = sd_bus_start(b);
         if (r < 0)
-                goto fail;
+                return r;
 
-        *ret = b;
+        *ret = TAKE_PTR(b);
         return 0;
+}
 
-fail:
-        bus_free(b);
-        return r;
+_public_ int sd_bus_open(sd_bus **ret) {
+        return sd_bus_open_with_description(ret, NULL);
 }
 
 int bus_set_address_system(sd_bus *b) {
@@ -1286,8 +1248,8 @@ int bus_set_address_system(sd_bus *b) {
         return sd_bus_set_address(b, DEFAULT_SYSTEM_BUS_ADDRESS);
 }
 
-_public_ int sd_bus_open_system(sd_bus **ret) {
-        sd_bus *b;
+_public_ int sd_bus_open_system_with_description(sd_bus **ret, const char *description) {
+        _cleanup_(bus_freep) sd_bus *b = NULL;
         int r;
 
         assert_return(ret, -EINVAL);
@@ -1296,9 +1258,15 @@ _public_ int sd_bus_open_system(sd_bus **ret) {
         if (r < 0)
                 return r;
 
+        if (description) {
+                r = sd_bus_set_description(b, description);
+                if (r < 0)
+                        return r;
+        }
+
         r = bus_set_address_system(b);
         if (r < 0)
-                goto fail;
+                return r;
 
         b->bus_client = true;
         b->is_system = true;
@@ -1311,14 +1279,14 @@ _public_ int sd_bus_open_system(sd_bus **ret) {
 
         r = sd_bus_start(b);
         if (r < 0)
-                goto fail;
+                return r;
 
-        *ret = b;
+        *ret = TAKE_PTR(b);
         return 0;
+}
 
-fail:
-        bus_free(b);
-        return r;
+_public_ int sd_bus_open_system(sd_bus **ret) {
+        return sd_bus_open_system_with_description(ret, NULL);
 }
 
 int bus_set_address_user(sd_bus *b) {
@@ -1347,8 +1315,8 @@ int bus_set_address_user(sd_bus *b) {
         return 0;
 }
 
-_public_ int sd_bus_open_user(sd_bus **ret) {
-        sd_bus *b;
+_public_ int sd_bus_open_user_with_description(sd_bus **ret, const char *description) {
+        _cleanup_(bus_freep) sd_bus *b = NULL;
         int r;
 
         assert_return(ret, -EINVAL);
@@ -1357,9 +1325,15 @@ _public_ int sd_bus_open_user(sd_bus **ret) {
         if (r < 0)
                 return r;
 
+        if (description) {
+                r = sd_bus_set_description(b, description);
+                if (r < 0)
+                        return r;
+        }
+
         r = bus_set_address_user(b);
         if (r < 0)
-                goto fail;
+                return r;
 
         b->bus_client = true;
         b->is_user = true;
@@ -1371,88 +1345,134 @@ _public_ int sd_bus_open_user(sd_bus **ret) {
 
         r = sd_bus_start(b);
         if (r < 0)
-                goto fail;
+                return r;
 
-        *ret = b;
+        *ret = TAKE_PTR(b);
         return 0;
+}
 
-fail:
-        bus_free(b);
-        return r;
+_public_ int sd_bus_open_user(sd_bus **ret) {
+        return sd_bus_open_user_with_description(ret, NULL);
 }
 
 int bus_set_address_system_remote(sd_bus *b, const char *host) {
         _cleanup_free_ char *e = NULL;
-        char *m = NULL, *c = NULL, *a;
+        char *m = NULL, *c = NULL, *a, *rbracket = NULL, *p = NULL;
 
         assert(b);
         assert(host);
 
-        /* Let's see if we shall enter some container */
-        m = strchr(host, ':');
-        if (m) {
-                m++;
-
-                /* Let's make sure this is not a port of some kind,
-                 * and is a valid machine name. */
-                if (!in_charset(m, "0123456789") && machine_name_is_valid(m)) {
-                        char *t;
+        /* Skip ":"s in ipv6 addresses */
+        if (*host == '[') {
+                char *t;
 
-                        /* Cut out the host part */
-                        t = strndupa(host, m - host - 1);
+                rbracket = strchr(host, ']');
+                if (!rbracket)
+                        return -EINVAL;
+                t = strndupa(host + 1, rbracket - host - 1);
+                e = bus_address_escape(t);
+                if (!e)
+                        return -ENOMEM;
+        } else if ((a = strchr(host, '@'))) {
+                if (*(a + 1) == '[') {
+                        _cleanup_free_ char *t = NULL;
+
+                        rbracket = strchr(a + 1, ']');
+                        if (!rbracket)
+                                return -EINVAL;
+                        t = new0(char, strlen(host));
+                        if (!t)
+                                return -ENOMEM;
+                        strncat(t, host, a - host + 1);
+                        strncat(t, a + 2, rbracket - a - 2);
                         e = bus_address_escape(t);
                         if (!e)
                                 return -ENOMEM;
+                } else if (*(a + 1) == '\0' || strchr(a + 1, '@'))
+                        return -EINVAL;
+        }
+
+        /* Let's see if a port was given */
+        m = strchr(rbracket ? rbracket + 1 : host, ':');
+        if (m) {
+                char *t;
+                bool got_forward_slash = false;
 
-                        c = strjoina(",argv5=--machine=", m);
+                p = m + 1;
+
+                t = strchr(p, '/');
+                if (t) {
+                        p = strndupa(p, t - p);
+                        got_forward_slash = true;
+                }
+
+                if (!in_charset(p, "0123456789") || *p == '\0') {
+                        if (!machine_name_is_valid(p) || got_forward_slash)
+                                return -EINVAL;
+                        else {
+                                m = p;
+                                p = NULL;
+                                goto interpret_port_as_machine_old_syntax;
+                        }
                 }
         }
 
+        /* Let's see if a machine was given */
+        m = strchr(rbracket ? rbracket + 1 : host, '/');
+        if (m) {
+                m++;
+interpret_port_as_machine_old_syntax:
+                /* Let's make sure this is not a port of some kind,
+                 * and is a valid machine name. */
+                if (!in_charset(m, "0123456789") && machine_name_is_valid(m))
+                        c = strjoina(",argv", p ? "7" : "5", "=--machine=", m);
+        }
+
         if (!e) {
-                e = bus_address_escape(host);
+                char *t;
+
+                t = strndupa(host, strcspn(host, ":/"));
+
+                e = bus_address_escape(t);
                 if (!e)
                         return -ENOMEM;
         }
 
-        a = strjoin("unixexec:path=ssh,argv1=-xT,argv2=--,argv3=", e, ",argv4=systemd-stdio-bridge", c);
+        a = strjoin("unixexec:path=ssh,argv1=-xT", p ? ",argv2=-p,argv3=" : "", strempty(p),
+                                ",argv", p ? "4" : "2", "=--,argv", p ? "5" : "3", "=", e,
+                                ",argv", p ? "6" : "4", "=systemd-stdio-bridge", c);
         if (!a)
                 return -ENOMEM;
 
-        free_and_replace(b->address, a);
-
-        return 0;
- }
+        return free_and_replace(b->address, a);
+}
 
 _public_ int sd_bus_open_system_remote(sd_bus **ret, const char *host) {
-        sd_bus *bus;
+        _cleanup_(bus_freep) sd_bus *b = NULL;
         int r;
 
         assert_return(host, -EINVAL);
         assert_return(ret, -EINVAL);
 
-        r = sd_bus_new(&bus);
+        r = sd_bus_new(&b);
         if (r < 0)
                 return r;
 
-        r = bus_set_address_system_remote(bus, host);
+        r = bus_set_address_system_remote(b, host);
         if (r < 0)
-                goto fail;
+                return r;
 
-        bus->bus_client = true;
-        bus->trusted = false;
-        bus->is_system = true;
-        bus->is_local = false;
+        b->bus_client = true;
+        b->trusted = false;
+        b->is_system = true;
+        b->is_local = false;
 
-        r = sd_bus_start(bus);
+        r = sd_bus_start(b);
         if (r < 0)
-                goto fail;
+                return r;
 
-        *ret = bus;
+        *ret = TAKE_PTR(b);
         return 0;
-
-fail:
-        bus_free(bus);
-        return r;
 }
 
 int bus_set_address_system_machine(sd_bus *b, const char *machine) {
@@ -1470,46 +1490,39 @@ int bus_set_address_system_machine(sd_bus *b, const char *machine) {
         if (!a)
                 return -ENOMEM;
 
-        free_and_replace(b->address, a);
-
-        return 0;
+        return free_and_replace(b->address, a);
 }
 
 _public_ int sd_bus_open_system_machine(sd_bus **ret, const char *machine) {
-        sd_bus *bus;
+        _cleanup_(bus_freep) sd_bus *b = NULL;
         int r;
 
         assert_return(machine, -EINVAL);
         assert_return(ret, -EINVAL);
-        assert_return(machine_name_is_valid(machine), -EINVAL);
+        assert_return(streq(machine, ".host") || machine_name_is_valid(machine), -EINVAL);
 
-        r = sd_bus_new(&bus);
+        r = sd_bus_new(&b);
         if (r < 0)
                 return r;
 
-        r = bus_set_address_system_machine(bus, machine);
+        r = bus_set_address_system_machine(b, machine);
         if (r < 0)
-                goto fail;
+                return r;
 
-        bus->bus_client = true;
-        bus->trusted = false;
-        bus->is_system = true;
-        bus->is_local = false;
+        b->bus_client = true;
+        b->trusted = false;
+        b->is_system = true;
+        b->is_local = false;
 
-        r = sd_bus_start(bus);
+        r = sd_bus_start(b);
         if (r < 0)
-                goto fail;
+                return r;
 
-        *ret = bus;
+        *ret = TAKE_PTR(b);
         return 0;
-
-fail:
-        bus_free(bus);
-        return r;
 }
 
 _public_ void sd_bus_close(sd_bus *bus) {
-
         if (!bus)
                 return;
         if (bus->state == BUS_CLOSED)
@@ -1533,7 +1546,6 @@ _public_ void sd_bus_close(sd_bus *bus) {
 }
 
 _public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) {
-
         if (!bus)
                 return NULL;
 
@@ -1555,32 +1567,9 @@ void bus_enter_closing(sd_bus *bus) {
         bus_set_state(bus, BUS_CLOSING);
 }
 
-_public_ sd_bus *sd_bus_ref(sd_bus *bus) {
-
-        if (!bus)
-                return NULL;
-
-        assert_se(REFCNT_INC(bus->n_ref) >= 2);
-
-        return bus;
-}
-
-_public_ sd_bus *sd_bus_unref(sd_bus *bus) {
-        unsigned i;
-
-        if (!bus)
-                return NULL;
-
-        i = REFCNT_DEC(bus->n_ref);
-        if (i > 0)
-                return NULL;
-
-        bus_free(bus);
-        return NULL;
-}
+DEFINE_PUBLIC_ATOMIC_REF_UNREF_FUNC(sd_bus, sd_bus, bus_free);
 
 _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);
@@ -1651,8 +1640,11 @@ static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) {
                 return 0;
         }
 
-        if (timeout == 0)
-                timeout = BUS_DEFAULT_TIMEOUT;
+        if (timeout == 0) {
+                r = sd_bus_get_method_call_timeout(b, &timeout);
+                if (r < 0)
+                        return r;
+        }
 
         if (!m->sender && b->patch_sender) {
                 r = sd_bus_message_set_sender(m, b->patch_sender);
@@ -1953,13 +1945,7 @@ static int timeout_compare(const void *a, const void *b) {
         if (x->timeout_usec == 0 && y->timeout_usec != 0)
                 return 1;
 
-        if (x->timeout_usec < y->timeout_usec)
-                return -1;
-
-        if (x->timeout_usec > y->timeout_usec)
-                return 1;
-
-        return 0;
+        return CMP(x->timeout_usec, y->timeout_usec);
 }
 
 _public_ int sd_bus_call_async(
@@ -2399,10 +2385,8 @@ static int process_timeout(sd_bus *bus) {
         bus->current_slot = NULL;
         bus->current_message = NULL;
 
-        if (slot->floating) {
-                bus_slot_disconnect(slot);
-                sd_bus_slot_unref(slot);
-        }
+        if (slot->floating)
+                bus_slot_disconnect(slot, true);
 
         sd_bus_slot_unref(slot);
 
@@ -2504,10 +2488,8 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
         bus->current_handler = NULL;
         bus->current_slot = NULL;
 
-        if (slot->floating) {
-                bus_slot_disconnect(slot);
-                sd_bus_slot_unref(slot);
-        }
+        if (slot->floating)
+                bus_slot_disconnect(slot, true);
 
         sd_bus_slot_unref(slot);
 
@@ -2751,8 +2733,8 @@ static int process_running(sd_bus *bus, bool hint_priority, int64_t priority, sd
                 if (r < 0)
                         return r;
 
-                *ret = m;
-                m = NULL;
+                *ret = TAKE_PTR(m);
+
                 return 1;
         }
 
@@ -2849,10 +2831,8 @@ static int process_closing_reply_callback(sd_bus *bus, struct reply_callback *c)
         bus->current_slot = NULL;
         bus->current_message = NULL;
 
-        if (slot->floating) {
-                bus_slot_disconnect(slot);
-                sd_bus_slot_unref(slot);
-        }
+        if (slot->floating)
+                bus_slot_disconnect(slot, true);
 
         sd_bus_slot_unref(slot);
 
@@ -2911,10 +2891,8 @@ static int process_closing(sd_bus *bus, sd_bus_message **ret) {
         bus->exit_triggered = true;
         (void) bus_exit_now(bus);
 
-        if (ret) {
-                *ret = m;
-                m = NULL;
-        }
+        if (ret)
+                *ret = TAKE_PTR(m);
 
         r = 1;
 
@@ -2925,7 +2903,6 @@ finish:
 }
 
 static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) {
-        BUS_DONT_DESTROY(bus);
         int r;
 
         /* Returns 0 when we didn't do anything. This should cause the
@@ -2939,7 +2916,9 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit
 
         /* We don't allow recursively invoking sd_bus_process(). */
         assert_return(!bus->current_message, -EBUSY);
-        assert(!bus->current_slot);
+        assert(!bus->current_slot); /* This should be NULL whenever bus->current_message is */
+
+        BUS_DONT_DESTROY(bus);
 
         switch (bus->state) {
 
@@ -3208,10 +3187,8 @@ static int add_match_callback(
                 r = 1;
         }
 
-        if (failed && match_slot->floating) {
-                bus_slot_disconnect(match_slot);
-                sd_bus_slot_unref(match_slot);
-        }
+        if (failed && match_slot->floating)
+                bus_slot_disconnect(match_slot, true);
 
         sd_bus_slot_unref(match_slot);
 
@@ -3266,13 +3243,21 @@ static int bus_add_match_full(
                                 goto finish;
                         }
 
-                        if (asynchronous)
+                        if (asynchronous) {
                                 r = bus_add_match_internal_async(bus,
                                                                  &s->match_callback.install_slot,
                                                                  s->match_callback.match_string,
                                                                  add_match_callback,
                                                                  s);
-                        else
+
+                                if (r < 0)
+                                        return r;
+
+                                /* Make the slot of the match call floating now. We need the reference, but we don't
+                                 * want that this match pins the bus object, hence we first create it non-floating, but
+                                 * then make it floating. */
+                                r = sd_bus_slot_set_floating(s->match_callback.install_slot, true);
+                        } else
                                 r = bus_add_match_internal(bus, s->match_callback.match_string);
                         if (r < 0)
                                 goto finish;
@@ -3669,7 +3654,6 @@ _public_ int sd_bus_default_system(sd_bus **ret) {
         return bus_default(sd_bus_open_system, &default_system_bus, ret);
 }
 
-
 _public_ int sd_bus_default_user(sd_bus **ret) {
         return bus_default(sd_bus_open_user, &default_user_bus, ret);
 }
@@ -4110,3 +4094,36 @@ _public_ int sd_bus_get_n_queued_write(sd_bus *bus, uint64_t *ret) {
         *ret = bus->wqueue_size;
         return 0;
 }
+
+_public_ int sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec) {
+        assert_return(bus, -EINVAL);
+        assert_return(bus = bus_resolve(bus), -ENOPKG);
+
+        bus->method_call_timeout = usec;
+        return 0;
+}
+
+_public_ int sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret) {
+        const char *e;
+        usec_t usec;
+
+        assert_return(bus, -EINVAL);
+        assert_return(bus = bus_resolve(bus), -ENOPKG);
+        assert_return(ret, -EINVAL);
+
+        if (bus->method_call_timeout != 0) {
+                *ret = bus->method_call_timeout;
+                return 0;
+        }
+
+        e = secure_getenv("SYSTEMD_BUS_TIMEOUT");
+        if (e && parse_sec(e, &usec) >= 0 && usec != 0) {
+                /* Save the parsed value to avoid multiple parsing. To change the timeout value,
+                 * use sd_bus_set_method_call_timeout() instead of setenv(). */
+                *ret = bus->method_call_timeout = usec;
+                return 0;
+        }
+
+        *ret = bus->method_call_timeout = BUS_DEFAULT_TIMEOUT;
+        return 0;
+}