#include <netdb.h>
#include <sys/poll.h>
#include <byteswap.h>
+#include <sys/mman.h>
+#include <pthread.h>
#include "util.h"
#include "macro.h"
#include "strv.h"
#include "set.h"
+#include "missing.h"
#include "sd-bus.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-type.h"
#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);
+static void bus_close_fds(sd_bus *b) {
+ assert(b);
+
+ if (b->input_fd >= 0)
+ close_nointr_nofail(b->input_fd);
+
+ if (b->output_fd >= 0 && b->output_fd != b->input_fd)
+ close_nointr_nofail(b->output_fd);
+
+ 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);
- sd_bus_close(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);
free(b->address);
+ free(b->kernel);
free(b->exec_path);
strv_free(b->exec_argv);
free(f);
}
- while ((c = hashmap_steal_first(b->object_callbacks))) {
- free(c->path);
- free(c);
- }
+ bus_match_free(&b->match_callbacks);
- hashmap_free(b->object_callbacks);
+ hashmap_free_free(b->vtable_methods);
+ hashmap_free_free(b->vtable_properties);
- bus_match_free(&b->match_callbacks);
+ 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);
}
if (!r)
return -ENOMEM;
- r->n_ref = 1;
+ 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 */
return -EPERM;
if (!address)
return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
a = strdup(address);
if (!a)
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;
return -EINVAL;
if (strv_isempty(argv))
return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
p = strdup(path);
if (!p)
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;
+
+ 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;
- bus->negotiate_fds = !!b;
+ 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;
}
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;
return -EINVAL;
if (bus->state != BUS_UNSET)
return -EPERM;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
bus->anonymous_auth = !!b;
return 0;
}
-static int hello_callback(sd_bus *bus, int error, sd_bus_message *reply, void *userdata) {
+static int hello_callback(sd_bus *bus, sd_bus_message *reply, void *userdata) {
const char *s;
int r;
assert(bus);
assert(bus->state == BUS_HELLO);
-
- if (error != 0)
- return -error;
-
assert(reply);
+ r = bus_message_to_errno(reply);
+ if (r < 0)
+ return r;
+
r = sd_bus_message_read(reply, "s", &s);
if (r < 0)
return r;
assert(bus);
- if (!bus->bus_client)
+ if (!bus->bus_client || bus->is_kernel)
return 0;
r = sd_bus_message_new_method_call(
int bus_start_running(sd_bus *bus) {
assert(bus);
- if (bus->bus_client) {
+ if (bus->bus_client && !bus->is_kernel) {
bus->state = BUS_HELLO;
return 1;
}
static int parse_tcp_address(sd_bus *b, const char **p, char **guid) {
_cleanup_free_ char *host = NULL, *port = NULL, *family = NULL;
- struct addrinfo hints, *result;
int r;
+ struct addrinfo *result, hints = {
+ .ai_socktype = SOCK_STREAM,
+ .ai_flags = AI_ADDRCONFIG,
+ };
assert(b);
assert(p);
if (!host || !port)
return -EINVAL;
- zero(hints);
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_ADDRCONFIG;
-
if (family) {
if (streq(family, "ipv4"))
hints.ai_family = AF_INET;
return r;
}
+static int parse_kernel_address(sd_bus *b, const char **p, char **guid) {
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ assert(b);
+ assert(p);
+ assert(*p);
+ assert(guid);
+
+ while (**p != 0 && **p != ';') {
+ r = parse_address_key(p, "guid", guid);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "path", &path);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ skip_address_key(p);
+ }
+
+ if (!path)
+ return -EINVAL;
+
+ free(b->kernel);
+ b->kernel = path;
+ path = NULL;
+
+ return 0;
+}
+
static void bus_reset_parsed_address(sd_bus *b) {
assert(b);
b->exec_path = NULL;
b->exec_argv = NULL;
b->server_id = SD_ID128_NULL;
+ free(b->kernel);
+ b->kernel = NULL;
}
static int bus_parse_next_address(sd_bus *b) {
break;
+ } else if (startswith(a, "kernel:")) {
+
+ a += 7;
+ r = parse_kernel_address(b, &a, &guid);
+ if (r < 0)
+ return r;
+
+ break;
}
a = strchr(a, ';');
if (r >= 0)
return r;
+ b->last_connect_error = -r;
+ } else if (b->kernel) {
+
+ r = bus_kernel_connect(b);
+ if (r >= 0)
+ return r;
+
b->last_connect_error = -r;
}
}
static int bus_start_fd(sd_bus *b) {
+ struct stat st;
int r;
assert(b);
return r;
}
- return bus_socket_take_fd(b);
+ if (fstat(b->input_fd, &st) < 0)
+ return -errno;
+
+ if (S_ISCHR(b->input_fd))
+ return bus_kernel_take_fd(b);
+ else
+ return bus_socket_take_fd(b);
}
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;
if (bus->input_fd >= 0)
r = bus_start_fd(bus);
- else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path)
+ else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path || bus->kernel)
r = bus_start_address(bus);
else
return -EINVAL;
if (r < 0)
return r;
- e = getenv("DBUS_SYSTEM_BUS_ADDRESS");
+ e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS");
if (e) {
r = sd_bus_set_address(b, e);
if (r < 0)
if (r < 0)
return r;
- e = getenv("DBUS_SESSION_BUS_ADDRESS");
+ e = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
if (e) {
r = sd_bus_set_address(b, e);
if (r < 0)
goto fail;
} else {
- e = getenv("XDG_RUNTIME_DIR");
+ e = secure_getenv("XDG_RUNTIME_DIR");
if (!e) {
r = -ENOENT;
goto fail;
void sd_bus_close(sd_bus *bus) {
if (!bus)
return;
+ if (bus->state == BUS_CLOSED)
+ return;
+ if (bus_pid_changed(bus))
+ return;
- if (bus->input_fd >= 0)
- close_nointr_nofail(bus->input_fd);
- if (bus->output_fd >= 0 && bus->output_fd != bus->input_fd)
- close_nointr_nofail(bus->output_fd);
+ bus->state = BUS_CLOSED;
+
+ if (!bus->is_kernel)
+ bus_close_fds(bus);
- bus->input_fd = bus->output_fd = -1;
+ /* We'll leave the fd open in case this is a kernel bus, since
+ * there might still be memblocks around that reference this
+ * bus, and they might need to invoke the
+ * KDBUS_CMD_MSG_RELEASE ioctl on the fd when they are
+ * freed. */
}
sd_bus *sd_bus_ref(sd_bus *bus) {
if (!bus)
return NULL;
- assert(bus->n_ref > 0);
+ assert_se(REFCNT_INC(bus->n_ref) >= 2);
- bus->n_ref++;
return bus;
}
if (!bus)
return NULL;
- assert(bus->n_ref > 0);
- bus->n_ref--;
-
- if (bus->n_ref <= 0)
+ if (REFCNT_DEC(bus->n_ref) <= 0)
bus_free(bus);
return NULL;
int sd_bus_is_open(sd_bus *bus) {
if (!bus)
return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
- return bus->state != BUS_UNSET && bus->input_fd >= 0;
+ return BUS_IS_OPEN(bus->state);
}
int sd_bus_can_send(sd_bus *bus, char type) {
if (!bus)
return -EINVAL;
- if (bus->output_fd < 0)
+ 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);
return -EINVAL;
if (!server_id)
return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
r = bus_ensure_running(bus);
if (r < 0)
assert(bus);
assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
- if (bus->output_fd < 0)
- return -ENOTCONN;
-
while (bus->wqueue_size > 0) {
- r = bus_socket_write_message(bus, bus->wqueue[0], &bus->windex);
+ if (bus->is_kernel)
+ r = bus_kernel_write_message(bus, bus->wqueue[0]);
+ else
+ r = bus_socket_write_message(bus, bus->wqueue[0], &bus->windex);
+
if (r < 0) {
sd_bus_close(bus);
return r;
} else if (r == 0)
/* Didn't do anything this time */
return ret;
- else if (bus->windex >= bus->wqueue[0]->size) {
+ else if (bus->is_kernel || bus->windex >= BUS_MESSAGE_SIZE(bus->wqueue[0])) {
/* Fully written. Let's drop the entry from
* the queue.
*
assert(m);
assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
- if (bus->input_fd < 0)
- return -ENOTCONN;
-
if (bus->rqueue_size > 0) {
/* Dispatch a queued message */
/* Try to read a new message */
do {
- r = bus_socket_read_message(bus, &z);
+ if (bus->is_kernel)
+ r = bus_kernel_read_message(bus, &z);
+ else
+ r = bus_socket_read_message(bus, &z);
+
if (r < 0) {
sd_bus_close(bus);
return r;
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) {
if (!bus)
return -EINVAL;
- if (bus->state == BUS_UNSET)
- return -ENOTCONN;
- if (bus->output_fd < 0)
+ if (!BUS_IS_OPEN(bus->state))
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);
if ((bus->state == BUS_RUNNING || bus->state == BUS_HELLO) && bus->wqueue_size <= 0) {
size_t idx = 0;
- r = bus_socket_write_message(bus, m, &idx);
+ if (bus->is_kernel)
+ r = bus_kernel_write_message(bus, m);
+ else
+ r = bus_socket_write_message(bus, m, &idx);
+
if (r < 0) {
sd_bus_close(bus);
return r;
- } else if (idx < m->size) {
+ } else if (!bus->is_kernel && idx < BUS_MESSAGE_SIZE(m)) {
/* Wasn't fully written. So let's remember how
* much was written. Note that the first entry
* of the wqueue array is always allocated so
if (!bus)
return -EINVAL;
- if (bus->state == BUS_UNSET)
- return -ENOTCONN;
- if (bus->output_fd < 0)
+ if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
if (!m)
return -EINVAL;
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)
if (r < 0)
return r;
- c = new(struct reply_callback, 1);
+ c = new0(struct reply_callback, 1);
if (!c)
return -ENOMEM;
return -EINVAL;
if (serial == 0)
return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
c = hashmap_remove(bus->reply_callbacks, &serial);
if (!c)
assert(bus);
- if (bus->input_fd < 0)
+ if (bus->state == BUS_UNSET || bus->state == BUS_CLOSED)
return -ENOTCONN;
- if (bus->state == BUS_UNSET)
- return -ENOTCONN;
-
if (bus->state == BUS_RUNNING)
return 1;
if (!bus)
return -EINVAL;
- if (bus->output_fd < 0)
- return -ENOTCONN;
- if (bus->state == BUS_UNSET)
+ if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
if (!m)
return -EINVAL;
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)
room = true;
}
- r = bus_socket_read_message(bus, &incoming);
+ if (bus->is_kernel)
+ r = bus_kernel_read_message(bus, &incoming);
+ else
+ r = bus_socket_read_message(bus, &incoming);
if (r < 0)
return r;
if (incoming) {
/* Found a match! */
if (incoming->header->type == SD_BUS_MESSAGE_TYPE_METHOD_RETURN) {
- *reply = incoming;
+
+ if (reply)
+ *reply = incoming;
+ else
+ sd_bus_message_unref(incoming);
+
return 0;
}
int sd_bus_get_fd(sd_bus *bus) {
if (!bus)
return -EINVAL;
- if (bus->input_fd < 0)
+ if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
if (bus->input_fd != bus->output_fd)
return -EPERM;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
return bus->input_fd;
}
if (!bus)
return -EINVAL;
- if (bus->state == BUS_UNSET)
- return -ENOTCONN;
- if (bus->input_fd < 0)
+ if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
if (bus->state == BUS_OPENING)
flags |= POLLOUT;
return -EINVAL;
if (!timeout_usec)
return -EINVAL;
- if (bus->state == BUS_UNSET)
- return -ENOTCONN;
- if (bus->input_fd < 0)
+ 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;
return 1;
}
- if (bus->state != BUS_RUNNING && bus->state != BUS_HELLO)
+ if (bus->state != BUS_RUNNING && bus->state != BUS_HELLO) {
+ *timeout_usec = (uint64_t) -1;
return 0;
+ }
c = prioq_peek(bus->reply_callbacks_prioq);
- if (!c)
+ if (!c) {
+ *timeout_usec = (uint64_t) -1;
return 0;
+ }
*timeout_usec = c->timeout;
return 1;
}
static int process_timeout(sd_bus *bus) {
+ _cleanup_bus_message_unref_ sd_bus_message* m = NULL;
struct reply_callback *c;
usec_t n;
int r;
if (c->timeout > n)
return 0;
+ r = bus_message_new_synthetic_error(
+ bus,
+ c->serial,
+ &SD_BUS_ERROR_MAKE("org.freedesktop.DBus.Error.Timeout", "Timed out"),
+ &m);
+ if (r < 0)
+ return r;
+
assert_se(prioq_pop(bus->reply_callbacks_prioq) == c);
hashmap_remove(bus->reply_callbacks, &c->serial);
- r = c->callback(bus, ETIMEDOUT, NULL, c->userdata);
+ r = c->callback(bus, m, c->userdata);
free(c);
return r < 0 ? r : 1;
if (c->timeout != 0)
prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
- r = c->callback(bus, 0, m, c->userdata);
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ r = c->callback(bus, m, c->userdata);
free(c);
return r;
assert(bus);
assert(m);
- LIST_FOREACH(callbacks, l, bus->filter_callbacks) {
- r = l->callback(bus, 0, m, l->userdata);
- if (r != 0)
- return r;
- }
+ do {
+ bus->filter_callbacks_modified = false;
+
+ LIST_FOREACH(callbacks, l, bus->filter_callbacks) {
+
+ if (bus->filter_callbacks_modified)
+ break;
+
+ /* Don't run this more than once per iteration */
+ if (l->last_iteration == bus->iteration_counter)
+ continue;
+
+ l->last_iteration = bus->iteration_counter;
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ r = l->callback(bus, m, l->userdata);
+ if (r != 0)
+ return r;
+
+ }
+
+ } while (bus->filter_callbacks_modified);
return 0;
}
static int process_match(sd_bus *bus, sd_bus_message *m) {
+ int r;
+
assert(bus);
assert(m);
- return bus_match_run(bus, &bus->match_callbacks, 0, m);
+ do {
+ bus->match_callbacks_modified = false;
+
+ r = bus_match_run(bus, &bus->match_callbacks, m);
+ if (r != 0)
+ return r;
+
+ } while (bus->match_callbacks_modified);
+
+ return 0;
}
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_INIT;
-
- 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_INIT;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- struct object_callback *c;
- char *p;
- int r;
- bool found = false;
-
- assert(bus);
- assert(m);
-
- if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
- return 0;
-
- if (hashmap_isempty(bus->object_callbacks))
- return 0;
-
- c = hashmap_get(bus->object_callbacks, m->path);
- if (c) {
- r = c->callback(bus, 0, m, c->userdata);
- if (r != 0)
- return r;
-
- found = true;
- }
-
- /* Look for fallback prefixes */
- p = strdupa(m->path);
- for (;;) {
- char *e;
-
- e = strrchr(p, '/');
- if (e == p || !e)
- break;
-
- *e = 0;
-
- c = hashmap_get(bus->object_callbacks, p);
- if (c && c->is_fallback) {
- r = c->callback(bus, 0, m, c->userdata);
- if (r != 0)
- return r;
-
- found = true;
- }
- }
-
- /* 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_put(s, a);
- if (r < 0) {
- free(a);
-
- if (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;
assert(bus);
assert(m);
+ bus->iteration_counter++;
+
r = process_hello(bus, m);
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) {
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) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_INIT;
-
- 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;
}
if (!bus)
return -EINVAL;
- if (bus->input_fd < 0)
- return -ENOTCONN;
+ 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:
case BUS_RUNNING:
case BUS_HELLO:
- return process_running(bus, ret);
+ 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];
+ struct pollfd p[2] = {};
int r, e, n;
struct timespec ts;
usec_t until, m;
assert(bus);
- if (bus->input_fd < 0)
+ if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
e = sd_bus_get_events(bus);
if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
m = timeout_usec;
- zero(p);
p[0].fd = bus->input_fd;
-
if (bus->output_fd == bus->input_fd) {
p[0].events = e;
n = 1;
if (!bus)
return -EINVAL;
- if (bus->state == BUS_UNSET)
- return -ENOTCONN;
- if (bus->input_fd < 0)
+ if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
+
if (bus->rqueue_size > 0)
return 0;
if (!bus)
return -EINVAL;
- if (bus->state == BUS_UNSET)
- return -ENOTCONN;
- if (bus->output_fd < 0)
+ if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
r = bus_ensure_running(bus);
if (r < 0)
return -EINVAL;
if (!callback)
return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
- f = new(struct filter_callback, 1);
+ 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;
}
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 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 = new(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;
-
- 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;
-
- 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) {
- 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;
+
+ 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)
- r = bus_remove_match_internal(bus, match);
+ q = bus_remove_match_internal(bus, match, cookie);
- if (callback)
- q = bus_match_remove(&bus->match_callbacks, match, callback, userdata);
+ bus_match_parse_free(components, n_components);
- if (r < 0)
- return r;
- return q;
+ return r < 0 ? r : q;
+}
+
+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();
}