static void bus_reset_queues(sd_bus *b) {
assert(b);
+ /* NOTE: We _must_ decrement b->Xqueue_size before calling
+ * sd_bus_message_unref() for _each_ message. Otherwise the
+ * self-reference checks in sd_bus_unref() will fire for each message.
+ * We would thus recurse into sd_bus_message_unref() and trigger the
+ * assert(m->n_ref > 0) */
+
while (b->rqueue_size > 0)
sd_bus_message_unref(b->rqueue[--b->rqueue_size]);
return 0;
}
+_public_ int sd_bus_set_monitor(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus->state == BUS_UNSET, -EPERM);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ SET_FLAG(bus->hello_flags, KDBUS_HELLO_MONITOR, b);
+ return 0;
+}
+
_public_ int sd_bus_negotiate_fds(sd_bus *bus, int b) {
assert_return(bus, -EINVAL);
assert_return(bus->state == BUS_UNSET, -EPERM);
/* The well knowns we need unconditionally, so that matches can work */
bus->creds_mask = mask | SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME;
- return kdbus_translate_attach_flags(bus->creds_mask, &bus->creds_mask);
+ return kdbus_translate_attach_flags(bus->creds_mask, &bus->attach_flags);
}
_public_ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) {
if (e) {
if (streq(e, "system"))
return sd_bus_open_system(ret);
- else if (streq(e, "session") || streq(e, "user"))
+ else if (STR_IN_SET(e, "session", "user"))
return sd_bus_open_user(ret);
}
return r;
}
-_public_ int sd_bus_open_system(sd_bus **ret) {
+int bus_set_address_system(sd_bus *b) {
const char *e;
+ assert(b);
+
+ e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS");
+ if (e)
+ return sd_bus_set_address(b, e);
+
+ return sd_bus_set_address(b, DEFAULT_SYSTEM_BUS_PATH);
+}
+
+_public_ int sd_bus_open_system(sd_bus **ret) {
sd_bus *b;
int r;
if (r < 0)
return r;
- e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS");
- if (e)
- r = sd_bus_set_address(b, e);
- else
- r = sd_bus_set_address(b, DEFAULT_SYSTEM_BUS_PATH);
+ r = bus_set_address_system(b);
if (r < 0)
goto fail;
return r;
}
-_public_ int sd_bus_open_user(sd_bus **ret) {
+int bus_set_address_user(sd_bus *b) {
const char *e;
- sd_bus *b;
- int r;
- assert_return(ret, -EINVAL);
-
- r = sd_bus_new(&b);
- if (r < 0)
- return r;
+ assert(b);
e = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
+ if (e)
+ return sd_bus_set_address(b, e);
+
+ e = secure_getenv("XDG_RUNTIME_DIR");
if (e) {
- r = sd_bus_set_address(b, e);
- if (r < 0)
- goto fail;
- } else {
- e = secure_getenv("XDG_RUNTIME_DIR");
- if (e) {
- _cleanup_free_ char *ee = NULL;
+ _cleanup_free_ char *ee = NULL;
- ee = bus_address_escape(e);
- if (!ee) {
- r = -ENOMEM;
- goto fail;
- }
+ ee = bus_address_escape(e);
+ if (!ee)
+ return -ENOMEM;
#ifdef ENABLE_KDBUS
- asprintf(&b->address, KERNEL_USER_BUS_FMT ";" UNIX_USER_BUS_FMT, (unsigned long) getuid(), ee);
+ asprintf(&b->address, KERNEL_USER_BUS_FMT ";" UNIX_USER_BUS_FMT, (unsigned long) getuid(), ee);
#else
- asprintf(&b->address, UNIX_USER_BUS_FMT, ee);
+ asprintf(&b->address, UNIX_USER_BUS_FMT, ee);
#endif
- } else {
+ } else {
#ifdef ENABLE_KDBUS
- asprintf(&b->address, KERNEL_USER_BUS_FMT, (unsigned long) getuid());
+ asprintf(&b->address, KERNEL_USER_BUS_FMT, (unsigned long) getuid());
#else
- r = -ECONNREFUSED;
- goto fail;
+ return -ECONNREFUSED;
#endif
- }
-
- if (!b->address) {
- r = -ENOMEM;
- goto fail;
- }
}
+ if (!b->address)
+ return -ENOMEM;
+
+ return 0;
+}
+
+_public_ int sd_bus_open_user(sd_bus **ret) {
+ sd_bus *b;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ r = sd_bus_new(&b);
+ if (r < 0)
+ return r;
+
+ r = bus_set_address_user(b);
+ if (r < 0)
+ return r;
+
b->bus_client = true;
b->is_user = true;
return r;
}
-_public_ int sd_bus_open_system_remote(sd_bus **ret, const char *host) {
+int bus_set_address_system_remote(sd_bus *b, const char *host) {
_cleanup_free_ char *e = NULL;
- char *p = NULL;
- sd_bus *bus;
- int r;
- assert_return(host, -EINVAL);
- assert_return(ret, -EINVAL);
+ assert(b);
+ assert(host);
e = bus_address_escape(host);
if (!e)
return -ENOMEM;
- p = strjoin("unixexec:path=ssh,argv1=-xT,argv2=", e, ",argv3=systemd-stdio-bridge", NULL);
- if (!p)
+ b->address = strjoin("unixexec:path=ssh,argv1=-xT,argv2=", e, ",argv3=systemd-stdio-bridge", NULL);
+ if (!b->address)
return -ENOMEM;
+ return 0;
+ }
+
+_public_ int sd_bus_open_system_remote(sd_bus **ret, const char *host) {
+ sd_bus *bus;
+ int r;
+
+ assert_return(host, -EINVAL);
+ assert_return(ret, -EINVAL);
+
r = sd_bus_new(&bus);
- if (r < 0) {
- free(p);
+ if (r < 0)
return r;
- }
- bus->address = p;
+ r = bus_set_address_system_remote(bus, host);
+ if (r < 0)
+ goto fail;
+
bus->bus_client = true;
+ bus->trusted = false;
r = sd_bus_start(bus);
- if (r < 0) {
- bus_free(bus);
- return r;
- }
+ if (r < 0)
+ goto fail;
*ret = bus;
return 0;
+
+fail:
+ bus_free(bus);
+ return r;
}
-_public_ int sd_bus_open_system_container(sd_bus **ret, const char *machine) {
+int bus_set_address_system_container(sd_bus *b, const char *machine) {
_cleanup_free_ char *e = NULL;
- sd_bus *bus;
- char *p;
- int r;
- assert_return(machine, -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(filename_is_safe(machine), -EINVAL);
+ assert(b);
+ assert(machine);
e = bus_address_escape(machine);
if (!e)
return -ENOMEM;
#ifdef ENABLE_KDBUS
- p = strjoin("x-container-kernel:machine=", e, ";x-container-unix:machine=", e, NULL);
+ b->address = strjoin("x-container-kernel:machine=", e, ";x-container-unix:machine=", e, NULL);
#else
- p = strjoin("x-container-unix:machine=", e, NULL);
+ b->address = strjoin("x-container-unix:machine=", e, NULL);
#endif
- if (!p)
+ if (!b->address)
return -ENOMEM;
+ return 0;
+}
+
+_public_ int sd_bus_open_system_container(sd_bus **ret, const char *machine) {
+ sd_bus *bus;
+ int r;
+
+ assert_return(machine, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(filename_is_safe(machine), -EINVAL);
+
r = sd_bus_new(&bus);
- if (r < 0) {
- free(p);
+ if (r < 0)
return r;
- }
- bus->address = p;
+ r = bus_set_address_system_container(bus, machine);
+ if (r < 0)
+ goto fail;
+
bus->bus_client = true;
+ bus->trusted = false;
r = sd_bus_start(bus);
- if (r < 0) {
- bus_free(bus);
- return r;
- }
+ if (r < 0)
+ goto fail;
*ret = bus;
return 0;
+
+fail:
+ bus_free(bus);
+ return r;
}
_public_ void sd_bus_close(sd_bus *bus) {
if (!bus)
return NULL;
+ /* TODO/FIXME: It's naive to think REFCNT_GET() is thread-safe in any
+ * way but exclusive REFCNT_DEC(). The current logic _must_ lock around
+ * REFCNT_GET() until REFCNT_DEC() or two threads might end up in
+ * parallel in bus_reset_queues(). But locking would totally break the
+ * recursion we introduce by bus_reset_queues()...
+ * (Imagine one thread in sd_bus_message_unref() setting n_ref to 0 and
+ * thus calling into sd_bus_unref(). If at the same time the real
+ * thread calls sd_bus_unref(), both end up with "q == true" and will
+ * call into bus_reset_queues().
+ * If we require the main bus to be alive until all dispatch threads
+ * are done, there is no need to do ref-counts at all. So in both ways,
+ * the REFCNT thing is humbug.)
+ *
+ * On a second note: messages are *not* required to have ->bus set nor
+ * does it have to be _this_ bus that they're assigned to. This whole
+ * ref-cnt checking breaks apart if a message is not assigned to us.
+ * (which is _very_ easy to trigger with the current API). */
+
if (REFCNT_GET(bus->n_ref) == bus->rqueue_size + bus->wqueue_size + 1) {
bool q = true;
/* We are the only holders on the messages, and the
* messages are the only holders on us, so let's drop
* the messages and thus implicitly also kill our own
- * last references */
+ * last references.
+ * bus_reset_queues() decrements the queue-size before
+ * calling into sd_bus_message_unref(). Thus, it
+ * protects us from recursion. */
if (q)
bus_reset_queues(bus);
assert_return(bus->state != BUS_UNSET, -ENOTCONN);
assert_return(!bus_pid_changed(bus), -ECHILD);
+ if (bus->hello_flags & KDBUS_HELLO_MONITOR)
+ return 0;
+
if (type == SD_BUS_TYPE_UNIX_FD) {
if (!(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD))
return 0;
int r;
assert_return(bus, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
assert_return(m, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS);
+
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
if (m->n_fds > 0) {
r = sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD);
int r;
assert_return(bus, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
assert_return(m, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
+
if (!streq_ptr(m->destination, destination)) {
if (!destination)
int r;
assert_return(bus, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
assert_return(m, -EINVAL);
assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL);
assert_return(callback, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS);
+
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
r = hashmap_ensure_allocated(&bus->reply_callbacks, uint64_hash_func, uint64_compare_func);
if (r < 0)
int r;
assert_return(bus, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
assert_return(m, -EINVAL);
assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL);
assert_return(!bus_error_is_dirty(error), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS);
+
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
r = bus_ensure_running(bus);
if (r < 0)
if (incoming->header->type == SD_BUS_MESSAGE_METHOD_RETURN) {
- if (reply)
- *reply = incoming;
- else
- sd_bus_message_unref(incoming);
+ if (incoming->n_fds <= 0 || (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) {
+ if (reply)
+ *reply = incoming;
+ else
+ sd_bus_message_unref(incoming);
+
+ return 1;
+ }
+
+ r = sd_bus_error_setf(error, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptors which I couldn't accept. Sorry.");
- return 1;
} else if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR)
r = sd_bus_error_copy(error, &incoming->error);
else
int flags = 0;
assert_return(bus, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state) || bus->state == BUS_CLOSING, -ENOTCONN);
assert_return(!bus_pid_changed(bus), -ECHILD);
+ if (!BUS_IS_OPEN(bus->state) && bus->state != BUS_CLOSING)
+ return -ENOTCONN;
+
if (bus->state == BUS_OPENING)
flags |= POLLOUT;
else if (bus->state == BUS_AUTHENTICATING) {
assert_return(bus, -EINVAL);
assert_return(timeout_usec, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state) || bus->state == BUS_CLOSING, -ENOTCONN);
assert_return(!bus_pid_changed(bus), -ECHILD);
+ if (!BUS_IS_OPEN(bus->state) && bus->state != BUS_CLOSING)
+ return -ENOTCONN;
+
if (bus->track_queue) {
*timeout_usec = 0;
return 1;
}
static int process_reply(sd_bus *bus, sd_bus_message *m) {
+ _cleanup_bus_message_unref_ sd_bus_message *synthetic_reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
- struct reply_callback *c;
+ _cleanup_free_ struct reply_callback *c = NULL;
int r;
assert(bus);
m->header->type != SD_BUS_MESSAGE_METHOD_ERROR)
return 0;
+ if (bus->is_kernel && (bus->hello_flags & KDBUS_HELLO_MONITOR))
+ return 0;
+
+ if (m->destination && bus->unique_name && !streq_ptr(m->destination, bus->unique_name))
+ return 0;
+
c = hashmap_remove(bus->reply_callbacks, &m->reply_cookie);
if (!c)
return 0;
if (c->timeout != 0)
prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
+ if (m->n_fds > 0 && !(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) {
+
+ /* If the reply contained a file descriptor which we
+ * didn't want we pass an error instead. */
+
+ r = bus_message_new_synthetic_error(
+ bus,
+ m->reply_cookie,
+ &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptor"),
+ &synthetic_reply);
+ if (r < 0)
+ return r;
+
+ r = bus_seal_synthetic_message(bus, synthetic_reply);
+ if (r < 0)
+ return r;
+
+ m = synthetic_reply;
+ } else {
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+ }
r = c->callback(bus, m, c->userdata, &error_buffer);
r = bus_maybe_reply_error(m, r, &error_buffer);
- free(c);
return r;
}
assert(bus);
assert(m);
+ if (bus->hello_flags & KDBUS_HELLO_MONITOR)
+ return 0;
+
if (bus->manual_peer_interface)
return 0;
return 1;
}
+static int process_fd_check(sd_bus *bus, sd_bus_message *m) {
+ assert(bus);
+ assert(m);
+
+ /* If we got a message with a file descriptor which we didn't
+ * want to accept, then let's drop it. How can this even
+ * happen? For example, when the kernel queues a message into
+ * an activatable names's queue which allows fds, and then is
+ * delivered to us later even though we ourselves did not
+ * negotiate it. */
+
+ if (bus->hello_flags & KDBUS_HELLO_MONITOR)
+ return 0;
+
+ if (m->n_fds <= 0)
+ return 0;
+
+ if (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)
+ return 0;
+
+ if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
+ return 1; /* just eat it up */
+
+ return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Message contains file descriptors, which I cannot accept. Sorry.");
+}
+
static int process_message(sd_bus *bus, sd_bus_message *m) {
int r;
if (r != 0)
goto finish;
+ r = process_fd_check(bus, m);
+ if (r != 0)
+ goto finish;
+
r = process_filter(bus, m);
if (r != 0)
goto finish;
if (bus->state == BUS_CLOSING)
return 1;
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
e = sd_bus_get_events(bus);
if (e < 0)
if (bus->state == BUS_CLOSING)
return 0;
- assert_return(BUS_IS_OPEN(bus->state) , -ENOTCONN);
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
if (bus->rqueue_size > 0)
return 0;
if (bus->state == BUS_CLOSING)
return 0;
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
r = bus_ensure_running(bus);
if (r < 0)
bus->event_priority = priority;
- r = sd_event_add_monotonic(bus->event, &bus->time_event_source, 0, 0, time_callback, bus);
+ r = sd_event_add_time(bus->event, &bus->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, bus);
if (r < 0)
goto fail;
if (e) {
if (streq(e, "system"))
return sd_bus_default_system(ret);
- else if (streq(e, "user") || streq(e, "session"))
+ else if (STR_IN_SET(e, "user", "session"))
return sd_bus_default_user(ret);
}
assert_return(bus, -EINVAL);
assert_return(mask <= _SD_BUS_CREDS_ALL, -ENOTSUP);
assert_return(ret, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
assert_return(!bus_pid_changed(bus), -ECHILD);
- assert_return(!bus->is_kernel, -ENOTSUP);
+
+ if (bus->is_kernel)
+ return -ENOTSUP;
+
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
if (!bus->ucred_valid && !isempty(bus->label))
return -ENODATA;
int r;
assert_return(bus, -EINVAL);
- assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
assert_return(!bus_pid_changed(bus), -ECHILD);
- assert_return(bus->is_kernel, -ENOTSUP);
+
+ if (!bus->is_kernel)
+ return -ENOTSUP;
+
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
if (bus->rqueue_size > 0)
return -EBUSY;