chiark / gitweb /
bus: return 1 on all calls that send messages
[elogind.git] / src / libsystemd-bus / sd-bus.c
index 5e66a31162611ac2d7ae988840aa55950a458320..5d1fcd90a0b87d6e4c93689fc86cb7a13e9afe7b 100644 (file)
@@ -42,6 +42,9 @@
 #include "bus-socket.h"
 #include "bus-kernel.h"
 #include "bus-control.h"
+#include "bus-introspect.h"
+#include "bus-signature.h"
+#include "bus-objects.h"
 
 static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec);
 
@@ -57,9 +60,46 @@ static void bus_close_fds(sd_bus *b) {
         b->input_fd = b->output_fd = -1;
 }
 
+static void bus_node_destroy(sd_bus *b, struct node *n) {
+        struct node_callback *c;
+        struct node_vtable *v;
+        struct node_enumerator *e;
+
+        assert(b);
+
+        if (!n)
+                return;
+
+        while (n->child)
+                bus_node_destroy(b, n->child);
+
+        while ((c = n->callbacks)) {
+                LIST_REMOVE(callbacks, n->callbacks, c);
+                free(c);
+        }
+
+        while ((v = n->vtables)) {
+                LIST_REMOVE(vtables, n->vtables, v);
+                free(v->interface);
+                free(v);
+        }
+
+        while ((e = n->enumerators)) {
+                LIST_REMOVE(enumerators, n->enumerators, e);
+                free(e);
+        }
+
+        if (n->parent)
+                LIST_REMOVE(siblings, n->parent->child, n);
+
+        assert_se(hashmap_remove(b->nodes, n->path) == n);
+        free(n->path);
+        free(n);
+}
+
 static void bus_free(sd_bus *b) {
         struct filter_callback *f;
-        struct object_callback *c;
+        struct node *n;
         unsigned i;
 
         assert(b);
@@ -93,18 +133,20 @@ static void bus_free(sd_bus *b) {
         prioq_free(b->reply_callbacks_prioq);
 
         while ((f = b->filter_callbacks)) {
-                LIST_REMOVE(struct filter_callback, callbacks, b->filter_callbacks, f);
+                LIST_REMOVE(callbacks, b->filter_callbacks, f);
                 free(f);
         }
 
-        while ((c = hashmap_steal_first(b->object_callbacks))) {
-                free(c->path);
-                free(c);
-        }
-
-        hashmap_free(b->object_callbacks);
         bus_match_free(&b->match_callbacks);
 
+        hashmap_free_free(b->vtable_methods);
+        hashmap_free_free(b->vtable_properties);
+
+        while ((n = hashmap_first(b->nodes)))
+                bus_node_destroy(b, n);
+
+        hashmap_free(b->nodes);
+
         bus_kernel_flush_memfd(b);
 
         assert_se(pthread_mutex_destroy(&b->memfd_cache_mutex) == 0);
@@ -115,8 +157,7 @@ static void bus_free(sd_bus *b) {
 int sd_bus_new(sd_bus **ret) {
         sd_bus *r;
 
-        if (!ret)
-                return -EINVAL;
+        assert_return(ret, -EINVAL);
 
         r = new0(sd_bus, 1);
         if (!r)
@@ -125,7 +166,7 @@ int sd_bus_new(sd_bus **ret) {
         r->n_ref = REFCNT_INIT;
         r->input_fd = r->output_fd = -1;
         r->message_version = 1;
-        r->negotiate_fds = true;
+        r->hello_flags |= KDBUS_HELLO_ACCEPT_FD;
         r->original_pid = getpid();
 
         assert_se(pthread_mutex_init(&r->memfd_cache_mutex, NULL) == 0);
@@ -145,14 +186,10 @@ int sd_bus_new(sd_bus **ret) {
 int sd_bus_set_address(sd_bus *bus, const char *address) {
         char *a;
 
-        if (!bus)
-                return -EINVAL;
-        if (bus->state != BUS_UNSET)
-                return -EPERM;
-        if (!address)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state == BUS_UNSET, -EPERM);
+        assert_return(address, -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         a = strdup(address);
         if (!a)
@@ -165,16 +202,11 @@ int sd_bus_set_address(sd_bus *bus, const char *address) {
 }
 
 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 (input_fd < 0)
-                return -EINVAL;
-        if (output_fd < 0)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state == BUS_UNSET, -EPERM);
+        assert_return(input_fd >= 0, -EINVAL);
+        assert_return(output_fd >= 0, -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         bus->input_fd = input_fd;
         bus->output_fd = output_fd;
@@ -184,16 +216,11 @@ int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) {
 int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) {
         char *p, **a;
 
-        if (!bus)
-                return -EINVAL;
-        if (bus->state != BUS_UNSET)
-                return -EPERM;
-        if (!path)
-                return -EINVAL;
-        if (strv_isempty(argv))
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state == BUS_UNSET, -EPERM);
+        assert_return(path, -EINVAL);
+        assert_return(!strv_isempty(argv), -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         p = strdup(path);
         if (!p)
@@ -215,38 +242,91 @@ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) {
 }
 
 int sd_bus_set_bus_client(sd_bus *bus, int b) {
-        if (!bus)
-                return -EINVAL;
-        if (bus->state != BUS_UNSET)
-                return -EPERM;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state == BUS_UNSET, -EPERM);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         bus->bus_client = !!b;
         return 0;
 }
 
-int sd_bus_set_negotiate_fds(sd_bus *bus, int b) {
-        if (!bus)
-                return -EINVAL;
-        if (bus->state != BUS_UNSET)
-                return -EPERM;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+int sd_bus_negotiate_fds(sd_bus *bus, int b) {
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state == BUS_UNSET, -EPERM);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
+
+        SET_FLAG(bus->hello_flags, KDBUS_HELLO_ACCEPT_FD, b);
+        return 0;
+}
+
+int sd_bus_negotiate_attach_comm(sd_bus *bus, int b) {
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state == BUS_UNSET, -EPERM);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
-        bus->negotiate_fds = !!b;
+        SET_FLAG(bus->hello_flags, KDBUS_HELLO_ATTACH_COMM, b);
+        return 0;
+}
+
+int sd_bus_negotiate_attach_exe(sd_bus *bus, int b) {
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state == BUS_UNSET, -EPERM);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
+
+        SET_FLAG(bus->hello_flags, KDBUS_HELLO_ATTACH_EXE, b);
+        return 0;
+}
+
+int sd_bus_negotiate_attach_cmdline(sd_bus *bus, int b) {
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state == BUS_UNSET, -EPERM);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
+
+        SET_FLAG(bus->hello_flags, KDBUS_HELLO_ATTACH_CMDLINE, b);
+        return 0;
+}
+
+int sd_bus_negotiate_attach_cgroup(sd_bus *bus, int b) {
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state == BUS_UNSET, -EPERM);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
+
+        SET_FLAG(bus->hello_flags, KDBUS_HELLO_ATTACH_CGROUP, b);
+        return 0;
+}
+
+int sd_bus_negotiate_attach_caps(sd_bus *bus, int b) {
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state == BUS_UNSET, -EPERM);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
+
+        SET_FLAG(bus->hello_flags, KDBUS_HELLO_ATTACH_CAPS, b);
+        return 0;
+}
+
+int sd_bus_negotiate_attach_selinux_context(sd_bus *bus, int b) {
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state == BUS_UNSET, -EPERM);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
+
+        SET_FLAG(bus->hello_flags, KDBUS_HELLO_ATTACH_SECLABEL, b);
+        return 0;
+}
+
+int sd_bus_negotiate_attach_audit(sd_bus *bus, int b) {
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state == BUS_UNSET, -EPERM);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
+
+        SET_FLAG(bus->hello_flags, KDBUS_HELLO_ATTACH_AUDIT, 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;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        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);
 
         bus->is_server = !!b;
         bus->server_id = server_id;
@@ -254,12 +334,9 @@ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) {
 }
 
 int sd_bus_set_anonymous(sd_bus *bus, int b) {
-        if (!bus)
-                return -EINVAL;
-        if (bus->state != BUS_UNSET)
-                return -EPERM;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state == BUS_UNSET, -EPERM);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         bus->anonymous_auth = !!b;
         return 0;
@@ -844,12 +921,9 @@ static int bus_start_fd(sd_bus *b) {
 int sd_bus_start(sd_bus *bus) {
         int r;
 
-        if (!bus)
-                return -EINVAL;
-        if (bus->state != BUS_UNSET)
-                return -EPERM;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state == BUS_UNSET, -EPERM);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         bus->state = BUS_OPENING;
 
@@ -874,8 +948,7 @@ int sd_bus_open_system(sd_bus **ret) {
         sd_bus *b;
         int r;
 
-        if (!ret)
-                return -EINVAL;
+        assert_return(ret, -EINVAL);
 
         r = sd_bus_new(&b);
         if (r < 0)
@@ -912,8 +985,7 @@ int sd_bus_open_user(sd_bus **ret) {
         size_t l;
         int r;
 
-        if (!ret)
-                return -EINVAL;
+        assert_return(ret, -EINVAL);
 
         r = sd_bus_new(&b);
         if (r < 0)
@@ -996,10 +1068,9 @@ sd_bus *sd_bus_unref(sd_bus *bus) {
 }
 
 int sd_bus_is_open(sd_bus *bus) {
-        if (!bus)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+
+        assert_return(bus, -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         return BUS_IS_OPEN(bus->state);
 }
@@ -1007,15 +1078,12 @@ int sd_bus_is_open(sd_bus *bus) {
 int sd_bus_can_send(sd_bus *bus, char type) {
         int r;
 
-        if (!bus)
-                return -EINVAL;
-        if (bus->state == BUS_UNSET)
-                return -ENOTCONN;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(bus->state != BUS_UNSET, -ENOTCONN);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         if (type == SD_BUS_TYPE_UNIX_FD) {
-                if (!bus->negotiate_fds)
+                if (!(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD))
                         return 0;
 
                 r = bus_ensure_running(bus);
@@ -1031,12 +1099,9 @@ int sd_bus_can_send(sd_bus *bus, char type) {
 int sd_bus_get_server_id(sd_bus *bus, sd_id128_t *server_id) {
         int r;
 
-        if (!bus)
-                return -EINVAL;
-        if (!server_id)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(server_id, -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         r = bus_ensure_running(bus);
         if (r < 0)
@@ -1131,24 +1196,20 @@ static int dispatch_rqueue(sd_bus *bus, sd_bus_message **m) {
                 if (r == 0)
                         return ret;
 
-                r = 1;
+                ret = 1;
         } while (!z);
 
         *m = z;
-        return 1;
+        return ret;
 }
 
 int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) {
         int r;
 
-        if (!bus)
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (!m)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+        assert_return(m, -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         if (m->n_fds > 0) {
                 r = sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD);
@@ -1170,7 +1231,7 @@ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) {
         /* If this is a reply and no reply was requested, then let's
          * suppress this, if we can */
         if (m->dont_send && !serial)
-                return 0;
+                return 1;
 
         if ((bus->state == BUS_RUNNING || bus->state == BUS_HELLO) && bus->wqueue_size <= 0) {
                 size_t idx = 0;
@@ -1212,7 +1273,7 @@ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) {
         if (serial)
                 *serial = BUS_MESSAGE_SERIAL(m);
 
-        return 0;
+        return 1;
 }
 
 static usec_t calc_elapse(uint64_t usec) {
@@ -1254,20 +1315,13 @@ int sd_bus_send_with_reply(
         struct reply_callback *c;
         int r;
 
-        if (!bus)
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (!m)
-                return -EINVAL;
-        if (!callback)
-                return -EINVAL;
-        if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
-                return -EINVAL;
-        if (m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+        assert_return(m, -EINVAL);
+        assert_return(m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL, -EINVAL);
+        assert_return(!(m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL);
+        assert_return(callback, -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         r = hashmap_ensure_allocated(&bus->reply_callbacks, uint64_hash_func, uint64_compare_func);
         if (r < 0)
@@ -1319,12 +1373,9 @@ int sd_bus_send_with_reply(
 int sd_bus_send_with_reply_cancel(sd_bus *bus, uint64_t serial) {
         struct reply_callback *c;
 
-        if (!bus)
-                return -EINVAL;
-        if (serial == 0)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(serial != 0, -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         c = hashmap_remove(bus->reply_callbacks, &serial);
         if (!c)
@@ -1374,20 +1425,13 @@ int sd_bus_send_with_reply_and_block(
         uint64_t serial;
         bool room = false;
 
-        if (!bus)
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (!m)
-                return -EINVAL;
-        if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
-                return -EINVAL;
-        if (m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
-                return -EINVAL;
-        if (bus_error_is_dirty(error))
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+        assert_return(m, -EINVAL);
+        assert_return(m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL, -EINVAL);
+        assert_return(!(m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL);
+        assert_return(!bus_error_is_dirty(error), -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         r = bus_ensure_running(bus);
         if (r < 0)
@@ -1438,7 +1482,7 @@ int sd_bus_send_with_reply_and_block(
                                         else
                                                 sd_bus_message_unref(incoming);
 
-                                        return 0;
+                                        return 1;
                                 }
 
                                 if (incoming->header->type == SD_BUS_MESSAGE_TYPE_METHOD_ERROR) {
@@ -1492,14 +1536,11 @@ int sd_bus_send_with_reply_and_block(
 }
 
 int sd_bus_get_fd(sd_bus *bus) {
-        if (!bus)
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (bus->input_fd != bus->output_fd)
-                return -EPERM;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+
+        assert_return(bus, -EINVAL);
+        assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+        assert_return(bus->input_fd == bus->output_fd, -EPERM);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         return bus->input_fd;
 }
@@ -1507,12 +1548,9 @@ int sd_bus_get_fd(sd_bus *bus) {
 int sd_bus_get_events(sd_bus *bus) {
         int flags = 0;
 
-        if (!bus)
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         if (bus->state == BUS_OPENING)
                 flags |= POLLOUT;
@@ -1536,14 +1574,10 @@ int sd_bus_get_events(sd_bus *bus) {
 int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) {
         struct reply_callback *c;
 
-        if (!bus)
-                return -EINVAL;
-        if (!timeout_usec)
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(timeout_usec, -EINVAL);
+        assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         if (bus->state == BUS_AUTHENTICATING) {
                 *timeout_usec = bus->auth_timeout;
@@ -1555,6 +1589,11 @@ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) {
                 return 0;
         }
 
+        if (bus->rqueue_size > 0) {
+                *timeout_usec = 0;
+                return 1;
+        }
+
         c = prioq_peek(bus->reply_callbacks_prioq);
         if (!c) {
                 *timeout_usec = (uint64_t) -1;
@@ -1734,197 +1773,12 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) {
 
                 r = sd_bus_message_append(reply, "s", sd_id128_to_string(id, sid));
         } else {
-                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-
-                sd_bus_error_set(&error,
-                                 "org.freedesktop.DBus.Error.UnknownMethod",
+                r = sd_bus_message_new_method_errorf(
+                                bus, m, &reply,
+                                "org.freedesktop.DBus.Error.UnknownMethod",
                                  "Unknown method '%s' on interface '%s'.", m->member, m->interface);
-
-                r = sd_bus_message_new_method_error(bus, m, &error, &reply);
-        }
-
-        if (r < 0)
-                return r;
-
-        r = sd_bus_send(bus, reply, NULL);
-        if (r < 0)
-                return r;
-
-        return 1;
-}
-
-static int process_object(sd_bus *bus, sd_bus_message *m) {
-        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
-        struct object_callback *c;
-        int r;
-        bool found = false;
-        size_t pl;
-
-        assert(bus);
-        assert(m);
-
-        if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
-                return 0;
-
-        if (hashmap_isempty(bus->object_callbacks))
-                return 0;
-
-        pl = strlen(m->path);
-
-        do {
-                char p[pl+1];
-
-                bus->object_callbacks_modified = false;
-
-                c = hashmap_get(bus->object_callbacks, m->path);
-                if (c && c->last_iteration != bus->iteration_counter) {
-
-                        c->last_iteration = bus->iteration_counter;
-
-                        r = sd_bus_message_rewind(m, true);
-                        if (r < 0)
-                                return r;
-
-                        r = c->callback(bus, m, c->userdata);
-                        if (r != 0)
-                                return r;
-
-                        found = true;
-                }
-
-                /* Look for fallback prefixes */
-                strcpy(p, m->path);
-                for (;;) {
-                        char *e;
-
-                        if (bus->object_callbacks_modified)
-                                break;
-
-                        e = strrchr(p, '/');
-                        if (e == p || !e)
-                                break;
-
-                        *e = 0;
-
-                        c = hashmap_get(bus->object_callbacks, p);
-                        if (c && c->last_iteration != bus->iteration_counter && c->is_fallback) {
-
-                                c->last_iteration = bus->iteration_counter;
-
-                                r = sd_bus_message_rewind(m, true);
-                                if (r < 0)
-                                        return r;
-
-                                r = c->callback(bus, m, c->userdata);
-                                if (r != 0)
-                                        return r;
-
-                                found = true;
-                        }
-                }
-
-        } while (bus->object_callbacks_modified);
-
-        /* We found some handlers but none wanted to take this, then
-         * return this -- with one exception, we can handle
-         * introspection minimally ourselves */
-        if (!found || sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect"))
-                return 0;
-
-        sd_bus_error_set(&error,
-                         "org.freedesktop.DBus.Error.UnknownMethod",
-                         "Unknown method '%s' or interface '%s'.", m->member, m->interface);
-
-        r = sd_bus_message_new_method_error(bus, m, &error, &reply);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_send(bus, reply, NULL);
-        if (r < 0)
-                return r;
-
-        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_consume(s, a);
-                if (r < 0 && 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;
 
@@ -1963,11 +1817,7 @@ static int process_message(sd_bus *bus, sd_bus_message *m) {
         if (r != 0)
                 return r;
 
-        r = process_object(bus, m);
-        if (r != 0)
-                return r;
-
-        return process_introspect(bus, m);
+        return bus_process_object(bus, m);
 }
 
 static int process_running(sd_bus *bus, sd_bus_message **ret) {
@@ -2006,16 +1856,11 @@ static int process_running(sd_bus *bus, sd_bus_message **ret) {
         }
 
         if (m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL) {
-                _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
-                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
 
-                sd_bus_error_set(&error, "org.freedesktop.DBus.Error.UnknownObject", "Unknown object '%s'.", m->path);
-
-                r = sd_bus_message_new_method_error(bus, m, &error, &reply);
-                if (r < 0)
-                        return r;
-
-                r = sd_bus_send(bus, reply, NULL);
+                r = sd_bus_reply_method_errorf(
+                                bus, m,
+                                "org.freedesktop.DBus.Error.UnknownObject",
+                                "Unknown object '%s'.", m->path);
                 if (r < 0)
                         return r;
         }
@@ -2030,6 +1875,7 @@ null_message:
 }
 
 int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
+        BUS_DONT_DESTROY(bus);
         int r;
 
         /* Returns 0 when we didn't do anything. This should cause the
@@ -2037,14 +1883,11 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
          * time. Returns > 0 when we did something, which possibly
          * means *ret is filled in with an unprocessed message. */
 
-        if (!bus)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         /* We don't allow recursively invoking sd_bus_process(). */
-        if (bus->processing)
-                return -EBUSY;
+        assert_return(!bus->processing, -EBUSY);
 
         switch (bus->state) {
 
@@ -2086,29 +1929,33 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
         struct pollfd p[2] = {};
         int r, e, n;
         struct timespec ts;
-        usec_t until, m;
+        usec_t m = (usec_t) -1;
 
         assert(bus);
-
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
+        assert_return(BUS_IS_OPEN(bus->state), -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;
-
-        r = sd_bus_get_timeout(bus, &until);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                m = (uint64_t) -1;
         else {
-                usec_t nw;
-                nw = now(CLOCK_MONOTONIC);
-                m = until > nw ? until - nw : 0;
+                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 (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
@@ -2134,12 +1981,9 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
 
 int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) {
 
-        if (!bus)
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         if (bus->rqueue_size > 0)
                 return 0;
@@ -2150,12 +1994,9 @@ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) {
 int sd_bus_flush(sd_bus *bus) {
         int r;
 
-        if (!bus)
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         r = bus_ensure_running(bus);
         if (r < 0)
@@ -2181,12 +2022,9 @@ int sd_bus_flush(sd_bus *bus) {
 int sd_bus_add_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata) {
         struct filter_callback *f;
 
-        if (!bus)
-                return -EINVAL;
-        if (!callback)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(callback, -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         f = new0(struct filter_callback, 1);
         if (!f)
@@ -2195,24 +2033,21 @@ int sd_bus_add_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *user
         f->userdata = userdata;
 
         bus->filter_callbacks_modified = true;
-        LIST_PREPEND(struct filter_callback, callbacks, bus->filter_callbacks, f);
+        LIST_PREPEND(callbacks, bus->filter_callbacks, f);
         return 0;
 }
 
 int sd_bus_remove_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata) {
         struct filter_callback *f;
 
-        if (!bus)
-                return -EINVAL;
-        if (!callback)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(callback, -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         LIST_FOREACH(callbacks, f, bus->filter_callbacks) {
                 if (f->callback == callback && f->userdata == userdata) {
                         bus->filter_callbacks_modified = true;
-                        LIST_REMOVE(struct filter_callback, callbacks, bus->filter_callbacks, f);
+                        LIST_REMOVE(callbacks, bus->filter_callbacks, f);
                         free(f);
                         return 1;
                 }
@@ -2221,290 +2056,63 @@ int sd_bus_remove_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *u
         return 0;
 }
 
-static int bus_add_object(
-                sd_bus *bus,
-                bool fallback,
-                const char *path,
-                sd_bus_message_handler_t callback,
-                void *userdata) {
-
-        struct object_callback *c;
-        int r;
-
-        if (!bus)
-                return -EINVAL;
-        if (!path)
-                return -EINVAL;
-        if (!callback)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
-
-        r = hashmap_ensure_allocated(&bus->object_callbacks, string_hash_func, string_compare_func);
-        if (r < 0)
-                return r;
-
-        c = new0(struct object_callback, 1);
-        if (!c)
-                return -ENOMEM;
-
-        c->path = strdup(path);
-        if (!c->path) {
-                free(c);
-                return -ENOMEM;
-        }
-
-        c->callback = callback;
-        c->userdata = userdata;
-        c->is_fallback = fallback;
-
-        bus->object_callbacks_modified = true;
-        r = hashmap_put(bus->object_callbacks, c->path, c);
-        if (r < 0) {
-                free(c->path);
-                free(c);
-                return r;
-        }
-
-        return 0;
-}
-
-static int bus_remove_object(
-                sd_bus *bus,
-                bool fallback,
-                const char *path,
-                sd_bus_message_handler_t callback,
-                void *userdata) {
-
-        struct object_callback *c;
-
-        if (!bus)
-                return -EINVAL;
-        if (!path)
-                return -EINVAL;
-        if (!callback)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
-
-        c = hashmap_get(bus->object_callbacks, path);
-        if (!c)
-                return 0;
-
-        if (c->callback != callback || c->userdata != userdata || c->is_fallback != fallback)
-                return 0;
-
-        bus->object_callbacks_modified = true;
-        assert_se(c == hashmap_remove(bus->object_callbacks, c->path));
-
-        free(c->path);
-        free(c);
-
-        return 1;
-}
-
-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_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_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_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) {
+        struct bus_match_component *components = NULL;
+        unsigned n_components = 0;
+        uint64_t cookie = 0;
         int r = 0;
 
-        if (!bus)
-                return -EINVAL;
-        if (!match)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        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;
 
         if (bus->bus_client) {
-                r = bus_add_match_internal(bus, match);
+                cookie = ++bus->match_cookie;
+
+                r = bus_add_match_internal(bus, match, components, n_components, cookie);
                 if (r < 0)
-                        return r;
+                        goto finish;
         }
 
-        if (callback) {
-                bus->match_callbacks_modified = true;
-                r = bus_match_add(&bus->match_callbacks, match, callback, userdata, NULL);
-                if (r < 0) {
-
-                        if (bus->bus_client)
-                                bus_remove_match_internal(bus, match);
-                }
+        bus->match_callbacks_modified = true;
+        r = bus_match_add(&bus->match_callbacks, components, n_components, callback, userdata, cookie, NULL);
+        if (r < 0) {
+                if (bus->bus_client)
+                        bus_remove_match_internal(bus, match, cookie);
         }
 
+finish:
+        bus_match_parse_free(components, n_components);
         return r;
 }
 
 int sd_bus_remove_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata) {
+        struct bus_match_component *components = NULL;
+        unsigned n_components = 0;
         int r = 0, q = 0;
+        uint64_t cookie = 0;
 
-        if (!bus)
-                return -EINVAL;
-        if (!match)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
-
-        if (bus->bus_client)
-                r = bus_remove_match_internal(bus, match);
-
-        if (callback) {
-                bus->match_callbacks_modified = true;
-                q = bus_match_remove(&bus->match_callbacks, match, callback, userdata);
-        }
+        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)
                 return r;
-        return q;
-}
-
-int sd_bus_emit_signal(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *member,
-                const char *types, ...) {
 
-        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-        va_list ap;
-        int r;
-
-        if (!bus)
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
-
-        r = sd_bus_message_new_signal(bus, path, interface, member, &m);
-        if (r < 0)
-                return r;
-
-        va_start(ap, types);
-        r = bus_message_append_ap(m, types, ap);
-        va_end(ap);
-        if (r < 0)
-                return r;
-
-        return sd_bus_send(bus, m, NULL);
-}
-
-int sd_bus_call_method(
-                sd_bus *bus,
-                const char *destination,
-                const char *path,
-                const char *interface,
-                const char *member,
-                sd_bus_error *error,
-                sd_bus_message **reply,
-                const char *types, ...) {
+        bus->match_callbacks_modified = true;
+        r = bus_match_remove(&bus->match_callbacks, components, n_components, callback, userdata, &cookie);
 
-        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-        va_list ap;
-        int r;
-
-        if (!bus)
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
-
-        r = sd_bus_message_new_method_call(bus, destination, path, interface, member, &m);
-        if (r < 0)
-                return r;
-
-        va_start(ap, types);
-        r = bus_message_append_ap(m, types, ap);
-        va_end(ap);
-        if (r < 0)
-                return r;
-
-        return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
-}
-
-int sd_bus_reply_method_return(
-                sd_bus *bus,
-                sd_bus_message *call,
-                const char *types, ...) {
-
-        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-        va_list ap;
-        int r;
-
-        if (!bus)
-                return -EINVAL;
-        if (!call)
-                return -EINVAL;
-        if (!call->sealed)
-                return -EPERM;
-        if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
-
-        if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
-                return 0;
-
-        r = sd_bus_message_new_method_return(bus, call, &m);
-        if (r < 0)
-                return r;
-
-        va_start(ap, types);
-        r = bus_message_append_ap(m, types, ap);
-        va_end(ap);
-        if (r < 0)
-                return r;
-
-        return sd_bus_send(bus, m, NULL);
-}
-
-int sd_bus_reply_method_error(
-                sd_bus *bus,
-                sd_bus_message *call,
-                const sd_bus_error *e) {
-
-        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-        int r;
-
-        if (!bus)
-                return -EINVAL;
-        if (!call)
-                return -EINVAL;
-        if (!call->sealed)
-                return -EPERM;
-        if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
-                return -EINVAL;
-        if (!sd_bus_error_is_set(e))
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
-
-        if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
-                return 0;
+        if (bus->bus_client)
+                q = bus_remove_match_internal(bus, match, cookie);
 
-        r = sd_bus_message_new_method_error(bus, call, e, &m);
-        if (r < 0)
-                return r;
+        bus_match_parse_free(components, n_components);
 
-        return sd_bus_send(bus, m, NULL);
+        return r < 0 ? r : q;
 }
 
 bool bus_pid_changed(sd_bus *bus) {