chiark / gitweb /
bus: beef up parameter checking of convenience calls
[elogind.git] / src / libsystemd-bus / sd-bus.c
index 5e66a31162611ac2d7ae988840aa55950a458320..ca687c0a65413a0ac1739b0d37e9d638a0e66ce8 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(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);
@@ -97,14 +137,16 @@ 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);
@@ -125,7 +167,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);
@@ -226,7 +268,7 @@ int sd_bus_set_bus_client(sd_bus *bus, int 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)
@@ -234,7 +276,91 @@ int sd_bus_set_negotiate_fds(sd_bus *bus, int b) {
         if (bus_pid_changed(bus))
                 return -ECHILD;
 
-        bus->negotiate_fds = !!b;
+        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;
+
+        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;
 }
 
@@ -1015,7 +1141,7 @@ int sd_bus_can_send(sd_bus *bus, char type) {
                 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);
@@ -1131,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) {
@@ -1734,13 +1860,10 @@ 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)
@@ -1753,188 +1876,6 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) {
         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;
-
-        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;
 
@@ -1963,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) {
@@ -2006,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;
         }
@@ -2221,105 +2153,10 @@ 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)
@@ -2329,27 +2166,35 @@ int sd_bus_add_match(sd_bus *bus, const char *match, sd_bus_message_handler_t ca
         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;
@@ -2358,153 +2203,19 @@ int sd_bus_remove_match(sd_bus *bus, const char *match, sd_bus_message_handler_t
         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;
-        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, ...) {
-
-        _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);
+        r = bus_match_parse(match, &components, &n_components);
         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;
+        bus->match_callbacks_modified = true;
+        r = bus_match_remove(&bus->match_callbacks, components, n_components, callback, userdata, &cookie);
 
-        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) {