#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);
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);
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);
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) {
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;
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) {
}
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;
}
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;
return r < 0 ? r : 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);
- 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;
-
- r = sd_bus_message_new_method_error(bus, call, e, &m);
- if (r < 0)
- return r;
-
- return sd_bus_send(bus, m, NULL);
-}
-
bool bus_pid_changed(sd_bus *bus) {
assert(bus);