+ 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);
+
+ bus_match_parse_free(components, n_components);
+
+ 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);
+}
+
+int sd_bus_reply_method_errorf(
+ sd_bus *bus,
+ sd_bus_message *call,
+ const char *name,
+ const char *format,
+ ...) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ char *n, *m;
+ va_list ap;
+ int r;
+
+ n = strdup(name);
+ if (!n)
+ return -ENOMEM;
+
+ if (format) {
+ va_start(ap, format);
+ r = vasprintf(&m, format, ap);
+ va_end(ap);
+
+ if (r < 0) {
+ free(n);
+ return -ENOMEM;
+ }
+ }
+
+ error.name = n;
+ error.message = m;
+ error.need_free = true;
+
+ return sd_bus_reply_method_error(bus, call, &error);
+}
+
+bool bus_pid_changed(sd_bus *bus) {
+ assert(bus);
+
+ /* We don't support people creating a bus connection and
+ * keeping it around over a fork(). Let's complain. */
+
+ return bus->original_pid != getpid();
+}
+
+static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
+ assert(bus);
+
+ if (!w)
+ return;
+
+ if (w->interface && w->node && w->vtable) {
+ const sd_bus_vtable *v;
+
+ for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; w++) {
+ struct vtable_member *x = NULL;
+
+ switch (v->type) {
+
+ case _SD_BUS_VTABLE_METHOD: {
+ struct vtable_member key;
+
+ key.path = w->node->path;
+ key.interface = w->interface;
+ key.member = v->method.member;
+
+ x = hashmap_remove(bus->vtable_methods, &key);
+ break;
+ }
+
+ case _SD_BUS_VTABLE_PROPERTY:
+ case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
+ struct vtable_member key;
+
+ key.path = w->node->path;
+ key.interface = w->interface;
+ key.member = v->property.member;
+ x = hashmap_remove(bus->vtable_properties, &key);
+ break;
+ }}
+
+ free(x);
+ }
+ }
+
+ free(w->interface);
+ free(w);
+}
+
+static unsigned vtable_member_hash_func(const void *a) {
+ const struct vtable_member *m = a;
+
+ return
+ string_hash_func(m->path) ^
+ string_hash_func(m->interface) ^
+ string_hash_func(m->member);
+}
+
+static int vtable_member_compare_func(const void *a, const void *b) {
+ const struct vtable_member *x = a, *y = b;
+ int r;
+
+ r = strcmp(x->path, y->path);
+ if (r != 0)
+ return r;
+
+ r = strcmp(x->interface, y->interface);
+ if (r != 0)
+ return r;
+
+ return strcmp(x->member, y->member);
+}
+
+static int add_object_vtable_internal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const sd_bus_vtable *vtable,
+ bool fallback,
+ sd_bus_object_find_t find,
+ void *userdata) {
+
+ struct node_vtable *c, *i;
+ const sd_bus_vtable *v;
+ struct node *n;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!object_path_is_valid(path))
+ return -EINVAL;
+ if (!interface_name_is_valid(interface))
+ return -EINVAL;
+ if (!vtable || vtable[0].type != _SD_BUS_VTABLE_START || vtable[0].start.element_size != sizeof(struct sd_bus_vtable))
+ return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
+
+ r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
+ if (r < 0)
+ return r;
+
+ r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
+ if (r < 0)
+ return r;
+
+ n = bus_node_allocate(bus, path);
+ if (!n)
+ return -ENOMEM;
+
+ LIST_FOREACH(vtables, i, n->vtables) {
+ if (streq(i->interface, interface)) {
+ r = -EEXIST;
+ goto fail;
+ }
+
+ if (i->is_fallback != fallback) {
+ r = -EPROTOTYPE;
+ goto fail;
+ }
+ }
+
+ c = new0(struct node_vtable, 1);
+ if (!c) {
+ r = -ENOMEM;
+ goto fail;
+ }