/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
#include <endian.h>
#include <netdb.h>
b->wqueue_allocated = 0;
}
-static void bus_free(sd_bus *b) {
+static sd_bus* bus_free(sd_bus *b) {
sd_bus_slot *s;
assert(b);
* apps, but are dead. */
assert(s->floating);
- bus_slot_disconnect(s);
- sd_bus_slot_unref(s);
+ bus_slot_disconnect(s, true);
}
if (b->default_bus_ptr)
assert_se(pthread_mutex_destroy(&b->memfd_cache_mutex) == 0);
- free(b);
+ return mfree(b);
}
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus*, bus_free);
+
_public_ int sd_bus_new(sd_bus **ret) {
- sd_bus *r;
+ _cleanup_free_ sd_bus *b = NULL;
assert_return(ret, -EINVAL);
- r = new0(sd_bus, 1);
- if (!r)
+ b = new0(sd_bus, 1);
+ if (!b)
return -ENOMEM;
- r->n_ref = REFCNT_INIT;
- r->input_fd = r->output_fd = -1;
- r->inotify_fd = -1;
- r->message_version = 1;
- r->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME;
- r->accept_fd = true;
- r->original_pid = getpid_cached();
- r->n_groups = (size_t) -1;
-
- assert_se(pthread_mutex_init(&r->memfd_cache_mutex, NULL) == 0);
-
- /* We guarantee that wqueue always has space for at least one
- * entry */
- if (!GREEDY_REALLOC(r->wqueue, r->wqueue_allocated, 1)) {
- free(r);
+ b->n_ref = REFCNT_INIT;
+ b->input_fd = b->output_fd = -1;
+ b->inotify_fd = -1;
+ b->message_version = 1;
+ b->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME;
+ b->accept_fd = true;
+ b->original_pid = getpid_cached();
+ b->n_groups = (size_t) -1;
+
+ assert_se(pthread_mutex_init(&b->memfd_cache_mutex, NULL) == 0);
+
+ /* We guarantee that wqueue always has space for at least one entry */
+ if (!GREEDY_REALLOC(b->wqueue, b->wqueue_allocated, 1))
return -ENOMEM;
- }
- *ret = r;
+ *ret = TAKE_PTR(b);
return 0;
}
_public_ int sd_bus_set_address(sd_bus *bus, const char *address) {
- char *a;
-
assert_return(bus, -EINVAL);
assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(address, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
- a = strdup(address);
- if (!a)
- return -ENOMEM;
-
- free_and_replace(bus->address, a);
-
- return 0;
+ return free_and_strdup(&bus->address, address);
}
_public_ int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) {
}
_public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) {
- char *p, **a;
+ _cleanup_strv_free_ char **a = NULL;
+ int r;
assert_return(bus, -EINVAL);
assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!strv_isempty(argv), -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
- p = strdup(path);
- if (!p)
- return -ENOMEM;
-
a = strv_copy(argv);
- if (!a) {
- free(p);
+ if (!a)
return -ENOMEM;
- }
-
- free_and_replace(bus->exec_path, p);
- strv_free(bus->exec_argv);
- bus->exec_argv = a;
+ r = free_and_strdup(&bus->exec_path, path);
+ if (r < 0)
+ return r;
- return 0;
+ return strv_free_and_replace(bus->exec_argv, a);
}
_public_ int sd_bus_set_bus_client(sd_bus *bus, int b) {
/* Insert at the very front */
memmove(bus->rqueue + 1, bus->rqueue, sizeof(sd_bus_message*) * bus->rqueue_size);
- bus->rqueue[0] = m;
- m = NULL;
+ bus->rqueue[0] = TAKE_PTR(m);
bus->rqueue_size++;
return 0;
static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
const char *s;
- char *t;
sd_bus *bus;
int r;
if (!service_name_is_valid(s) || s[0] != ':')
return -EBADMSG;
- t = strdup(s);
- if (!t)
- return -ENOMEM;
-
- free_and_replace(bus->unique_name, t);
+ r = free_and_strdup(&bus->unique_name, s);
+ if (r < 0)
+ return r;
if (bus->state == BUS_HELLO) {
bus_set_state(bus, BUS_RUNNING);
return -EINVAL;
if (machine) {
- if (!machine_name_is_valid(machine))
+ if (!streq(machine, ".host") && !machine_name_is_valid(machine))
return -EINVAL;
free_and_replace(b->machine, machine);
return bus_send_hello(bus);
}
-_public_ int sd_bus_open(sd_bus **ret) {
+_public_ int sd_bus_open_with_description(sd_bus **ret, const char *description) {
const char *e;
- sd_bus *b;
+ _cleanup_(bus_freep) sd_bus *b = NULL;
int r;
assert_return(ret, -EINVAL);
e = secure_getenv("DBUS_STARTER_BUS_TYPE");
if (e) {
if (streq(e, "system"))
- return sd_bus_open_system(ret);
+ return sd_bus_open_system_with_description(ret, description);
else if (STR_IN_SET(e, "session", "user"))
- return sd_bus_open_user(ret);
+ return sd_bus_open_user_with_description(ret, description);
}
e = secure_getenv("DBUS_STARTER_ADDRESS");
if (!e) {
if (cg_pid_get_owner_uid(0, NULL) >= 0)
- return sd_bus_open_user(ret);
+ return sd_bus_open_user_with_description(ret, description);
else
- return sd_bus_open_system(ret);
+ return sd_bus_open_system_with_description(ret, description);
}
r = sd_bus_new(&b);
r = sd_bus_set_address(b, e);
if (r < 0)
- goto fail;
+ return r;
b->bus_client = true;
r = sd_bus_start(b);
if (r < 0)
- goto fail;
+ return r;
- *ret = b;
+ *ret = TAKE_PTR(b);
return 0;
+}
-fail:
- bus_free(b);
- return r;
+_public_ int sd_bus_open(sd_bus **ret) {
+ return sd_bus_open_with_description(ret, NULL);
}
int bus_set_address_system(sd_bus *b) {
return sd_bus_set_address(b, DEFAULT_SYSTEM_BUS_ADDRESS);
}
-_public_ int sd_bus_open_system(sd_bus **ret) {
- sd_bus *b;
+_public_ int sd_bus_open_system_with_description(sd_bus **ret, const char *description) {
+ _cleanup_(bus_freep) sd_bus *b = NULL;
int r;
assert_return(ret, -EINVAL);
if (r < 0)
return r;
+ if (description) {
+ r = sd_bus_set_description(b, description);
+ if (r < 0)
+ return r;
+ }
+
r = bus_set_address_system(b);
if (r < 0)
- goto fail;
+ return r;
b->bus_client = true;
b->is_system = true;
r = sd_bus_start(b);
if (r < 0)
- goto fail;
+ return r;
- *ret = b;
+ *ret = TAKE_PTR(b);
return 0;
+}
-fail:
- bus_free(b);
- return r;
+_public_ int sd_bus_open_system(sd_bus **ret) {
+ return sd_bus_open_system_with_description(ret, NULL);
}
int bus_set_address_user(sd_bus *b) {
return 0;
}
-_public_ int sd_bus_open_user(sd_bus **ret) {
- sd_bus *b;
+_public_ int sd_bus_open_user_with_description(sd_bus **ret, const char *description) {
+ _cleanup_(bus_freep) sd_bus *b = NULL;
int r;
assert_return(ret, -EINVAL);
if (r < 0)
return r;
+ if (description) {
+ r = sd_bus_set_description(b, description);
+ if (r < 0)
+ return r;
+ }
+
r = bus_set_address_user(b);
if (r < 0)
- goto fail;
+ return r;
b->bus_client = true;
b->is_user = true;
r = sd_bus_start(b);
if (r < 0)
- goto fail;
+ return r;
- *ret = b;
+ *ret = TAKE_PTR(b);
return 0;
+}
-fail:
- bus_free(b);
- return r;
+_public_ int sd_bus_open_user(sd_bus **ret) {
+ return sd_bus_open_user_with_description(ret, NULL);
}
int bus_set_address_system_remote(sd_bus *b, const char *host) {
_cleanup_free_ char *e = NULL;
- char *m = NULL, *c = NULL, *a;
+ char *m = NULL, *c = NULL, *a, *rbracket = NULL, *p = NULL;
assert(b);
assert(host);
- /* Let's see if we shall enter some container */
- m = strchr(host, ':');
- if (m) {
- m++;
-
- /* Let's make sure this is not a port of some kind,
- * and is a valid machine name. */
- if (!in_charset(m, "0123456789") && machine_name_is_valid(m)) {
- char *t;
+ /* Skip ":"s in ipv6 addresses */
+ if (*host == '[') {
+ char *t;
- /* Cut out the host part */
- t = strndupa(host, m - host - 1);
+ rbracket = strchr(host, ']');
+ if (!rbracket)
+ return -EINVAL;
+ t = strndupa(host + 1, rbracket - host - 1);
+ e = bus_address_escape(t);
+ if (!e)
+ return -ENOMEM;
+ } else if ((a = strchr(host, '@'))) {
+ if (*(a + 1) == '[') {
+ _cleanup_free_ char *t = NULL;
+
+ rbracket = strchr(a + 1, ']');
+ if (!rbracket)
+ return -EINVAL;
+ t = new0(char, strlen(host));
+ if (!t)
+ return -ENOMEM;
+ strncat(t, host, a - host + 1);
+ strncat(t, a + 2, rbracket - a - 2);
e = bus_address_escape(t);
if (!e)
return -ENOMEM;
+ } else if (*(a + 1) == '\0' || strchr(a + 1, '@'))
+ return -EINVAL;
+ }
+
+ /* Let's see if a port was given */
+ m = strchr(rbracket ? rbracket + 1 : host, ':');
+ if (m) {
+ char *t;
+ bool got_forward_slash = false;
- c = strjoina(",argv5=--machine=", m);
+ p = m + 1;
+
+ t = strchr(p, '/');
+ if (t) {
+ p = strndupa(p, t - p);
+ got_forward_slash = true;
+ }
+
+ if (!in_charset(p, "0123456789") || *p == '\0') {
+ if (!machine_name_is_valid(p) || got_forward_slash)
+ return -EINVAL;
+ else {
+ m = p;
+ p = NULL;
+ goto interpret_port_as_machine_old_syntax;
+ }
}
}
+ /* Let's see if a machine was given */
+ m = strchr(rbracket ? rbracket + 1 : host, '/');
+ if (m) {
+ m++;
+interpret_port_as_machine_old_syntax:
+ /* Let's make sure this is not a port of some kind,
+ * and is a valid machine name. */
+ if (!in_charset(m, "0123456789") && machine_name_is_valid(m))
+ c = strjoina(",argv", p ? "7" : "5", "=--machine=", m);
+ }
+
if (!e) {
- e = bus_address_escape(host);
+ char *t;
+
+ t = strndupa(host, strcspn(host, ":/"));
+
+ e = bus_address_escape(t);
if (!e)
return -ENOMEM;
}
- a = strjoin("unixexec:path=ssh,argv1=-xT,argv2=--,argv3=", e, ",argv4=systemd-stdio-bridge", c);
+ a = strjoin("unixexec:path=ssh,argv1=-xT", p ? ",argv2=-p,argv3=" : "", strempty(p),
+ ",argv", p ? "4" : "2", "=--,argv", p ? "5" : "3", "=", e,
+ ",argv", p ? "6" : "4", "=systemd-stdio-bridge", c);
if (!a)
return -ENOMEM;
- free_and_replace(b->address, a);
-
- return 0;
- }
+ return free_and_replace(b->address, a);
+}
_public_ int sd_bus_open_system_remote(sd_bus **ret, const char *host) {
- sd_bus *bus;
+ _cleanup_(bus_freep) sd_bus *b = NULL;
int r;
assert_return(host, -EINVAL);
assert_return(ret, -EINVAL);
- r = sd_bus_new(&bus);
+ r = sd_bus_new(&b);
if (r < 0)
return r;
- r = bus_set_address_system_remote(bus, host);
+ r = bus_set_address_system_remote(b, host);
if (r < 0)
- goto fail;
+ return r;
- bus->bus_client = true;
- bus->trusted = false;
- bus->is_system = true;
- bus->is_local = false;
+ b->bus_client = true;
+ b->trusted = false;
+ b->is_system = true;
+ b->is_local = false;
- r = sd_bus_start(bus);
+ r = sd_bus_start(b);
if (r < 0)
- goto fail;
+ return r;
- *ret = bus;
+ *ret = TAKE_PTR(b);
return 0;
-
-fail:
- bus_free(bus);
- return r;
}
int bus_set_address_system_machine(sd_bus *b, const char *machine) {
if (!a)
return -ENOMEM;
- free_and_replace(b->address, a);
-
- return 0;
+ return free_and_replace(b->address, a);
}
_public_ int sd_bus_open_system_machine(sd_bus **ret, const char *machine) {
- sd_bus *bus;
+ _cleanup_(bus_freep) sd_bus *b = NULL;
int r;
assert_return(machine, -EINVAL);
assert_return(ret, -EINVAL);
- assert_return(machine_name_is_valid(machine), -EINVAL);
+ assert_return(streq(machine, ".host") || machine_name_is_valid(machine), -EINVAL);
- r = sd_bus_new(&bus);
+ r = sd_bus_new(&b);
if (r < 0)
return r;
- r = bus_set_address_system_machine(bus, machine);
+ r = bus_set_address_system_machine(b, machine);
if (r < 0)
- goto fail;
+ return r;
- bus->bus_client = true;
- bus->trusted = false;
- bus->is_system = true;
- bus->is_local = false;
+ b->bus_client = true;
+ b->trusted = false;
+ b->is_system = true;
+ b->is_local = false;
- r = sd_bus_start(bus);
+ r = sd_bus_start(b);
if (r < 0)
- goto fail;
+ return r;
- *ret = bus;
+ *ret = TAKE_PTR(b);
return 0;
-
-fail:
- bus_free(bus);
- return r;
}
_public_ void sd_bus_close(sd_bus *bus) {
-
if (!bus)
return;
if (bus->state == BUS_CLOSED)
}
_public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) {
-
if (!bus)
return NULL;
bus_set_state(bus, BUS_CLOSING);
}
-_public_ sd_bus *sd_bus_ref(sd_bus *bus) {
-
- if (!bus)
- return NULL;
-
- assert_se(REFCNT_INC(bus->n_ref) >= 2);
-
- return bus;
-}
-
-_public_ sd_bus *sd_bus_unref(sd_bus *bus) {
- unsigned i;
-
- if (!bus)
- return NULL;
-
- i = REFCNT_DEC(bus->n_ref);
- if (i > 0)
- return NULL;
-
- bus_free(bus);
- return NULL;
-}
+DEFINE_PUBLIC_ATOMIC_REF_UNREF_FUNC(sd_bus, sd_bus, bus_free);
_public_ int sd_bus_is_open(sd_bus *bus) {
-
assert_return(bus, -EINVAL);
assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return 0;
}
- if (timeout == 0)
- timeout = BUS_DEFAULT_TIMEOUT;
+ if (timeout == 0) {
+ r = sd_bus_get_method_call_timeout(b, &timeout);
+ if (r < 0)
+ return r;
+ }
if (!m->sender && b->patch_sender) {
r = sd_bus_message_set_sender(m, b->patch_sender);
if (x->timeout_usec == 0 && y->timeout_usec != 0)
return 1;
- if (x->timeout_usec < y->timeout_usec)
- return -1;
-
- if (x->timeout_usec > y->timeout_usec)
- return 1;
-
- return 0;
+ return CMP(x->timeout_usec, y->timeout_usec);
}
_public_ int sd_bus_call_async(
bus->current_slot = NULL;
bus->current_message = NULL;
- if (slot->floating) {
- bus_slot_disconnect(slot);
- sd_bus_slot_unref(slot);
- }
+ if (slot->floating)
+ bus_slot_disconnect(slot, true);
sd_bus_slot_unref(slot);
bus->current_handler = NULL;
bus->current_slot = NULL;
- if (slot->floating) {
- bus_slot_disconnect(slot);
- sd_bus_slot_unref(slot);
- }
+ if (slot->floating)
+ bus_slot_disconnect(slot, true);
sd_bus_slot_unref(slot);
if (r < 0)
return r;
- *ret = m;
- m = NULL;
+ *ret = TAKE_PTR(m);
+
return 1;
}
bus->current_slot = NULL;
bus->current_message = NULL;
- if (slot->floating) {
- bus_slot_disconnect(slot);
- sd_bus_slot_unref(slot);
- }
+ if (slot->floating)
+ bus_slot_disconnect(slot, true);
sd_bus_slot_unref(slot);
bus->exit_triggered = true;
(void) bus_exit_now(bus);
- if (ret) {
- *ret = m;
- m = NULL;
- }
+ if (ret)
+ *ret = TAKE_PTR(m);
r = 1;
}
static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) {
- BUS_DONT_DESTROY(bus);
int r;
/* Returns 0 when we didn't do anything. This should cause the
/* We don't allow recursively invoking sd_bus_process(). */
assert_return(!bus->current_message, -EBUSY);
- assert(!bus->current_slot);
+ assert(!bus->current_slot); /* This should be NULL whenever bus->current_message is */
+
+ BUS_DONT_DESTROY(bus);
switch (bus->state) {
r = 1;
}
- if (failed && match_slot->floating) {
- bus_slot_disconnect(match_slot);
- sd_bus_slot_unref(match_slot);
- }
+ if (failed && match_slot->floating)
+ bus_slot_disconnect(match_slot, true);
sd_bus_slot_unref(match_slot);
goto finish;
}
- if (asynchronous)
+ if (asynchronous) {
r = bus_add_match_internal_async(bus,
&s->match_callback.install_slot,
s->match_callback.match_string,
add_match_callback,
s);
- else
+
+ if (r < 0)
+ return r;
+
+ /* Make the slot of the match call floating now. We need the reference, but we don't
+ * want that this match pins the bus object, hence we first create it non-floating, but
+ * then make it floating. */
+ r = sd_bus_slot_set_floating(s->match_callback.install_slot, true);
+ } else
r = bus_add_match_internal(bus, s->match_callback.match_string);
if (r < 0)
goto finish;
return bus_default(sd_bus_open_system, &default_system_bus, ret);
}
-
_public_ int sd_bus_default_user(sd_bus **ret) {
return bus_default(sd_bus_open_user, &default_user_bus, ret);
}
*ret = bus->wqueue_size;
return 0;
}
+
+_public_ int sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+
+ bus->method_call_timeout = usec;
+ return 0;
+}
+
+_public_ int sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret) {
+ const char *e;
+ usec_t usec;
+
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(ret, -EINVAL);
+
+ if (bus->method_call_timeout != 0) {
+ *ret = bus->method_call_timeout;
+ return 0;
+ }
+
+ e = secure_getenv("SYSTEMD_BUS_TIMEOUT");
+ if (e && parse_sec(e, &usec) >= 0 && usec != 0) {
+ /* Save the parsed value to avoid multiple parsing. To change the timeout value,
+ * use sd_bus_set_method_call_timeout() instead of setenv(). */
+ *ret = bus->method_call_timeout = usec;
+ return 0;
+ }
+
+ *ret = bus->method_call_timeout = BUS_DEFAULT_TIMEOUT;
+ return 0;
+}