chiark / gitweb /
Fix write-only use of a few variables
[elogind.git] / src / libsystemd-bus / sd-bus.c
index 08ab202baf8301b1eca4c280d2fa52a1e5ba802a..ca687c0a65413a0ac1739b0d37e9d638a0e66ce8 100644 (file)
@@ -26,6 +26,8 @@
 #include <netdb.h>
 #include <sys/poll.h>
 #include <byteswap.h>
+#include <sys/mman.h>
+#include <pthread.h>
 
 #include "util.h"
 #include "macro.h"
@@ -40,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);
 
@@ -55,15 +60,55 @@ 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(struct node_callback, callbacks, n->callbacks, c);
+                free(c);
+        }
+
+        while ((v = n->vtables)) {
+                LIST_REMOVE(struct node_vtable, vtables, n->vtables, v);
+                free(v->interface);
+                free(v);
+        }
+
+        while ((e = n->enumerators)) {
+                LIST_REMOVE(struct node_enumerator, enumerators, n->enumerators, e);
+                free(e);
+        }
+
+        if (n->parent)
+                LIST_REMOVE(struct node, 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);
 
         bus_close_fds(b);
 
+        if (b->kdbus_buffer)
+                munmap(b->kdbus_buffer, KDBUS_POOL_SIZE);
+
         free(b->rbuffer);
         free(b->unique_name);
         free(b->auth_buffer);
@@ -92,16 +137,20 @@ static void bus_free(sd_bus *b) {
                 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);
+
         free(b);
 }
 
@@ -118,7 +167,10 @@ 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);
 
         /* We guarantee that wqueue always has space for at least one
          * entry */
@@ -141,6 +193,8 @@ int sd_bus_set_address(sd_bus *bus, const char *address) {
                 return -EPERM;
         if (!address)
                 return -EINVAL;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         a = strdup(address);
         if (!a)
@@ -161,6 +215,8 @@ int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) {
                 return -EINVAL;
         if (output_fd < 0)
                 return -EINVAL;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         bus->input_fd = input_fd;
         bus->output_fd = output_fd;
@@ -178,6 +234,8 @@ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) {
                 return -EINVAL;
         if (strv_isempty(argv))
                 return -EINVAL;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         p = strdup(path);
         if (!p)
@@ -203,18 +261,106 @@ int sd_bus_set_bus_client(sd_bus *bus, int b) {
                 return -EINVAL;
         if (bus->state != BUS_UNSET)
                 return -EPERM;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         bus->bus_client = !!b;
         return 0;
 }
 
-int sd_bus_set_negotiate_fds(sd_bus *bus, int b) {
+int sd_bus_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;
+
+        SET_FLAG(bus->hello_flags, KDBUS_HELLO_ACCEPT_FD, b);
+        return 0;
+}
+
+int sd_bus_negotiate_attach_comm(sd_bus *bus, int b) {
         if (!bus)
                 return -EINVAL;
         if (bus->state != BUS_UNSET)
                 return -EPERM;
+        if (bus_pid_changed(bus))
+                return -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) {
+        if (!bus)
+                return -EINVAL;
+        if (bus->state != BUS_UNSET)
+                return -EPERM;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
+
+        SET_FLAG(bus->hello_flags, KDBUS_HELLO_ATTACH_EXE, b);
+        return 0;
+}
+
+int sd_bus_negotiate_attach_cmdline(sd_bus *bus, int b) {
+        if (!bus)
+                return -EINVAL;
+        if (bus->state != BUS_UNSET)
+                return -EPERM;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
+
+        SET_FLAG(bus->hello_flags, KDBUS_HELLO_ATTACH_CMDLINE, b);
+        return 0;
+}
+
+int sd_bus_negotiate_attach_cgroup(sd_bus *bus, int b) {
+        if (!bus)
+                return -EINVAL;
+        if (bus->state != BUS_UNSET)
+                return -EPERM;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
+
+        SET_FLAG(bus->hello_flags, KDBUS_HELLO_ATTACH_CGROUP, b);
+        return 0;
+}
+
+int sd_bus_negotiate_attach_caps(sd_bus *bus, int b) {
+        if (!bus)
+                return -EINVAL;
+        if (bus->state != BUS_UNSET)
+                return -EPERM;
+        if (bus_pid_changed(bus))
+                return -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) {
+        if (!bus)
+                return -EINVAL;
+        if (bus->state != BUS_UNSET)
+                return -EPERM;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
+
+        SET_FLAG(bus->hello_flags, KDBUS_HELLO_ATTACH_SECLABEL, b);
+        return 0;
+}
+
+int sd_bus_negotiate_attach_audit(sd_bus *bus, int b) {
+        if (!bus)
+                return -EINVAL;
+        if (bus->state != BUS_UNSET)
+                return -EPERM;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
+
+        SET_FLAG(bus->hello_flags, KDBUS_HELLO_ATTACH_AUDIT, b);
         return 0;
 }
 
@@ -225,6 +371,8 @@ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) {
                 return -EINVAL;
         if (bus->state != BUS_UNSET)
                 return -EPERM;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         bus->is_server = !!b;
         bus->server_id = server_id;
@@ -236,6 +384,8 @@ int sd_bus_set_anonymous(sd_bus *bus, int b) {
                 return -EINVAL;
         if (bus->state != BUS_UNSET)
                 return -EPERM;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         bus->anonymous_auth = !!b;
         return 0;
@@ -824,6 +974,8 @@ int sd_bus_start(sd_bus *bus) {
                 return -EINVAL;
         if (bus->state != BUS_UNSET)
                 return -EPERM;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         bus->state = BUS_OPENING;
 
@@ -933,8 +1085,9 @@ fail:
 void sd_bus_close(sd_bus *bus) {
         if (!bus)
                 return;
-
-        if (bus->state != BUS_CLOSED)
+        if (bus->state == BUS_CLOSED)
+                return;
+        if (bus_pid_changed(bus))
                 return;
 
         bus->state = BUS_CLOSED;
@@ -971,6 +1124,8 @@ 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;
 
         return BUS_IS_OPEN(bus->state);
 }
@@ -982,9 +1137,11 @@ int sd_bus_can_send(sd_bus *bus, char type) {
                 return -EINVAL;
         if (bus->state == BUS_UNSET)
                 return -ENOTCONN;
+        if (bus_pid_changed(bus))
+                return -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);
@@ -1004,6 +1161,8 @@ int sd_bus_get_server_id(sd_bus *bus, sd_id128_t *server_id) {
                 return -EINVAL;
         if (!server_id)
                 return -EINVAL;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         r = bus_ensure_running(bus);
         if (r < 0)
@@ -1098,11 +1257,11 @@ 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) {
@@ -1114,6 +1273,8 @@ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) {
                 return -ENOTCONN;
         if (!m)
                 return -EINVAL;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         if (m->n_fds > 0) {
                 r = sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD);
@@ -1231,6 +1392,8 @@ int sd_bus_send_with_reply(
                 return -EINVAL;
         if (m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
                 return -EINVAL;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         r = hashmap_ensure_allocated(&bus->reply_callbacks, uint64_hash_func, uint64_compare_func);
         if (r < 0)
@@ -1286,6 +1449,8 @@ int sd_bus_send_with_reply_cancel(sd_bus *bus, uint64_t serial) {
                 return -EINVAL;
         if (serial == 0)
                 return -EINVAL;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         c = hashmap_remove(bus->reply_callbacks, &serial);
         if (!c)
@@ -1347,6 +1512,8 @@ int sd_bus_send_with_reply_and_block(
                 return -EINVAL;
         if (bus_error_is_dirty(error))
                 return -EINVAL;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         r = bus_ensure_running(bus);
         if (r < 0)
@@ -1457,6 +1624,8 @@ int sd_bus_get_fd(sd_bus *bus) {
                 return -ENOTCONN;
         if (bus->input_fd != bus->output_fd)
                 return -EPERM;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         return bus->input_fd;
 }
@@ -1468,6 +1637,8 @@ int sd_bus_get_events(sd_bus *bus) {
                 return -EINVAL;
         if (!BUS_IS_OPEN(bus->state))
                 return -ENOTCONN;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         if (bus->state == BUS_OPENING)
                 flags |= POLLOUT;
@@ -1497,6 +1668,8 @@ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) {
                 return -EINVAL;
         if (!BUS_IS_OPEN(bus->state))
                 return -ENOTCONN;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         if (bus->state == BUS_AUTHENTICATING) {
                 *timeout_usec = bus->auth_timeout;
@@ -1687,197 +1860,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;
 
@@ -1916,11 +1904,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) {
@@ -1959,16 +1943,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;
         }
@@ -1992,6 +1971,8 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
 
         if (!bus)
                 return -EINVAL;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         /* We don't allow recursively invoking sd_bus_process(). */
         if (bus->processing)
@@ -2089,6 +2070,9 @@ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) {
                 return -EINVAL;
         if (!BUS_IS_OPEN(bus->state))
                 return -ENOTCONN;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
+
         if (bus->rqueue_size > 0)
                 return 0;
 
@@ -2102,6 +2086,8 @@ int sd_bus_flush(sd_bus *bus) {
                 return -EINVAL;
         if (!BUS_IS_OPEN(bus->state))
                 return -ENOTCONN;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         r = bus_ensure_running(bus);
         if (r < 0)
@@ -2131,6 +2117,8 @@ int sd_bus_add_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *user
                 return -EINVAL;
         if (!callback)
                 return -EINVAL;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         f = new0(struct filter_callback, 1);
         if (!f)
@@ -2150,6 +2138,8 @@ int sd_bus_remove_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *u
                 return -EINVAL;
         if (!callback)
                 return -EINVAL;
+        if (bus_pid_changed(bus))
+                return -ECHILD;
 
         LIST_FOREACH(callbacks, f, bus->filter_callbacks) {
                 if (f->callback == callback && f->userdata == userdata) {
@@ -2163,264 +2153,76 @@ 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;
-
-        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;
-
-        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;
+
+        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);
-        }
-
-        if (r < 0)
-                return r;
-        return q;
-}
-
-int sd_bus_emit_signal(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *member,
-                const char *types, ...) {
-
-        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-        va_list ap;
-        int r;
-
-        if (!bus)
-                return -EINVAL;
-
-        r = sd_bus_message_new_signal(bus, path, interface, member, &m);
-        if (r < 0)
-                return r;
-
-        va_start(ap, types);
-        r = bus_message_append_ap(m, types, ap);
-        va_end(ap);
-        if (r < 0)
-                return r;
-
-        return sd_bus_send(bus, m, NULL);
-}
-
-int sd_bus_call_method(
-                sd_bus *bus,
-                const char *destination,
-                const char *path,
-                const char *interface,
-                const char *member,
-                sd_bus_error *error,
-                sd_bus_message **reply,
-                const char *types, ...) {
-
-        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-        va_list ap;
-        int r;
-
-        if (!bus)
-                return -EINVAL;
-
-        r = sd_bus_message_new_method_call(bus, destination, path, interface, member, &m);
+        r = bus_match_parse(match, &components, &n_components);
         if (r < 0)
                 return r;
 
-        va_start(ap, types);
-        r = bus_message_append_ap(m, types, ap);
-        va_end(ap);
-        if (r < 0)
-                return r;
-
-        return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
-}
-
-int sd_bus_reply_method_return(
-                sd_bus *bus,
-                sd_bus_message *call,
-                const char *types, ...) {
-
-        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-        va_list ap;
-        int r;
-
-        if (!bus)
-                return -EINVAL;
-        if (!call)
-                return -EINVAL;
-        if (!call->sealed)
-                return -EPERM;
-        if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
-                return -EINVAL;
-
-        if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
-                return 0;
+        bus->match_callbacks_modified = true;
+        r = bus_match_remove(&bus->match_callbacks, components, n_components, callback, userdata, &cookie);
 
-        r = sd_bus_message_new_method_return(bus, call, &m);
-        if (r < 0)
-                return r;
+        if (bus->bus_client)
+                q = bus_remove_match_internal(bus, match, cookie);
 
-        va_start(ap, types);
-        r = bus_message_append_ap(m, types, ap);
-        va_end(ap);
-        if (r < 0)
-                return r;
+        bus_match_parse_free(components, n_components);
 
-        return sd_bus_send(bus, m, NULL);
+        return r < 0 ? r : q;
 }
 
-int sd_bus_reply_method_error(
-                sd_bus *bus,
-                sd_bus_message *call,
-                const sd_bus_error *e) {
-
-        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-        int r;
-
-        if (!bus)
-                return -EINVAL;
-        if (!call)
-                return -EINVAL;
-        if (!call->sealed)
-                return -EPERM;
-        if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
-                return -EINVAL;
-        if (!sd_bus_error_is_set(e))
-                return -EINVAL;
-
-        if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
-                return 0;
+bool bus_pid_changed(sd_bus *bus) {
+        assert(bus);
 
-        r = sd_bus_message_new_method_error(bus, call, e, &m);
-        if (r < 0)
-                return r;
+        /* We don't support people creating a bus connection and
+         * keeping it around over a fork(). Let's complain. */
 
-        return sd_bus_send(bus, m, NULL);
+        return bus->original_pid != getpid();
 }