- if (require_fallback && !c->is_fallback)
- continue;
-
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- empty = false;
- break;
- }
-
- if (empty)
- return 0;
- } else {
- Iterator i;
- char *path;
-
- SET_FOREACH(path, s, i) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-
- r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
- if (r < 0)
- return -ENOMEM;
-
- if (sd_bus_error_is_set(&error)) {
- r = sd_bus_reply_method_error(bus, m, &error);
- if (r < 0)
- return r;
-
- return 1;
- }
- }
- }
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- r = sd_bus_send(bus, reply, NULL);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int object_find_and_run(sd_bus *bus, sd_bus_message *m, const char *p, bool require_fallback, bool *found_object) {
- struct node *n;
- struct vtable_member vtable_key, *v;
- int r;
-
- assert(bus);
- assert(m);
- assert(p);
- assert(found_object);
-
- n = hashmap_get(bus->nodes, p);
- if (!n)
- return 0;
-
- /* First, try object callbacks */
- r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
- if (r != 0)
- return r;
-
- if (!m->interface || !m->member)
- return 0;
-
- /* Then, look for a known method */
- vtable_key.path = (char*) p;
- vtable_key.interface = m->interface;
- vtable_key.member = m->member;
-
- v = hashmap_get(bus->vtable_methods, &vtable_key);
- if (v) {
- r = method_callbacks_run(bus, m, v, require_fallback, found_object);
- if (r != 0)
- return r;
- }
-
- /* Then, look for a known property */
- if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
- bool get = false;
-
- get = streq(m->member, "Get");
-
- if (get || streq(m->member, "Set")) {
-
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
-
- vtable_key.path = (char*) p;
-
- r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
- if (r < 0)
- return r;
-
- v = hashmap_get(bus->vtable_properties, &vtable_key);
- if (v) {
- r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
- if (r != 0)
- return r;
- }
-
- } else if (streq(m->member, "GetAll")) {
- const char *iface;
-
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(m, "s", &iface);
- if (r < 0)
- return r;
-
- if (iface[0] == 0)
- iface = NULL;
-
- r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
- if (r != 0)
- return r;
- }
-
- } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
-
- r = process_introspect(bus, m, n, require_fallback, found_object);
- if (r != 0)
- return r;
-
- } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
-
- r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
- if (r != 0)
- return r;
- }
-
- if (!*found_object) {
- r = bus_node_exists(bus, n, m->path, require_fallback);
- if (r < 0)
- return r;
-
- if (r > 0)
- *found_object = true;
- }
-
- return 0;
-}
-
-static int process_object(sd_bus *bus, sd_bus_message *m) {
- int r;
- size_t pl;
- bool found_object = false;
-
- assert(bus);
- assert(m);
-
- if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
- return 0;
-
- if (!m->path)
- return 0;
-
- if (hashmap_isempty(bus->nodes))
- return 0;
-
- pl = strlen(m->path);
- do {
- char p[pl+1];
-
- bus->nodes_modified = false;
-
- r = object_find_and_run(bus, m, m->path, false, &found_object);
- if (r != 0)
- return r;
-
- /* Look for fallback prefixes */
- strcpy(p, m->path);
- for (;;) {
- char *e;
-
- if (streq(p, "/"))
- break;
-
- if (bus->nodes_modified)
- break;
-
- e = strrchr(p, '/');
- assert(e);
- if (e == p)
- *(e+1) = 0;
- else
- *e = 0;
-
- r = object_find_and_run(bus, m, p, true, &found_object);
- if (r != 0)
- return r;
- }
-
- } while (bus->nodes_modified);
-
- if (!found_object)
- return 0;
-
- if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
- sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
- r = sd_bus_reply_method_errorf(
- bus, m,
- "org.freedesktop.DBus.Error.UnknownProperty",
- "Unknown property or interface.");
- else
- r = sd_bus_reply_method_errorf(
- bus, m,
- "org.freedesktop.DBus.Error.UnknownMethod",
- "Unknown method '%s' or interface '%s'.", m->member, m->interface);
-
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int process_message(sd_bus *bus, sd_bus_message *m) {
- int r;
-
- assert(bus);
- assert(m);
-
- bus->iteration_counter++;
-
- r = process_hello(bus, m);
- if (r != 0)
- return r;
-
- r = process_reply(bus, m);
- if (r != 0)
- return r;
-
- r = process_filter(bus, m);
- if (r != 0)
- return r;
-
- r = process_match(bus, m);
- if (r != 0)
- return r;
-
- r = process_builtin(bus, m);
- if (r != 0)
- return r;
-
- return process_object(bus, m);
-}
-
-static int process_running(sd_bus *bus, sd_bus_message **ret) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert(bus);
- assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
-
- r = process_timeout(bus);
- if (r != 0)
- goto null_message;
-
- r = dispatch_wqueue(bus);
- if (r != 0)
- goto null_message;
-
- r = dispatch_rqueue(bus, &m);
- if (r < 0)
- return r;
- if (!m)
- goto null_message;
-
- r = process_message(bus, m);
- if (r != 0)
- goto null_message;
-
- if (ret) {
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
-
- *ret = m;
- m = NULL;
- return 1;
- }
-
- if (m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL) {
-
- r = sd_bus_reply_method_errorf(
- bus, m,
- "org.freedesktop.DBus.Error.UnknownObject",
- "Unknown object '%s'.", m->path);
- if (r < 0)
- return r;
- }
-
- return 1;
-
-null_message:
- if (r >= 0 && ret)
- *ret = NULL;
-
- return r;
-}
-
-int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
- int r;
-
- /* Returns 0 when we didn't do anything. This should cause the
- * caller to invoke sd_bus_wait() before returning the next
- * 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;
-
- /* We don't allow recursively invoking sd_bus_process(). */
- if (bus->processing)
- return -EBUSY;
-
- switch (bus->state) {
-
- case BUS_UNSET:
- case BUS_CLOSED:
- return -ENOTCONN;
-
- case BUS_OPENING:
- r = bus_socket_process_opening(bus);
- if (r < 0)
- return r;
- if (ret)
- *ret = NULL;
- return r;
-
- case BUS_AUTHENTICATING:
-
- r = bus_socket_process_authenticating(bus);
- if (r < 0)
- return r;
- if (ret)
- *ret = NULL;
- return r;
-
- case BUS_RUNNING:
- case BUS_HELLO:
-
- bus->processing = true;
- r = process_running(bus, ret);
- bus->processing = false;
-
- return r;
- }
-
- assert_not_reached("Unknown state");
-}
-
-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;
-
- assert(bus);
-
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
-
- e = sd_bus_get_events(bus);
- if (e < 0)
- return e;
-
- if (need_more)
- 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;
- }
-
- if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
- m = timeout_usec;
-
- p[0].fd = bus->input_fd;
- if (bus->output_fd == bus->input_fd) {
- p[0].events = e;
- n = 1;
- } else {
- p[0].events = e & POLLIN;
- p[1].fd = bus->output_fd;
- p[1].events = e & POLLOUT;
- n = 2;
- }
-
- r = ppoll(p, n, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
- if (r < 0)
- return -errno;
-
- return r > 0 ? 1 : 0;
-}
-
-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;
-
- if (bus->rqueue_size > 0)
- return 0;
-
- return bus_poll(bus, false, 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;
-
- r = bus_ensure_running(bus);
- if (r < 0)
- return r;
-
- if (bus->wqueue_size <= 0)
- return 0;
-
- for (;;) {
- r = dispatch_wqueue(bus);
- if (r < 0)
- return r;
-
- if (bus->wqueue_size <= 0)
- return 0;
-
- r = bus_poll(bus, false, (uint64_t) -1);
- if (r < 0)
- return r;
- }
-}
-
-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;
-
- f = new0(struct filter_callback, 1);
- if (!f)
- return -ENOMEM;
- f->callback = callback;
- f->userdata = userdata;
-
- bus->filter_callbacks_modified = true;
- LIST_PREPEND(struct filter_callback, 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;
-
- 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);
- free(f);
- return 1;
- }
- }
-
- return 0;
-}
-
-static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
- struct node *n, *parent;
- const char *e;
- char *s, *p;
- int r;
-
- assert(bus);
- assert(path);
- assert(path[0] == '/');
-
- n = hashmap_get(bus->nodes, path);
- if (n)
- return n;
-
- r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
- if (r < 0)
- return NULL;
-
- s = strdup(path);
- if (!s)
- return NULL;
-
- if (streq(path, "/"))
- parent = NULL;
- else {
- e = strrchr(path, '/');
- assert(e);
-
- p = strndupa(path, MAX(1, path - e));
-
- parent = bus_node_allocate(bus, p);
- if (!parent) {
- free(s);
- return NULL;
- }
- }
-
- n = new0(struct node, 1);
- if (!n)
- return NULL;
-
- n->parent = parent;
- n->path = s;
-
- r = hashmap_put(bus->nodes, s, n);
- if (r < 0) {
- free(s);
- free(n);
- return NULL;
- }
-
- if (parent)
- LIST_PREPEND(struct node, siblings, parent->child, n);
-
- return n;
-}
-
-static void bus_node_gc(sd_bus *b, struct node *n) {
- assert(b);
-
- if (!n)
- return;
-
- if (n->child ||
- n->callbacks ||
- n->vtables ||
- n->enumerators ||
- n->object_manager)
- return;
-
- assert(hashmap_remove(b->nodes, n->path) == n);
-
- if (n->parent)
- LIST_REMOVE(struct node, siblings, n->parent->child, n);
-
- free(n->path);
- bus_node_gc(b, n->parent);
- free(n);
-}
-
-static int bus_add_object(
- sd_bus *b,
- bool fallback,
- const char *path,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
- struct node_callback *c;
- struct node *n;
- int r;
-
- if (!b)
- return -EINVAL;
- if (!object_path_is_valid(path))
- return -EINVAL;
- if (!callback)
- return -EINVAL;
- if (bus_pid_changed(b))
- return -ECHILD;
-
- n = bus_node_allocate(b, path);
- if (!n)
- return -ENOMEM;
-
- c = new0(struct node_callback, 1);
- if (!c) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->node = n;
- c->callback = callback;
- c->userdata = userdata;
- c->is_fallback = fallback;
-
- LIST_PREPEND(struct node_callback, callbacks, n->callbacks, c);
- return 0;
-
-fail:
- free(c);
- bus_node_gc(b, n);
- return r;
-}
-
-static int bus_remove_object(
- sd_bus *bus,
- bool fallback,
- const char *path,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
- struct node_callback *c;
- struct node *n;
-
- if (!bus)
- return -EINVAL;
- if (!object_path_is_valid(path))
- return -EINVAL;
- if (!callback)
- return -EINVAL;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- n = hashmap_get(bus->nodes, path);
- if (!n)
- return 0;
-
- LIST_FOREACH(callbacks, c, n->callbacks)
- if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
- break;
- if (!c)
- return 0;
-
- LIST_REMOVE(struct node_callback, callbacks, n->callbacks, c);
- free(c);
-
- bus_node_gc(bus, n);
-
- 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) {
- cookie = ++bus->match_cookie;
-
- r = bus_add_match_internal(bus, match, components, n_components, cookie);
- if (r < 0)
- goto finish;
- }
-
- 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;
-
- r = bus_match_parse(match, &components, &n_components);
- if (r < 0)
- return r;
-
- bus->match_callbacks_modified = true;
- r = bus_match_remove(&bus->match_callbacks, components, n_components, callback, userdata, &cookie);
-
- if (bus->bus_client)
- q = bus_remove_match_internal(bus, match, cookie);