From e3dd987cfc395848256fc6eae637ed0eaf5f1635 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 30 Nov 2013 03:53:42 +0100 Subject: [PATCH] core: allocate a kdbus bus for each systemd instance, if we can --- src/core/main.c | 2 +- src/core/manager.c | 95 ++++++++++++++++----- src/core/manager.h | 5 +- src/libsystemd-bus/bus-kernel.c | 177 ++++++++++++++++++++-------------------- src/libsystemd-bus/kdbus.h | 5 -- src/libsystemd-bus/sd-bus.c | 40 ++++----- src/test/test-cgroup-mask.c | 2 +- src/test/test-engine.c | 2 +- src/test/test-sched-prio.c | 2 +- src/test/test-unit-name.c | 2 +- 10 files changed, 194 insertions(+), 138 deletions(-) diff --git a/src/core/main.c b/src/core/main.c index 69d3a43..6c3d9bf 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1531,7 +1531,7 @@ int main(int argc, char *argv[]) { if (arg_running_as == SYSTEMD_SYSTEM) bump_rlimit_nofile(&saved_rlimit_nofile); - r = manager_new(arg_running_as, !!serialization, &m); + r = manager_new(arg_running_as, &m); if (r < 0) { log_error("Failed to allocate manager object: %s", strerror(-r)); goto finish; diff --git a/src/core/manager.c b/src/core/manager.c index 65cb73c..a168589 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -76,6 +76,7 @@ #include "dbus-unit.h" #include "dbus-job.h" #include "dbus-manager.h" +#include "bus-kernel.h" /* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */ #define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC) @@ -408,10 +409,45 @@ static int manager_default_environment(Manager *m) { return 0; } -int manager_new(SystemdRunningAs running_as, bool reexecuting, Manager **_m) { +static int manager_setup_kdbus(Manager *m) { + _cleanup_free_ char *p = NULL; + + assert(m); + + if (m->kdbus_fd >= 0) + return 0; + + /* If there's already a bus address set, don't set up kdbus */ + if (m->running_as == SYSTEMD_USER && getenv("DBUS_SESSION_BUS_ADDRESS")) + return 0; + + m->kdbus_fd = bus_kernel_create(m->running_as == SYSTEMD_SYSTEM ? "system" : "user", &p); + if (m->kdbus_fd < 0) { + log_debug("Failed to set up kdbus: %s", strerror(-m->kdbus_fd)); + return m->kdbus_fd; + } + + log_info("Successfully set up kdbus on %s", p); + return 0; +} + +static int manager_connect_bus(Manager *m, bool reexecuting) { + bool try_bus_connect; + + assert(m); + + try_bus_connect = + m->kdbus_fd >= 0 || + reexecuting || + (m->running_as == SYSTEMD_USER && getenv("DBUS_SESSION_BUS_ADDRESS")); + + /* Try to connect to the busses, if possible. */ + return bus_init(m, try_bus_connect); +} + +int manager_new(SystemdRunningAs running_as, Manager **_m) { Manager *m; - int r = -ENOMEM; - bool try_bus_connect = false; + int r; assert(_m); assert(running_as >= 0); @@ -431,7 +467,7 @@ int manager_new(SystemdRunningAs running_as, bool reexecuting, Manager **_m) { m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1; - m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = m->dev_autofs_fd = m->private_listen_fd = -1; + m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = -1; m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ r = manager_default_environment(m); @@ -496,18 +532,6 @@ int manager_new(SystemdRunningAs running_as, bool reexecuting, Manager **_m) { goto fail; } - if (running_as == SYSTEMD_SYSTEM) - try_bus_connect = reexecuting; - else if (getenv("DBUS_SESSION_BUS_ADDRESS")) - try_bus_connect = true; - else - log_debug("Skipping DBus session bus connection attempt - no DBUS_SESSION_BUS_ADDRESS set..."); - - /* Try to connect to the busses, if possible. */ - r = bus_init(m, try_bus_connect); - if (r < 0) - goto fail; - m->taint_usr = dir_is_empty("/usr") > 0; *_m = m; @@ -694,6 +718,8 @@ void manager_free(Manager *m) { close_nointr_nofail(m->notify_fd); if (m->time_change_fd >= 0) close_nointr_nofail(m->time_change_fd); + if (m->kdbus_fd >= 0) + close_nointr_nofail(m->kdbus_fd); manager_close_idle_pipe(m); @@ -889,6 +915,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { r = q; } + /* We might have deserialized the kdbus control fd, but if we + * didn't, then let's create the bus now. */ + manager_setup_kdbus(m); + manager_connect_bus(m, !!serialization); + /* Third, fire things up! */ q = manager_coldplug(m); if (q < 0) @@ -1979,11 +2010,23 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { _cleanup_free_ char *ce; ce = cescape(*e); - if (ce) - fprintf(f, "env=%s\n", *e); + if (!ce) + return -ENOMEM; + + fprintf(f, "env=%s\n", *e); } } + if (m->kdbus_fd >= 0) { + int copy; + + copy = fdset_put_dup(fds, m->kdbus_fd); + if (copy < 0) + return copy; + + fprintf(f, "kdbus-fd=%i\n", copy); + } + bus_serialize(m, f); fputc('\n', f); @@ -2074,7 +2117,8 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { } else if (startswith(l, "taint-usr=")) { int b; - if ((b = parse_boolean(l+10)) < 0) + b = parse_boolean(l+10); + if (b < 0) log_debug("Failed to parse taint /usr flag %s", l+10); else m->taint_usr = m->taint_usr || b; @@ -2121,6 +2165,19 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { strv_free(m->environment); m->environment = e; + + } else if (startswith(l, "kdbus-fd=")) { + int fd; + + if (safe_atoi(l + 9, &fd) < 0 || !fdset_contains(fds, fd)) + log_debug("Failed to parse kdbus fd: %s", l + 9); + else { + if (m->kdbus_fd >= 0) + close_nointr_nofail(m->kdbus_fd); + + m->kdbus_fd = fdset_remove(fds, fd); + } + } else if (bus_deserialize_item(m, l) == 0) log_debug("Unknown serialization item '%s'", l); } diff --git a/src/core/manager.h b/src/core/manager.h index bf05812..fdf60ae 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -234,9 +234,12 @@ struct Manager { * them. It's a hashmap with a path string as key and a Set as * value where Unit objects are contained. */ Hashmap *units_requiring_mounts_for; + + /* Reference to the kdbus bus control fd */ + int kdbus_fd; }; -int manager_new(SystemdRunningAs running_as, bool reexecuting, Manager **m); +int manager_new(SystemdRunningAs running_as, Manager **m); void manager_free(Manager *m); int manager_enumerate(Manager *m); diff --git a/src/libsystemd-bus/bus-kernel.c b/src/libsystemd-bus/bus-kernel.c index 6914343..1cab254 100644 --- a/src/libsystemd-bus/bus-kernel.c +++ b/src/libsystemd-bus/bus-kernel.c @@ -605,39 +605,6 @@ static int bus_kernel_translate_message(sd_bus *bus, struct kdbus_msg *k) { return translate[found->type - _KDBUS_ITEM_KERNEL_BASE](bus, k, found); } -int kdbus_translate_attach_flags(uint64_t mask, uint64_t *kdbus_mask) { - - uint64_t m = 0; - - SET_FLAG(m, KDBUS_ATTACH_CREDS, - !!(mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_TID))); - - SET_FLAG(m, KDBUS_ATTACH_COMM, - !!(mask & (SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM))); - - SET_FLAG(m, KDBUS_ATTACH_EXE, - !!(mask & SD_BUS_CREDS_EXE)); - - SET_FLAG(m, KDBUS_ATTACH_CMDLINE, - !!(mask & SD_BUS_CREDS_CMDLINE)); - - SET_FLAG(m, KDBUS_ATTACH_CGROUP, - !!(mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID))); - - SET_FLAG(m, KDBUS_ATTACH_CAPS, - !!(mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS))); - - SET_FLAG(m, KDBUS_ATTACH_SECLABEL, - !!(mask & SD_BUS_CREDS_SELINUX_CONTEXT)); - - SET_FLAG(m, KDBUS_ATTACH_AUDIT, - !!(mask & (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))); - - *kdbus_mask = m; - - return 0; -} - static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { sd_bus_message *m = NULL; struct kdbus_item *d; @@ -923,52 +890,6 @@ int bus_kernel_read_message(sd_bus *bus) { return r < 0 ? r : 1; } -int bus_kernel_create(const char *name, char **s) { - struct kdbus_cmd_bus_make *make; - struct kdbus_item *n; - size_t l; - int fd; - char *p; - - assert(name); - assert(s); - - fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - - l = strlen(name); - make = alloca0(offsetof(struct kdbus_cmd_bus_make, items) + - KDBUS_PART_HEADER_SIZE + sizeof(uint64_t) + - KDBUS_PART_HEADER_SIZE + DECIMAL_STR_MAX(uid_t) + 1 + l + 1); - - n = make->items; - n->type = KDBUS_MAKE_NAME; - sprintf(n->str, "%lu-%s", (unsigned long) getuid(), name); - n->size = KDBUS_PART_HEADER_SIZE + strlen(n->str) + 1; - - make->size = offsetof(struct kdbus_cmd_bus_make, items) + n->size; - make->flags = KDBUS_MAKE_POLICY_OPEN; - make->bus_flags = 0; - make->bloom_size = BLOOM_SIZE; - assert_cc(BLOOM_SIZE % 8 == 0); - - p = strjoin("/dev/kdbus/", n->str, "/bus", NULL); - if (!p) - return -ENOMEM; - - if (ioctl(fd, KDBUS_CMD_BUS_MAKE, make) < 0) { - close_nointr_nofail(fd); - free(p); - return -errno; - } - - if (s) - *s = p; - - return fd; -} - int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *size) { struct memfd_cache *c; int fd; @@ -1061,20 +982,100 @@ void bus_kernel_flush_memfd(sd_bus *b) { close_and_munmap(b->memfd_cache[i].fd, b->memfd_cache[i].address, b->memfd_cache[i].size); } -int kdbus_translate_request_name_flags(uint64_t sd_bus_flags, uint64_t *kdbus_flags) { +int kdbus_translate_request_name_flags(uint64_t flags, uint64_t *kdbus_flags) { + uint64_t f = 0; - assert_return(kdbus_flags != NULL, -EINVAL); + assert(kdbus_flags); - *kdbus_flags = 0; + if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT) + f |= KDBUS_NAME_ALLOW_REPLACEMENT; - if (sd_bus_flags & SD_BUS_NAME_ALLOW_REPLACEMENT) - *kdbus_flags |= KDBUS_NAME_ALLOW_REPLACEMENT; + if (flags & SD_BUS_NAME_REPLACE_EXISTING) + f |= KDBUS_NAME_REPLACE_EXISTING; - if (sd_bus_flags & SD_BUS_NAME_REPLACE_EXISTING) - *kdbus_flags |= KDBUS_NAME_REPLACE_EXISTING; + if (!(flags & SD_BUS_NAME_DO_NOT_QUEUE)) + f |= KDBUS_NAME_QUEUE; - if (!(sd_bus_flags & SD_BUS_NAME_DO_NOT_QUEUE)) - *kdbus_flags |= KDBUS_NAME_QUEUE; + *kdbus_flags = f; + return 0; +} + +int kdbus_translate_attach_flags(uint64_t mask, uint64_t *kdbus_mask) { + uint64_t m = 0; + + assert(kdbus_mask); + + if (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_TID)) + m |= KDBUS_ATTACH_CREDS; + + if (mask & (SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM)) + m |= KDBUS_ATTACH_COMM; + + if (mask & SD_BUS_CREDS_EXE) + m |= KDBUS_ATTACH_EXE; + + if (mask & SD_BUS_CREDS_CMDLINE) + m |= KDBUS_ATTACH_CMDLINE; + + if (mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) + m |= KDBUS_ATTACH_CGROUP; + + if (mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS)) + m |= KDBUS_ATTACH_CAPS; + + if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) + m |= KDBUS_ATTACH_SECLABEL; + if (mask & (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)) + m |= KDBUS_ATTACH_AUDIT; + + *kdbus_mask = m; return 0; } + +int bus_kernel_create(const char *name, char **s) { + struct kdbus_cmd_bus_make *make; + struct kdbus_item *n; + int fd; + + assert(name); + assert(s); + + fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; + + make = alloca0(ALIGN8(offsetof(struct kdbus_cmd_bus_make, items) + + offsetof(struct kdbus_item, str) + + DECIMAL_STR_MAX(uid_t) + 1 + strlen(name) + 1)); + + n = make->items; + sprintf(n->str, "%lu-%s", (unsigned long) getuid(), name); + n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1; + n->type = KDBUS_MAKE_NAME; + + make->size = ALIGN8(offsetof(struct kdbus_cmd_bus_make, items) + n->size); + make->flags = KDBUS_MAKE_POLICY_OPEN; + make->bus_flags = 0; + make->bloom_size = BLOOM_SIZE; + assert_cc(BLOOM_SIZE % 8 == 0); + + if (ioctl(fd, KDBUS_CMD_BUS_MAKE, make) < 0) { + close_nointr_nofail(fd); + return -errno; + } + + if (s) { + char *p; + + p = strjoin("/dev/kdbus/", n->str, "/bus", NULL); + if (!p) { + close_nointr_nofail(fd); + return -ENOMEM; + } + + *s = p; + } + + return fd; +} diff --git a/src/libsystemd-bus/kdbus.h b/src/libsystemd-bus/kdbus.h index c9377ea..bfd299b 100644 --- a/src/libsystemd-bus/kdbus.h +++ b/src/libsystemd-bus/kdbus.h @@ -297,11 +297,6 @@ enum { enum { _KDBUS_MAKE_NULL, KDBUS_MAKE_NAME, - KDBUS_MAKE_CRED, /* allow translator services which connect - * to the bus on behalf of somebody else, - * allow specifying the credentials of the - * client to connect on behalf on. Needs - * privileges */ }; struct kdbus_cmd_bus_make { diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c index 8ffc9a9..0d5deb6 100644 --- a/src/libsystemd-bus/sd-bus.c +++ b/src/libsystemd-bus/sd-bus.c @@ -986,15 +986,13 @@ _public_ int sd_bus_open_system(sd_bus **ret) { return r; e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS"); - if (e) { + if (e) r = sd_bus_set_address(b, e); - if (r < 0) - goto fail; - } else { - b->sockaddr.un.sun_family = AF_UNIX; - strncpy(b->sockaddr.un.sun_path, "/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path)); - b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/dbus/system_bus_socket") - 1; - } + else + r = sd_bus_set_address(b, "kernel:path=/dev/kdbus/0-system/bus;unix:path=/run/dbus/system_bus_socket"); + + if (r < 0) + goto fail; b->bus_client = true; @@ -1013,7 +1011,6 @@ fail: _public_ int sd_bus_open_user(sd_bus **ret) { const char *e; sd_bus *b; - size_t l; int r; assert_return(ret, -EINVAL); @@ -1029,20 +1026,23 @@ _public_ int sd_bus_open_user(sd_bus **ret) { goto fail; } else { e = secure_getenv("XDG_RUNTIME_DIR"); - if (!e) { - r = -ENOENT; - goto fail; - } + if (e) { + _cleanup_free_ char *ee = NULL; + + ee = bus_address_escape(e); + if (!ee) { + r = -ENOENT; + goto fail; + } - l = strlen(e); - if (l + 4 > sizeof(b->sockaddr.un.sun_path)) { - r = -E2BIG; + asprintf(&b->address, "kernel:path=/dev/kdbus/%lu-user/bus;unix:path=%s/bus", (unsigned long) getuid(), ee); + } else + asprintf(&b->address, "kernel:path=/dev/kdbus/%lu-user/bus", (unsigned long) getuid()); + + if (!b->address) { + r = -ENOMEM; goto fail; } - - b->sockaddr.un.sun_family = AF_UNIX; - memcpy(mempcpy(b->sockaddr.un.sun_path, e, l), "/bus", 4); - b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l + 4; } b->bus_client = true; diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c index ecf041f..0f145d6 100644 --- a/src/test/test-cgroup-mask.c +++ b/src/test/test-cgroup-mask.c @@ -40,7 +40,7 @@ static int test_cgroup_mask(void) { /* Prepare the manager. */ assert_se(set_unit_path(TEST_DIR) >= 0); - r = manager_new(SYSTEMD_USER, false, &m); + r = manager_new(SYSTEMD_USER, &m); if (r == -EPERM || r == -EACCES) { puts("manager_new: Permission denied. Skipping test."); return EXIT_TEST_SKIP; diff --git a/src/test/test-engine.c b/src/test/test-engine.c index 20ae103..0f38622 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -33,7 +33,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path("test") >= 0); - assert_se(manager_new(SYSTEMD_SYSTEM, false, &m) >= 0); + assert_se(manager_new(SYSTEMD_SYSTEM, &m) >= 0); printf("Load1:\n"); assert_se(manager_load_unit(m, "a.service", NULL, NULL, &a) >= 0); diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c index 208fca8..56e11f8 100644 --- a/src/test/test-sched-prio.c +++ b/src/test/test-sched-prio.c @@ -34,7 +34,7 @@ int main(int argc, char *argv[]) { /* prepare the test */ assert_se(set_unit_path(TEST_DIR) >= 0); - r = manager_new(SYSTEMD_USER, false, &m); + r = manager_new(SYSTEMD_USER, &m); if (r == -EPERM || r == -EACCES || r == -EADDRINUSE || r == -EHOSTDOWN) { printf("Skipping test: manager_new: %s", strerror(-r)); return EXIT_TEST_SKIP; diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index bde643a..b6209b1 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -124,7 +124,7 @@ static int test_unit_printf(void) { assert_se((root = getpwnam("root"))); assert_se(asprintf(&root_uid, "%d", (int) root->pw_uid) > 0); - r = manager_new(SYSTEMD_USER, false, &m); + r = manager_new(SYSTEMD_USER, &m); if (r == -EPERM || r == -EACCES || r == -EADDRINUSE) { puts("manager_new: Permission denied. Skipping test."); return EXIT_TEST_SKIP; -- 2.7.4