chiark / gitweb /
bus: when the first char a server receives isn't the NUL byte immediately fail
[elogind.git] / src / libsystemd-bus / sd-bus.c
index 6a76db960dc42007c1a998c8daf52ae7bc670a38..4f004add2e72b953be5d47e9eed5c4e5292629d0 100644 (file)
 #include "util.h"
 #include "macro.h"
 #include "strv.h"
+#include "set.h"
 
 #include "sd-bus.h"
 #include "bus-internal.h"
 #include "bus-message.h"
 #include "bus-type.h"
 #include "bus-socket.h"
+#include "bus-control.h"
 
 static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec);
 
@@ -46,12 +48,11 @@ static void bus_free(sd_bus *b) {
 
         assert(b);
 
-        if (b->fd >= 0)
-                close_nointr_nofail(b->fd);
+        sd_bus_close(b);
 
         free(b->rbuffer);
         free(b->unique_name);
-        free(b->auth_uid);
+        free(b->auth_buffer);
         free(b->address);
 
         free(b->exec_path);
@@ -83,6 +84,8 @@ static void bus_free(sd_bus *b) {
 
         hashmap_free(b->object_callbacks);
 
+        bus_match_free(&b->match_callbacks);
+
         free(b);
 }
 
@@ -97,7 +100,7 @@ int sd_bus_new(sd_bus **ret) {
                 return -ENOMEM;
 
         r->n_ref = 1;
-        r->fd = -1;
+        r->input_fd = r->output_fd = -1;
         r->message_version = 1;
         r->negotiate_fds = true;
 
@@ -133,15 +136,18 @@ int sd_bus_set_address(sd_bus *bus, const char *address) {
         return 0;
 }
 
-int sd_bus_set_fd(sd_bus *bus, int fd) {
+int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) {
         if (!bus)
                 return -EINVAL;
         if (bus->state != BUS_UNSET)
                 return -EPERM;
-        if (fd < 0)
+        if (input_fd < 0)
+                return -EINVAL;
+        if (output_fd < 0)
                 return -EINVAL;
 
-        bus->fd = fd;
+        bus->input_fd = input_fd;
+        bus->output_fd = output_fd;
         return 0;
 }
 
@@ -196,6 +202,29 @@ int sd_bus_set_negotiate_fds(sd_bus *bus, int b) {
         return 0;
 }
 
+int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) {
+        if (!bus)
+                return -EINVAL;
+        if (!b && !sd_id128_equal(server_id, SD_ID128_NULL))
+                return -EINVAL;
+        if (bus->state != BUS_UNSET)
+                return -EPERM;
+
+        bus->is_server = !!b;
+        bus->server_id = server_id;
+        return 0;
+}
+
+int sd_bus_set_anonymous(sd_bus *bus, int b) {
+        if (!bus)
+                return -EINVAL;
+        if (bus->state != BUS_UNSET)
+                return -EPERM;
+
+        bus->anonymous_auth = !!b;
+        return 0;
+}
+
 static int hello_callback(sd_bus *bus, int error, sd_bus_message *reply, void *userdata) {
         const char *s;
         int r;
@@ -243,11 +272,7 @@ static int bus_send_hello(sd_bus *bus) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_send_with_reply(bus, m, hello_callback, NULL, 0, NULL);
-        if (r < 0)
-                return r;
-
-        return r;
+        return sd_bus_send_with_reply(bus, m, hello_callback, NULL, 0, &bus->hello_serial);
 }
 
 int bus_start_running(sd_bus *bus) {
@@ -506,7 +531,7 @@ static int parse_exec_address(sd_bus *b, const char **p, char **guid) {
 
                         errno = 0;
                         ul = strtoul(*p + 4, (char**) p, 10);
-                        if (errno != 0 || **p != '=' || ul > 256) {
+                        if (errno > 0 || **p != '=' || ul > 256) {
                                 r = -EINVAL;
                                 goto fail;
                         }
@@ -538,8 +563,10 @@ static int parse_exec_address(sd_bus *b, const char **p, char **guid) {
                 skip_address_key(p);
         }
 
-        if (!path)
+        if (!path) {
+                r = -EINVAL;
                 goto fail;
+        }
 
         /* Make sure there are no holes in the array, with the
          * exception of argv[0] */
@@ -579,7 +606,7 @@ static void bus_reset_parsed_address(sd_bus *b) {
         free(b->exec_path);
         b->exec_path = NULL;
         b->exec_argv = NULL;
-        b->peer = SD_ID128_NULL;
+        b->server_id = SD_ID128_NULL;
 }
 
 static int bus_parse_next_address(sd_bus *b) {
@@ -639,7 +666,7 @@ static int bus_parse_next_address(sd_bus *b) {
         }
 
         if (guid) {
-                r = sd_id128_from_string(guid, &b->peer);
+                r = sd_id128_from_string(guid, &b->server_id);
                 if (r < 0)
                         return r;
         }
@@ -654,10 +681,7 @@ static int bus_start_address(sd_bus *b) {
         assert(b);
 
         for (;;) {
-                if (b->fd >= 0) {
-                        close_nointr_nofail(b->fd);
-                        b->fd = -1;
-                }
+                sd_bus_close(b);
 
                 if (b->sockaddr.sa.sa_family != AF_UNSPEC) {
 
@@ -695,15 +719,27 @@ static int bus_start_fd(sd_bus *b) {
         int r;
 
         assert(b);
+        assert(b->input_fd >= 0);
+        assert(b->output_fd >= 0);
 
-        r = fd_nonblock(b->fd, true);
+        r = fd_nonblock(b->input_fd, true);
         if (r < 0)
                 return r;
 
-        r = fd_cloexec(b->fd, true);
+        r = fd_cloexec(b->input_fd, true);
         if (r < 0)
                 return r;
 
+        if (b->input_fd != b->output_fd) {
+                r = fd_nonblock(b->output_fd, true);
+                if (r < 0)
+                        return r;
+
+                r = fd_cloexec(b->output_fd, true);
+                if (r < 0)
+                        return r;
+        }
+
         return bus_socket_take_fd(b);
 }
 
@@ -717,9 +753,12 @@ int sd_bus_start(sd_bus *bus) {
 
         bus->state = BUS_OPENING;
 
-        if (bus->fd >= 0)
+        if (bus->is_server && bus->bus_client)
+                return -EINVAL;
+
+        if (bus->input_fd >= 0)
                 r = bus_start_fd(bus);
-        else if (bus->address)
+        else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path)
                 r = bus_start_address(bus);
         else
                 return -EINVAL;
@@ -820,11 +859,13 @@ fail:
 void sd_bus_close(sd_bus *bus) {
         if (!bus)
                 return;
-        if (bus->fd < 0)
-                return;
 
-        close_nointr_nofail(bus->fd);
-        bus->fd = -1;
+        if (bus->input_fd >= 0)
+                close_nointr_nofail(bus->input_fd);
+        if (bus->output_fd >= 0 && bus->output_fd != bus->input_fd)
+                close_nointr_nofail(bus->output_fd);
+
+        bus->input_fd = bus->output_fd = -1;
 }
 
 sd_bus *sd_bus_ref(sd_bus *bus) {
@@ -854,7 +895,7 @@ int sd_bus_is_open(sd_bus *bus) {
         if (!bus)
                 return -EINVAL;
 
-        return bus->state != BUS_UNSET && bus->fd >= 0;
+        return bus->state != BUS_UNSET && bus->input_fd >= 0;
 }
 
 int sd_bus_can_send(sd_bus *bus, char type) {
@@ -862,7 +903,7 @@ int sd_bus_can_send(sd_bus *bus, char type) {
 
         if (!bus)
                 return -EINVAL;
-        if (bus->fd < 0)
+        if (bus->output_fd < 0)
                 return -ENOTCONN;
 
         if (type == SD_BUS_TYPE_UNIX_FD) {
@@ -879,19 +920,19 @@ int sd_bus_can_send(sd_bus *bus, char type) {
         return bus_type_is_valid(type);
 }
 
-int sd_bus_get_peer(sd_bus *bus, sd_id128_t *peer) {
+int sd_bus_get_server_id(sd_bus *bus, sd_id128_t *server_id) {
         int r;
 
         if (!bus)
                 return -EINVAL;
-        if (!peer)
+        if (!server_id)
                 return -EINVAL;
 
         r = bus_ensure_running(bus);
         if (r < 0)
                 return r;
 
-        *peer = bus->peer;
+        *server_id = bus->server_id;
         return 0;
 }
 
@@ -913,7 +954,7 @@ static int dispatch_wqueue(sd_bus *bus) {
         assert(bus);
         assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
 
-        if (bus->fd < 0)
+        if (bus->output_fd < 0)
                 return -ENOTCONN;
 
         while (bus->wqueue_size > 0) {
@@ -956,7 +997,7 @@ static int dispatch_rqueue(sd_bus *bus, sd_bus_message **m) {
         assert(m);
         assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
 
-        if (bus->fd < 0)
+        if (bus->input_fd < 0)
                 return -ENOTCONN;
 
         if (bus->rqueue_size > 0) {
@@ -992,7 +1033,7 @@ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) {
                 return -EINVAL;
         if (bus->state == BUS_UNSET)
                 return -ENOTCONN;
-        if (bus->fd < 0)
+        if (bus->output_fd < 0)
                 return -ENOTCONN;
         if (!m)
                 return -EINVAL;
@@ -1089,7 +1130,7 @@ static int timeout_compare(const void *a, const void *b) {
 int sd_bus_send_with_reply(
                 sd_bus *bus,
                 sd_bus_message *m,
-                sd_message_handler_t callback,
+                sd_bus_message_handler_t callback,
                 void *userdata,
                 uint64_t usec,
                 uint64_t *serial) {
@@ -1101,7 +1142,7 @@ int sd_bus_send_with_reply(
                 return -EINVAL;
         if (bus->state == BUS_UNSET)
                 return -ENOTCONN;
-        if (bus->fd < 0)
+        if (bus->output_fd < 0)
                 return -ENOTCONN;
         if (!m)
                 return -EINVAL;
@@ -1183,7 +1224,7 @@ int bus_ensure_running(sd_bus *bus) {
 
         assert(bus);
 
-        if (bus->fd < 0)
+        if (bus->input_fd < 0)
                 return -ENOTCONN;
         if (bus->state == BUS_UNSET)
                 return -ENOTCONN;
@@ -1220,7 +1261,7 @@ int sd_bus_send_with_reply_and_block(
 
         if (!bus)
                 return -EINVAL;
-        if (bus->fd < 0)
+        if (bus->output_fd < 0)
                 return -ENOTCONN;
         if (bus->state == BUS_UNSET)
                 return -ENOTCONN;
@@ -1330,11 +1371,12 @@ int sd_bus_send_with_reply_and_block(
 int sd_bus_get_fd(sd_bus *bus) {
         if (!bus)
                 return -EINVAL;
-
-        if (bus->fd < 0)
+        if (bus->input_fd < 0)
                 return -ENOTCONN;
+        if (bus->input_fd != bus->output_fd)
+                return -EPERM;
 
-        return bus->fd;
+        return bus->input_fd;
 }
 
 int sd_bus_get_events(sd_bus *bus) {
@@ -1344,14 +1386,14 @@ int sd_bus_get_events(sd_bus *bus) {
                 return -EINVAL;
         if (bus->state == BUS_UNSET)
                 return -ENOTCONN;
-        if (bus->fd < 0)
+        if (bus->input_fd < 0)
                 return -ENOTCONN;
 
         if (bus->state == BUS_OPENING)
                 flags |= POLLOUT;
         else if (bus->state == BUS_AUTHENTICATING) {
 
-                if (bus->auth_index < ELEMENTSOF(bus->auth_iovec))
+                if (bus_socket_auth_needs_write(bus))
                         flags |= POLLOUT;
 
                 flags |= POLLIN;
@@ -1375,7 +1417,7 @@ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) {
                 return -EINVAL;
         if (bus->state == BUS_UNSET)
                 return -ENOTCONN;
-        if (bus->fd < 0)
+        if (bus->input_fd < 0)
                 return -ENOTCONN;
 
         if (bus->state == BUS_AUTHENTICATING) {
@@ -1418,6 +1460,28 @@ static int process_timeout(sd_bus *bus) {
         return r < 0 ? r : 1;
 }
 
+static int process_hello(sd_bus *bus, sd_bus_message *m) {
+        assert(bus);
+        assert(m);
+
+        if (bus->state != BUS_HELLO)
+                return 0;
+
+        /* Let's make sure the first message on the bus is the HELLO
+         * reply. But note that we don't actually parse the message
+         * here (we leave that to the usual handling), we just verify
+         * we don't let any earlier msg through. */
+
+        if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_RETURN &&
+            m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_ERROR)
+                return -EIO;
+
+        if (m->reply_serial != bus->hello_serial)
+                return -EIO;
+
+        return 0;
+}
+
 static int process_reply(sd_bus *bus, sd_bus_message *m) {
         struct reply_callback *c;
         int r;
@@ -1446,6 +1510,9 @@ static int process_filter(sd_bus *bus, sd_bus_message *m) {
         struct filter_callback *l;
         int r;
 
+        assert(bus);
+        assert(m);
+
         LIST_FOREACH(callbacks, l, bus->filter_callbacks) {
                 r = l->callback(bus, 0, m, l->userdata);
                 if (r != 0)
@@ -1455,6 +1522,13 @@ static int process_filter(sd_bus *bus, sd_bus_message *m) {
         return 0;
 }
 
+static int process_match(sd_bus *bus, sd_bus_message *m) {
+        assert(bus);
+        assert(m);
+
+        return bus_match_run(bus, &bus->match_callbacks, 0, m);
+}
+
 static int process_builtin(sd_bus *bus, sd_bus_message *m) {
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         int r;
@@ -1553,7 +1627,10 @@ static int process_object(sd_bus *bus, sd_bus_message *m) {
                 }
         }
 
-        if (!found)
+        /* We found some handlers but none wanted to take this, then
+         * return this -- with one exception, we can handle
+         * introspection minimally ourselves */
+        if (!found || sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect"))
                 return 0;
 
         sd_bus_error_set(&error,
@@ -1571,12 +1648,108 @@ static int process_object(sd_bus *bus, sd_bus_message *m) {
         return 1;
 }
 
+static int process_introspect(sd_bus *bus, sd_bus_message *m) {
+        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+        _cleanup_free_ char *introspection = NULL;
+        _cleanup_set_free_free_ Set *s = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        struct object_callback *c;
+        Iterator i;
+        size_t size = 0;
+        char *node;
+        int r;
+
+        assert(bus);
+        assert(m);
+
+        if (!sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect"))
+                return 0;
+
+        if (!m->path)
+                return 0;
+
+        s = set_new(string_hash_func, string_compare_func);
+        if (!s)
+                return -ENOMEM;
+
+        HASHMAP_FOREACH(c, bus->object_callbacks, i) {
+                const char *e;
+                char *a, *p;
+
+                if (streq(c->path, "/"))
+                        continue;
+
+                if (streq(m->path, "/"))
+                        e = c->path;
+                else {
+                        e = startswith(c->path, m->path);
+                        if (!e || *e != '/')
+                                continue;
+                }
+
+                a = strdup(e+1);
+                if (!a)
+                        return -ENOMEM;
+
+                p = strchr(a, '/');
+                if (p)
+                        *p = 0;
+
+                r = set_put(s, a);
+                if (r < 0) {
+                        free(a);
+
+                        if (r != -EEXIST)
+                                return r;
+                }
+        }
+
+        f = open_memstream(&introspection, &size);
+        if (!f)
+                return -ENOMEM;
+
+        fputs(SD_BUS_INTROSPECT_DOCTYPE, f);
+        fputs("<node>\n", f);
+        fputs(SD_BUS_INTROSPECT_INTERFACE_PEER, f);
+        fputs(SD_BUS_INTROSPECT_INTERFACE_INTROSPECTABLE, f);
+
+        while ((node = set_steal_first(s))) {
+                fprintf(f, " <node name=\"%s\"/>\n", node);
+                free(node);
+        }
+
+        fputs("</node>\n", f);
+
+        fflush(f);
+
+        if (ferror(f))
+                return -ENOMEM;
+
+        r = sd_bus_message_new_method_return(bus, m, &reply);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(reply, "s", introspection);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_send(bus, reply, NULL);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
 static int process_message(sd_bus *bus, sd_bus_message *m) {
         int r;
 
         assert(bus);
         assert(m);
 
+        r = process_hello(bus, m);
+        if (r != 0)
+                return r;
+
         r = process_reply(bus, m);
         if (r != 0)
                 return r;
@@ -1585,11 +1758,19 @@ static int process_message(sd_bus *bus, sd_bus_message *m) {
         if (r != 0)
                 return r;
 
+        r = process_match(bus, m);
+        if (r != 0)
+                return r;
+
         r = process_builtin(bus, m);
         if (r != 0)
                 return r;
 
-        return process_object(bus, m);
+        r = process_object(bus, m);
+        if (r != 0)
+                return r;
+
+        return process_introspect(bus, m);
 }
 
 static int process_running(sd_bus *bus, sd_bus_message **ret) {
@@ -1657,7 +1838,7 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
 
         if (!bus)
                 return -EINVAL;
-        if (bus->fd < 0)
+        if (bus->input_fd < 0)
                 return -ENOTCONN;
 
         switch (bus->state) {
@@ -1692,14 +1873,14 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
 }
 
 static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
-        struct pollfd p;
-        int r, e;
+        struct pollfd p[2];
+        int r, e, n;
         struct timespec ts;
         usec_t until, m;
 
         assert(bus);
 
-        if (bus->fd < 0)
+        if (bus->input_fd < 0)
                 return -ENOTCONN;
 
         e = sd_bus_get_events(bus);
@@ -1715,19 +1896,28 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
         if (r == 0)
                 m = (uint64_t) -1;
         else {
-                usec_t n;
-                n = now(CLOCK_MONOTONIC);
-                m = until > n ? until - n : 0;
+                usec_t nw;
+                nw = now(CLOCK_MONOTONIC);
+                m = until > nw ? until - nw : 0;
         }
 
         if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
                 m = timeout_usec;
 
         zero(p);
-        p.fd = bus->fd;
-        p.events = e;
+        p[0].fd = bus->input_fd;
 
-        r = ppoll(&p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
+        if (bus->output_fd == bus->input_fd) {
+                p[0].events = e;
+                n = 1;
+        } else {
+                p[0].events = e & POLLIN;
+                p[1].fd = bus->output_fd;
+                p[1].events = e & POLLOUT;
+                n = 2;
+        }
+
+        r = ppoll(p, n, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
         if (r < 0)
                 return -errno;
 
@@ -1740,7 +1930,7 @@ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) {
                 return -EINVAL;
         if (bus->state == BUS_UNSET)
                 return -ENOTCONN;
-        if (bus->fd < 0)
+        if (bus->input_fd < 0)
                 return -ENOTCONN;
         if (bus->rqueue_size > 0)
                 return 0;
@@ -1755,7 +1945,7 @@ int sd_bus_flush(sd_bus *bus) {
                 return -EINVAL;
         if (bus->state == BUS_UNSET)
                 return -ENOTCONN;
-        if (bus->fd < 0)
+        if (bus->output_fd < 0)
                 return -ENOTCONN;
 
         r = bus_ensure_running(bus);
@@ -1779,7 +1969,7 @@ int sd_bus_flush(sd_bus *bus) {
         }
 }
 
-int sd_bus_add_filter(sd_bus *bus, sd_message_handler_t callback, void *userdata) {
+int sd_bus_add_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata) {
         struct filter_callback *f;
 
         if (!bus)
@@ -1797,7 +1987,7 @@ int sd_bus_add_filter(sd_bus *bus, sd_message_handler_t callback, void *userdata
         return 0;
 }
 
-int sd_bus_remove_filter(sd_bus *bus, sd_message_handler_t callback, void *userdata) {
+int sd_bus_remove_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata) {
         struct filter_callback *f;
 
         if (!bus)
@@ -1820,7 +2010,7 @@ static int bus_add_object(
                 sd_bus *bus,
                 bool fallback,
                 const char *path,
-                sd_message_handler_t callback,
+                sd_bus_message_handler_t callback,
                 void *userdata) {
 
         struct object_callback *c;
@@ -1842,7 +2032,7 @@ static int bus_add_object(
                 return -ENOMEM;
 
         c->path = strdup(path);
-        if (!path) {
+        if (!c->path) {
                 free(c);
                 return -ENOMEM;
         }
@@ -1865,7 +2055,7 @@ static int bus_remove_object(
                 sd_bus *bus,
                 bool fallback,
                 const char *path,
-                sd_message_handler_t callback,
+                sd_bus_message_handler_t callback,
                 void *userdata) {
 
         struct object_callback *c;
@@ -1892,18 +2082,63 @@ static int bus_remove_object(
         return 1;
 }
 
-int sd_bus_add_object(sd_bus *bus, const char *path, sd_message_handler_t callback, void *userdata) {
+int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
         return bus_add_object(bus, false, path, callback, userdata);
 }
 
-int sd_bus_remove_object(sd_bus *bus, const char *path, sd_message_handler_t callback, void *userdata) {
+int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
         return bus_remove_object(bus, false, path, callback, userdata);
 }
 
-int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_message_handler_t callback, void *userdata) {
+int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
         return bus_add_object(bus, true, prefix, callback, userdata);
 }
 
-int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_message_handler_t callback, void *userdata) {
+int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
         return bus_remove_object(bus, true, prefix, callback, userdata);
 }
+
+int sd_bus_add_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata) {
+        int r = 0;
+
+        if (!bus)
+                return -EINVAL;
+        if (!match)
+                return -EINVAL;
+
+        if (bus->bus_client) {
+                r = bus_add_match_internal(bus, match);
+                if (r < 0)
+                        return r;
+        }
+
+        if (callback) {
+                r = bus_match_add(&bus->match_callbacks, match, callback, userdata, NULL);
+                if (r < 0) {
+
+                        if (bus->bus_client)
+                                bus_remove_match_internal(bus, match);
+                }
+        }
+
+        return r;
+}
+
+int sd_bus_remove_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata) {
+        int r = 0, q = 0;
+
+        if (!bus)
+                return -EINVAL;
+        if (!match)
+                return -EINVAL;
+
+        if (bus->bus_client)
+                r = bus_remove_match_internal(bus, match);
+
+        if (callback)
+                q = bus_match_remove(&bus->match_callbacks, match, callback, userdata);
+
+        if (r < 0)
+                return r;
+        return q;
+}