summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mkosi/mkosi.fedora2
-rw-r--r--TODO11
-rw-r--r--src/basic/missing.h4
-rw-r--r--src/basic/process-util.c64
-rw-r--r--src/basic/user-util.c25
-rw-r--r--src/core/bpf-firewall.c8
-rw-r--r--src/core/cgroup.c172
-rw-r--r--src/core/cgroup.h5
-rw-r--r--src/core/dbus-cgroup.c6
-rw-r--r--src/core/dbus-manager.c128
-rw-r--r--src/core/dbus-scope.c32
-rw-r--r--src/core/dbus-unit.c113
-rw-r--r--src/core/dbus-unit.h1
-rw-r--r--src/core/dbus.c158
-rw-r--r--src/core/dbus.h10
-rw-r--r--src/core/manager.c148
-rw-r--r--src/core/manager.h5
-rw-r--r--src/core/mount.c1
-rw-r--r--src/core/org.freedesktop.systemd1.conf156
-rw-r--r--src/core/scope.c3
-rw-r--r--src/core/service.c5
-rw-r--r--src/core/socket.c1
-rw-r--r--src/core/swap.c1
-rw-r--r--src/core/unit.c87
-rw-r--r--src/core/unit.h5
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c10
26 files changed, 875 insertions, 286 deletions
diff --git a/.mkosi/mkosi.fedora b/.mkosi/mkosi.fedora
index 4d7168c00b..c7505d19f6 100644
--- a/.mkosi/mkosi.fedora
+++ b/.mkosi/mkosi.fedora
@@ -22,7 +22,7 @@
[Distribution]
Distribution=fedora
-Release=26
+Release=27
[Output]
Format=raw_btrfs
diff --git a/TODO b/TODO
index a6cdae84a0..17022d912b 100644
--- a/TODO
+++ b/TODO
@@ -24,8 +24,13 @@ Janitorial Clean-ups:
Features:
-* check what setting the login shell to /bin/false vs. /sbin/nologin means and
- do the right thing in get_user_creds_clean() with it.
+* block setrlimit(RLIMIT_NOPROC) (and other per-user limits) in nspawn when userns is not on
+
+* nss-systemd: implement enumeration, that shows all dynamic users plus the
+ synthesized ones if necessary, so that "getent passwd" shows useful data.
+
+* teach tmpfiles.d q/Q logic something sensible in the context of XFS/ext4
+ project quota
* maybe rework get_user_creds() to query the user database if $SHELL is used
for root, but only then.
@@ -378,8 +383,6 @@ Features:
* what to do about udev db binary stability for apps? (raw access is not an option)
-* maybe provide an API to allow migration of foreign PIDs into existing scopes.
-
* man: maybe use the word "inspect" rather than "introspect"?
* systemctl: if some operation fails, show log output?
diff --git a/src/basic/missing.h b/src/basic/missing.h
index ed884dddad..2f68e3ae8a 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -1368,4 +1368,8 @@ struct fib_rule_uid_range {
#define FALLOC_FL_PUNCH_HOLE 0x02
#endif
+#ifndef PF_KTHREAD
+#define PF_KTHREAD 0x00200000
+#endif
+
#include "missing_syscall.h"
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index 7f8644ea9f..855ac7534a 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -398,37 +398,61 @@ use_saved_argv:
}
int is_kernel_thread(pid_t pid) {
+ _cleanup_free_ char *line = NULL;
+ unsigned long long flags;
+ size_t l, i;
const char *p;
- size_t count;
- char c;
- bool eof;
- FILE *f;
+ char *q;
+ int r;
if (IN_SET(pid, 0, 1) || pid == getpid_cached()) /* pid 1, and we ourselves certainly aren't a kernel thread */
return 0;
+ if (!pid_is_valid(pid))
+ return -EINVAL;
- assert(pid > 1);
+ p = procfs_file_alloca(pid, "stat");
+ r = read_one_line_file(p, &line);
+ if (r == -ENOENT)
+ return -ESRCH;
+ if (r < 0)
+ return r;
- p = procfs_file_alloca(pid, "cmdline");
- f = fopen(p, "re");
- if (!f) {
- if (errno == ENOENT)
- return -ESRCH;
- return -errno;
+ /* Skip past the comm field */
+ q = strrchr(line, ')');
+ if (!q)
+ return -EINVAL;
+ q++;
+
+ /* Skip 6 fields to reach the flags field */
+ for (i = 0; i < 6; i++) {
+ l = strspn(q, WHITESPACE);
+ if (l < 1)
+ return -EINVAL;
+ q += l;
+
+ l = strcspn(q, WHITESPACE);
+ if (l < 1)
+ return -EINVAL;
+ q += l;
}
- (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
- count = fread(&c, 1, 1, f);
- eof = feof(f);
- fclose(f);
+ /* Skip preceeding whitespace */
+ l = strspn(q, WHITESPACE);
+ if (l < 1)
+ return -EINVAL;
+ q += l;
- /* Kernel threads have an empty cmdline */
+ /* Truncate the rest */
+ l = strcspn(q, WHITESPACE);
+ if (l < 1)
+ return -EINVAL;
+ q[l] = 0;
- if (count <= 0)
- return eof ? 1 : -errno;
+ r = safe_atollu(q, &flags);
+ if (r < 0)
+ return r;
- return 0;
+ return !!(flags & PF_KTHREAD);
}
int get_process_capeff(pid_t pid, char **capeff) {
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index db18ee31c0..ceb71b61e8 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -197,6 +197,25 @@ int get_user_creds(
return 0;
}
+static inline bool is_nologin_shell(const char *shell) {
+
+ return PATH_IN_SET(shell,
+ /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice
+ * message and exits. Different distributions place the binary at different places though,
+ * hence let's list them all. */
+ "/bin/nologin",
+ "/sbin/nologin",
+ "/usr/bin/nologin",
+ "/usr/sbin/nologin",
+ /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do
+ * any message printing. Different distributions place the binary at various places but at
+ * least not in the 'sbin' directory. */
+ "/bin/false",
+ "/usr/bin/false",
+ "/bin/true",
+ "/usr/bin/true");
+}
+
int get_user_creds_clean(
const char **username,
uid_t *uid, gid_t *gid,
@@ -212,11 +231,7 @@ int get_user_creds_clean(
return r;
if (shell &&
- (isempty(*shell) || PATH_IN_SET(*shell,
- "/bin/nologin",
- "/sbin/nologin",
- "/usr/bin/nologin",
- "/usr/sbin/nologin")))
+ (isempty(*shell) || is_nologin_shell(*shell)))
*shell = NULL;
if (home &&
diff --git a/src/core/bpf-firewall.c b/src/core/bpf-firewall.c
index f3f40fb0e8..c5cda83006 100644
--- a/src/core/bpf-firewall.c
+++ b/src/core/bpf-firewall.c
@@ -494,7 +494,7 @@ int bpf_firewall_compile(Unit *u) {
if (r < 0)
return r;
if (r == 0) {
- log_debug("BPF firewalling not supported on this systemd, proceeding without.");
+ log_debug("BPF firewalling not supported on this manager, proceeding without.");
return -EOPNOTSUPP;
}
@@ -556,7 +556,7 @@ int bpf_firewall_install(Unit *u) {
if (r < 0)
return r;
if (r == 0) {
- log_debug("BPF firewalling not supported on this systemd, proceeding without.");
+ log_debug("BPF firewalling not supported on this manager, proceeding without.");
return -EOPNOTSUPP;
}
@@ -569,7 +569,7 @@ int bpf_firewall_install(Unit *u) {
if (r < 0)
return log_error_errno(r, "Kernel upload of egress BPF program failed: %m");
- r = bpf_program_cgroup_attach(u->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path, cc->delegate ? BPF_F_ALLOW_OVERRIDE : 0);
+ r = bpf_program_cgroup_attach(u->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path, unit_cgroup_delegate(u) ? BPF_F_ALLOW_OVERRIDE : 0);
if (r < 0)
return log_error_errno(r, "Attaching egress BPF program to cgroup %s failed: %m", path);
} else {
@@ -584,7 +584,7 @@ int bpf_firewall_install(Unit *u) {
if (r < 0)
return log_error_errno(r, "Kernel upload of ingress BPF program failed: %m");
- r = bpf_program_cgroup_attach(u->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path, cc->delegate ? BPF_F_ALLOW_OVERRIDE : 0);
+ r = bpf_program_cgroup_attach(u->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path, unit_cgroup_delegate(u) ? BPF_F_ALLOW_OVERRIDE : 0);
if (r < 0)
return log_error_errno(r, "Attaching ingress BPF program to cgroup %s failed: %m", path);
} else {
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 97b3756567..edb702ce48 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -24,6 +24,7 @@
#include "alloc-util.h"
#include "blockdev-util.h"
#include "bpf-firewall.h"
+#include "bus-error.h"
#include "cgroup-util.h"
#include "cgroup.h"
#include "fd-util.h"
@@ -1124,14 +1125,7 @@ CGroupMask unit_get_delegate_mask(Unit *u) {
*
* Note that on the unified hierarchy it is safe to delegate controllers to unprivileged services. */
- if (u->type == UNIT_SLICE)
- return 0;
-
- c = unit_get_cgroup_context(u);
- if (!c)
- return 0;
-
- if (!c->delegate)
+ if (!unit_cgroup_delegate(u))
return 0;
if (cg_all_unified() <= 0) {
@@ -1142,6 +1136,7 @@ CGroupMask unit_get_delegate_mask(Unit *u) {
return 0;
}
+ assert_se(c = unit_get_cgroup_context(u));
return c->delegate_controllers;
}
@@ -1309,13 +1304,12 @@ void unit_update_cgroup_members_masks(Unit *u) {
}
}
-static const char *migrate_callback(CGroupMask mask, void *userdata) {
- Unit *u = userdata;
+const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask) {
- assert(mask != 0);
- assert(u);
+ /* Returns the realized cgroup path of the specified unit where all specified controllers are available. */
while (u) {
+
if (u->cgroup_path &&
u->cgroup_realized &&
(u->cgroup_realized_mask & mask) == mask)
@@ -1327,6 +1321,10 @@ static const char *migrate_callback(CGroupMask mask, void *userdata) {
return NULL;
}
+static const char *migrate_callback(CGroupMask mask, void *userdata) {
+ return unit_get_realized_cgroup_path(userdata, mask);
+}
+
char *unit_default_cgroup_path(Unit *u) {
_cleanup_free_ char *escaped = NULL, *slice = NULL;
int r;
@@ -1496,7 +1494,7 @@ static int unit_create_cgroup(
u->cgroup_enabled_mask = enable_mask;
u->cgroup_bpf_state = needs_bpf ? UNIT_CGROUP_BPF_ON : UNIT_CGROUP_BPF_OFF;
- if (u->type != UNIT_SLICE && !c->delegate) {
+ if (u->type != UNIT_SLICE && !unit_cgroup_delegate(u)) {
/* Then, possibly move things over, but not if
* subgroups may contain processes, which is the case
@@ -1509,19 +1507,142 @@ static int unit_create_cgroup(
return 0;
}
-int unit_attach_pids_to_cgroup(Unit *u) {
+static int unit_attach_pid_to_cgroup_via_bus(Unit *u, pid_t pid, const char *suffix_path) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ char *pp;
int r;
+
assert(u);
- r = unit_realize_cgroup(u);
+ if (MANAGER_IS_SYSTEM(u->manager))
+ return -EINVAL;
+
+ if (!u->manager->system_bus)
+ return -EIO;
+
+ if (!u->cgroup_path)
+ return -EINVAL;
+
+ /* Determine this unit's cgroup path relative to our cgroup root */
+ pp = path_startswith(u->cgroup_path, u->manager->cgroup_root);
+ if (!pp)
+ return -EINVAL;
+
+ pp = strjoina("/", pp, suffix_path);
+ path_kill_slashes(pp);
+
+ r = sd_bus_call_method(u->manager->system_bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "AttachProcessesToUnit",
+ &error, NULL,
+ "ssau",
+ NULL /* empty unit name means client's unit, i.e. us */, pp, 1, (uint32_t) pid);
if (r < 0)
- return r;
+ return log_unit_debug_errno(u, r, "Failed to attach unit process " PID_FMT " via the bus: %s", pid, bus_error_message(&error, r));
+
+ return 0;
+}
+
+int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_path) {
+ CGroupMask delegated_mask;
+ const char *p;
+ Iterator i;
+ void *pidp;
+ int r, q;
- r = cg_attach_many_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->pids, migrate_callback, u);
+ assert(u);
+
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
+ return -EINVAL;
+
+ if (set_isempty(pids))
+ return 0;
+
+ r = unit_realize_cgroup(u);
if (r < 0)
return r;
- return 0;
+ if (isempty(suffix_path))
+ p = u->cgroup_path;
+ else
+ p = strjoina(u->cgroup_path, "/", suffix_path);
+
+ delegated_mask = unit_get_delegate_mask(u);
+
+ r = 0;
+ SET_FOREACH(pidp, pids, i) {
+ pid_t pid = PTR_TO_PID(pidp);
+ CGroupController c;
+
+ /* First, attach the PID to the main cgroup hierarchy */
+ q = cg_attach(SYSTEMD_CGROUP_CONTROLLER, p, pid);
+ if (q < 0) {
+ log_unit_debug_errno(u, q, "Couldn't move process " PID_FMT " to requested cgroup '%s': %m", pid, p);
+
+ if (MANAGER_IS_USER(u->manager) && IN_SET(q, -EPERM, -EACCES)) {
+ int z;
+
+ /* If we are in a user instance, and we can't move the process ourselves due to
+ * permission problems, let's ask the system instance about it instead. Since it's more
+ * privileged it might be able to move the process across the leaves of a subtree who's
+ * top node is not owned by us. */
+
+ z = unit_attach_pid_to_cgroup_via_bus(u, pid, suffix_path);
+ if (z < 0)
+ log_unit_debug_errno(u, z, "Couldn't move process " PID_FMT " to requested cgroup '%s' via the system bus either: %m", pid, p);
+ else
+ continue; /* When the bus thing worked via the bus we are fully done for this PID. */
+ }
+
+ if (r >= 0)
+ r = q; /* Remember first error */
+
+ continue;
+ }
+
+ q = cg_all_unified();
+ if (q < 0)
+ return q;
+ if (q > 0)
+ continue;
+
+ /* In the legacy hierarchy, attach the process to the request cgroup if possible, and if not to the
+ * innermost realized one */
+
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
+ const char *realized;
+
+ if (!(u->manager->cgroup_supported & bit))
+ continue;
+
+ /* If this controller is delegated and realized, honour the caller's request for the cgroup suffix. */
+ if (delegated_mask & u->cgroup_realized_mask & bit) {
+ q = cg_attach(cgroup_controller_to_string(c), p, pid);
+ if (q >= 0)
+ continue; /* Success! */
+
+ log_unit_debug_errno(u, q, "Failed to attach PID " PID_FMT " to requested cgroup %s in controller %s, falling back to unit's cgroup: %m",
+ pid, p, cgroup_controller_to_string(c));
+ }
+
+ /* So this controller is either not delegate or realized, or something else weird happened. In
+ * that case let's attach the PID at least to the closest cgroup up the tree that is
+ * realized. */
+ realized = unit_get_realized_cgroup_path(u, bit);
+ if (!realized)
+ continue; /* Not even realized in the root slice? Then let's not bother */
+
+ q = cg_attach(cgroup_controller_to_string(c), realized, pid);
+ if (q < 0)
+ log_unit_debug_errno(u, q, "Failed to attach PID " PID_FMT " to realized cgroup %s in controller %s, ignoring: %m",
+ pid, realized, cgroup_controller_to_string(c));
+ }
+ }
+
+ return r;
}
static void cgroup_xattr_apply(Unit *u) {
@@ -2563,6 +2684,21 @@ void unit_invalidate_cgroup_bpf(Unit *u) {
}
}
+bool unit_cgroup_delegate(Unit *u) {
+ CGroupContext *c;
+
+ assert(u);
+
+ if (!UNIT_VTABLE(u)->can_delegate)
+ return false;
+
+ c = unit_get_cgroup_context(u);
+ if (!c)
+ return false;
+
+ return c->delegate;
+}
+
void manager_invalidate_startup_units(Manager *m) {
Iterator i;
Unit *u;
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 1f50441412..e2e875d1c7 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -167,6 +167,7 @@ bool unit_get_needs_bpf(Unit *u);
void unit_update_cgroup_members_masks(Unit *u);
+const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask);
char *unit_default_cgroup_path(Unit *u);
int unit_set_cgroup_path(Unit *u, const char *path);
int unit_pick_cgroup_path(Unit *u);
@@ -178,7 +179,7 @@ int unit_watch_cgroup(Unit *u);
void unit_add_to_cgroup_empty_queue(Unit *u);
-int unit_attach_pids_to_cgroup(Unit *u);
+int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_path);
int manager_setup_cgroup(Manager *m);
void manager_shutdown_cgroup(Manager *m, bool delete);
@@ -219,3 +220,5 @@ void manager_invalidate_startup_units(Manager *m);
const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_;
CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_;
+
+bool unit_cgroup_delegate(Unit *u);
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index f8d90d4b3a..30456fa9f5 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -351,6 +351,9 @@ static int bus_cgroup_set_transient_property(
if (streq(name, "Delegate")) {
int b;
+ if (!UNIT_VTABLE(u)->can_delegate)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delegation not available for unit type");
+
r = sd_bus_message_read(message, "b", &b);
if (r < 0)
return r;
@@ -367,6 +370,9 @@ static int bus_cgroup_set_transient_property(
} else if (streq(name, "DelegateControllers")) {
CGroupMask mask = 0;
+ if (!UNIT_VTABLE(u)->can_delegate)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delegation not available for unit type");
+
r = sd_bus_message_enter_container(message, 'a', "s");
if (r < 0)
return r;
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 4fe374867c..889779d548 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -372,21 +372,16 @@ static int property_get_timer_slack_nsec(
return sd_bus_message_append(reply, "t", (uint64_t) prctl(PR_GET_TIMERSLACK));
}
-static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_free_ char *path = NULL;
- Manager *m = userdata;
- const char *name;
+static int bus_get_unit_by_name(Manager *m, sd_bus_message *message, const char *name, Unit **ret_unit, sd_bus_error *error) {
Unit *u;
int r;
- assert(message);
assert(m);
+ assert(message);
+ assert(ret_unit);
- /* Anyone can call this method */
-
- r = sd_bus_message_read(message, "s", &name);
- if (r < 0)
- return r;
+ /* More or less a wrapper around manager_get_unit() that generates nice errors and has one trick up its sleeve:
+ * if the name is specified empty we use the client's unit. */
if (isempty(name)) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
@@ -409,6 +404,43 @@ static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", name);
}
+ *ret_unit = u;
+ return 0;
+}
+
+static int bus_load_unit_by_name(Manager *m, sd_bus_message *message, const char *name, Unit **ret_unit, sd_bus_error *error) {
+ assert(m);
+ assert(message);
+ assert(ret_unit);
+
+ /* Pretty much the same as bus_get_unit_by_name(), but we also load the unit if necessary. */
+
+ if (isempty(name))
+ return bus_get_unit_by_name(m, message, name, ret_unit, error);
+
+ return manager_load_unit(m, name, NULL, error, ret_unit);
+}
+
+static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ char *path = NULL;
+ Manager *m = userdata;
+ const char *name;
+ Unit *u;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ /* Anyone can call this method */
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = bus_get_unit_by_name(m, message, name, &u, error);
+ if (r < 0)
+ return r;
+
r = mac_selinux_unit_access_check(u, message, "status", error);
if (r < 0)
return r;
@@ -541,26 +573,9 @@ static int method_load_unit(sd_bus_message *message, void *userdata, sd_bus_erro
if (r < 0)
return r;
- if (isempty(name)) {
- _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- pid_t pid;
-
- r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
- if (r < 0)
- return r;
-
- r = sd_bus_creds_get_pid(creds, &pid);
- if (r < 0)
- return r;
-
- u = manager_get_unit_by_pid(m, pid);
- if (!u)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit.");
- } else {
- r = manager_load_unit(m, name, NULL, error, &u);
- if (r < 0)
- return r;
- }
+ r = bus_load_unit_by_name(m, message, name, &u, error);
+ if (r < 0)
+ return r;
r = mac_selinux_unit_access_check(u, message, "status", error);
if (r < 0)
@@ -633,8 +648,10 @@ static int method_start_unit_replace(sd_bus_message *message, void *userdata, sd
if (r < 0)
return r;
- u = manager_get_unit(m, old_name);
- if (!u || !u->job || u->job->type != JOB_START)
+ r = bus_get_unit_by_name(m, message, old_name, &u, error);
+ if (r < 0)
+ return r;
+ if (!u->job || u->job->type != JOB_START)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name);
return method_start_unit_generic(message, m, JOB_START, false, error);
@@ -653,9 +670,9 @@ static int method_kill_unit(sd_bus_message *message, void *userdata, sd_bus_erro
if (r < 0)
return r;
- u = manager_get_unit(m, name);
- if (!u)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ r = bus_get_unit_by_name(m, message, name, &u, error);
+ if (r < 0)
+ return r;
return bus_unit_method_kill(message, u, error);
}
@@ -673,9 +690,9 @@ static int method_reset_failed_unit(sd_bus_message *message, void *userdata, sd_
if (r < 0)
return r;
- u = manager_get_unit(m, name);
- if (!u)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ r = bus_get_unit_by_name(m, message, name, &u, error);
+ if (r < 0)
+ return r;
return bus_unit_method_reset_failed(message, u, error);
}
@@ -693,7 +710,7 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- r = manager_load_unit(m, name, NULL, error, &u);
+ r = bus_load_unit_by_name(m, message, name, &u, error);
if (r < 0)
return r;
@@ -717,7 +734,7 @@ static int method_ref_unit(sd_bus_message *message, void *userdata, sd_bus_error
if (r < 0)
return r;
- r = manager_load_unit(m, name, NULL, error, &u);
+ r = bus_load_unit_by_name(m, message, name, &u, error);
if (r < 0)
return r;
@@ -741,7 +758,7 @@ static int method_unref_unit(sd_bus_message *message, void *userdata, sd_bus_err
if (r < 0)
return r;
- r = manager_load_unit(m, name, NULL, error, &u);
+ r = bus_load_unit_by_name(m, message, name, &u, error);
if (r < 0)
return r;
@@ -810,7 +827,7 @@ static int method_list_units_by_names(sd_bus_message *message, void *userdata, s
if (!unit_name_is_valid(*unit, UNIT_NAME_ANY))
continue;
- r = manager_load_unit(m, *unit, NULL, error, &u);
+ r = bus_load_unit_by_name(m, message, *unit, &u, error);
if (r < 0)
return r;
@@ -839,13 +856,33 @@ static int method_get_unit_processes(sd_bus_message *message, void *userdata, sd
if (r < 0)
return r;
- u = manager_get_unit(m, name);
- if (!u)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", name);
+ r = bus_get_unit_by_name(m, message, name, &u, error);
+ if (r < 0)
+ return r;
return bus_unit_method_get_processes(message, u, error);
}
+static int method_attach_processes_to_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Unit *u;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = bus_get_unit_by_name(m, message, name, &u, error);
+ if (r < 0)
+ return r;
+
+ return bus_unit_method_attach_processes(message, u, error);
+}
+
static int transient_unit_from_message(
Manager *m,
sd_bus_message *message,
@@ -2487,6 +2524,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("UnrefUnit", "s", NULL, method_unref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("AttachProcessesToUnit", "ssau", NULL, method_attach_processes_to_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetJobAfter", "u", "a(usssoo)", method_get_job_waiting, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetJobBefore", "u", "a(usssoo)", method_get_job_waiting, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c
index a0c4a65b33..4d9ced81de 100644
--- a/src/core/dbus-scope.c
+++ b/src/core/dbus-scope.c
@@ -89,17 +89,39 @@ static int bus_scope_set_transient_property(
return bus_set_transient_usec(UNIT(s), name, &s->timeout_stop_usec, message, flags, error);
if (streq(name, "PIDs")) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
unsigned n = 0;
- uint32_t pid;
r = sd_bus_message_enter_container(message, 'a', "u");
if (r < 0)
return r;
- while ((r = sd_bus_message_read(message, "u", &pid)) > 0) {
+ for (;;) {
+ uint32_t upid;
+ pid_t pid;
- if (pid <= 1)
- return -EINVAL;
+ r = sd_bus_message_read(message, "u", &upid);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (upid == 0) {
+ if (!creds) {
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_creds_get_pid(creds, &pid);
+ if (r < 0)
+ return r;
+ } else
+ pid = (uid_t) upid;
+
+ r = unit_pid_attachable(UNIT(s), pid, error);
+ if (r < 0)
+ return r;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
r = unit_watch_pid(UNIT(s), pid);
@@ -109,8 +131,6 @@ static int bus_scope_set_transient_property(
n++;
}
- if (r < 0)
- return r;
r = sd_bus_message_exit_container(message);
if (r < 0)
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 7085eee930..50a5ab9819 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -1127,6 +1127,118 @@ static int property_get_ip_counter(
return sd_bus_message_append(reply, "t", value);
}
+int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ _cleanup_(set_freep) Set *pids = NULL;
+ Unit *u = userdata;
+ const char *path;
+ int r;
+
+ assert(message);
+
+ /* This migrates the processes with the specified PIDs into the cgroup of this unit, optionally below a
+ * specified cgroup path. Obviously this only works for units that actually maintain a cgroup
+ * representation. If a process is already in the cgroup no operation is executed – in this case the specified
+ * subcgroup path has no effect! */
+
+ r = mac_selinux_unit_access_check(u, message, "start", error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(message, "s", &path);
+ if (r < 0)
+ return r;
+
+ path = empty_to_null(path);
+ if (path) {
+ if (!path_is_absolute(path))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Control group path is not absolute: %s", path);
+
+ if (!path_is_normalized(path))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Control group path is not normalized: %s", path);
+ }
+
+ if (!unit_cgroup_delegate(u))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Process migration not available on non-delegated units.");
+
+ if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not active, refusing.");
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(message, 'a', "u");
+ if (r < 0)
+ return r;
+ for (;;) {
+ uid_t process_uid, sender_uid;
+ uint32_t upid;
+ pid_t pid;
+
+ r = sd_bus_message_read(message, "u", &upid);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (upid == 0) {
+ r = sd_bus_creds_get_pid(creds, &pid);
+ if (r < 0)
+ return r;
+ } else
+ pid = (uid_t) upid;
+
+ /* Filter out duplicates */
+ if (set_contains(pids, PID_TO_PTR(pid)))
+ continue;
+
+ /* Check if this process is suitable for attaching to this unit */
+ r = unit_pid_attachable(u, pid, error);
+ if (r < 0)
+ return r;
+
+ /* Let's query the sender's UID, so that we can make our security decisions */
+ r = sd_bus_creds_get_euid(creds, &sender_uid);
+ if (r < 0)
+ return r;
+
+ /* Let's validate security: if the sender is root, then all is OK. If the sender is is any other unit,
+ * then the process' UID and the target unit's UID have to match the sender's UID */
+ if (sender_uid != 0 && sender_uid != getuid()) {
+ r = get_process_uid(pid, &process_uid);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to retrieve process UID: %m");
+
+ if (process_uid != sender_uid)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Process " PID_FMT " not owned by client's UID. Refusing.", pid);
+ if (process_uid != u->ref_uid)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Process " PID_FMT " not owned by target unit's UID. Refusing.", pid);
+ }
+
+ if (!pids) {
+ pids = set_new(NULL);
+ if (!pids)
+ return -ENOMEM;
+ }
+
+ r = set_put(pids, PID_TO_PTR(pid));
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ r = unit_attach_pids_to_cgroup(u, pids, path);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to attach processes to control group: %m");
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
const sd_bus_vtable bus_unit_cgroup_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
@@ -1139,6 +1251,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
SD_BUS_PROPERTY("IPEgressBytes", "t", property_get_ip_counter, 0, 0),
SD_BUS_PROPERTY("IPEgressPackets", "t", property_get_ip_counter, 0, 0),
SD_BUS_METHOD("GetProcesses", NULL, "a(sus)", bus_unit_method_get_processes, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("AttachProcesses", "sau", NULL, bus_unit_method_attach_processes, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index d7066eefc8..bb4e43f5ca 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -37,6 +37,7 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus
int bus_unit_set_properties(Unit *u, sd_bus_message *message, UnitWriteFlags flags, bool commit, sd_bus_error *error);
int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 1c3fca353a..305824da9b 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -139,9 +139,10 @@ static int signal_disconnected(sd_bus_message *message, void *userdata, sd_bus_e
assert_se(bus = sd_bus_message_get_bus(message));
if (bus == m->api_bus)
- destroy_bus(m, &m->api_bus);
+ bus_done_api(m);
if (bus == m->system_bus)
- destroy_bus(m, &m->system_bus);
+ bus_done_system(m);
+
if (set_remove(m->private_buses, bus)) {
log_debug("Got disconnect on private connection.");
destroy_bus(m, &bus);
@@ -652,6 +653,8 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
+ (void) sd_bus_set_description(bus, "private-bus-connection");
+
r = sd_bus_set_fd(bus, nfd, nfd);
if (r < 0) {
log_warning_errno(r, "Failed to set fd on new connection bus: %m");
@@ -724,17 +727,29 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
-int manager_sync_bus_names(Manager *m, sd_bus *bus) {
+static int manager_dispatch_sync_bus_names(sd_event_source *es, void *userdata) {
_cleanup_strv_free_ char **names = NULL;
+ Manager *m = userdata;
const char *name;
Iterator i;
Unit *u;
int r;
+ assert(es);
assert(m);
- assert(bus);
+ assert(m->sync_bus_names_event_source == es);
+
+ /* First things first, destroy the defer event so that we aren't triggered again */
+ m->sync_bus_names_event_source = sd_event_source_unref(m->sync_bus_names_event_source);
+
+ /* Let's see if there's anything to do still? */
+ if (!m->api_bus)
+ return 0;
+ if (hashmap_isempty(m->watch_bus))
+ return 0;
- r = sd_bus_list_names(bus, &names, NULL);
+ /* OK, let's sync up the names. Let's see which names are currently on the bus. */
+ r = sd_bus_list_names(m->api_bus, &names, NULL);
if (r < 0)
return log_error_errno(r, "Failed to get initial list of names: %m");
@@ -758,7 +773,7 @@ int manager_sync_bus_names(Manager *m, sd_bus *bus) {
const char *unique;
/* If it is, determine its current owner */
- r = sd_bus_get_name_creds(bus, name, SD_BUS_CREDS_UNIQUE_NAME, &creds);
+ r = sd_bus_get_name_creds(m->api_bus, name, SD_BUS_CREDS_UNIQUE_NAME, &creds);
if (r < 0) {
log_full_errno(r == -ENXIO ? LOG_DEBUG : LOG_ERR, r, "Failed to get bus name owner %s: %m", name);
continue;
@@ -792,6 +807,34 @@ int manager_sync_bus_names(Manager *m, sd_bus *bus) {
return 0;
}
+int manager_enqueue_sync_bus_names(Manager *m) {
+ int r;
+
+ assert(m);
+
+ /* Enqueues a request to synchronize the bus names in a later event loop iteration. The callers generally don't
+ * want us to invoke ->bus_name_owner_change() unit calls from their stack frames as this might result in event
+ * dispatching on its own creating loops, hence we simply create a defer event for the event loop and exit. */
+
+ if (m->sync_bus_names_event_source)
+ return 0;
+
+ r = sd_event_add_defer(m->event, &m->sync_bus_names_event_source, manager_dispatch_sync_bus_names, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus name synchronization event: %m");
+
+ r = sd_event_source_set_priority(m->sync_bus_names_event_source, SD_EVENT_PRIORITY_IDLE);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set event priority: %m");
+
+ r = sd_event_source_set_enabled(m->sync_bus_names_event_source, SD_EVENT_ONESHOT);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set even to oneshot: %m");
+
+ (void) sd_event_source_set_description(m->sync_bus_names_event_source, "manager-sync-bus-names");
+ return 0;
+}
+
static int bus_setup_api(Manager *m, sd_bus *bus) {
Iterator i;
char *name;
@@ -837,15 +880,12 @@ static int bus_setup_api(Manager *m, sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to request name: %m");
- r = manager_sync_bus_names(m, bus);
- if (r < 0)
- return r;
-
log_debug("Successfully connected to API bus.");
+
return 0;
}
-static int bus_init_api(Manager *m) {
+int bus_init_api(Manager *m) {
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
@@ -879,6 +919,10 @@ static int bus_init_api(Manager *m) {
m->api_bus = bus;
bus = NULL;
+ r = manager_enqueue_sync_bus_names(m);
+ if (r < 0)
+ return r;
+
return 0;
}
@@ -906,7 +950,7 @@ static int bus_setup_system(Manager *m, sd_bus *bus) {
return 0;
}
-static int bus_init_system(Manager *m) {
+int bus_init_system(Manager *m) {
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
@@ -914,22 +958,21 @@ static int bus_init_system(Manager *m) {
return 0;
/* The API and system bus is the same if we are running in system mode */
- if (MANAGER_IS_SYSTEM(m) && m->api_bus) {
- m->system_bus = sd_bus_ref(m->api_bus);
- return 0;
- }
-
- r = sd_bus_open_system(&bus);
- if (r < 0)
- return log_error_errno(r, "Failed to connect to system bus: %m");
+ if (MANAGER_IS_SYSTEM(m) && m->api_bus)
+ bus = sd_bus_ref(m->api_bus);
+ else {
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to system bus: %m");
- r = bus_setup_disconnected_match(m, bus);
- if (r < 0)
- return r;
+ r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach system bus to event loop: %m");
- r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL);
- if (r < 0)
- return log_error_errno(r, "Failed to attach system bus to event loop: %m");
+ r = bus_setup_disconnected_match(m, bus);
+ if (r < 0)
+ return r;
+ }
r = bus_setup_system(m, bus);
if (r < 0)
@@ -941,7 +984,7 @@ static int bus_init_system(Manager *m) {
return 0;
}
-static int bus_init_private(Manager *m) {
+int bus_init_private(Manager *m) {
_cleanup_close_ int fd = -1;
union sockaddr_union sa = {
.un.sun_family = AF_UNIX
@@ -1013,26 +1056,6 @@ static int bus_init_private(Manager *m) {
return 0;
}
-int bus_init(Manager *m, bool try_bus_connect) {
- int r;
-
- if (try_bus_connect) {
- r = bus_init_system(m);
- if (r < 0)
- return log_error_errno(r, "Failed to initialize D-Bus connection: %m");
-
- r = bus_init_api(m);
- if (r < 0)
- return log_error_errno(r, "Error occured during D-Bus APIs initialization: %m");
- }
-
- r = bus_init_private(m);
- if (r < 0)
- return log_error_errno(r, "Failed to create private D-Bus server: %m");
-
- return 0;
-}
-
static void destroy_bus(Manager *m, sd_bus **bus) {
Iterator i;
Unit *u;
@@ -1063,6 +1086,10 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
if (j->bus_track && sd_bus_track_get_bus(j->bus_track) == *bus)
j->bus_track = sd_bus_track_unref(j->bus_track);
+ HASHMAP_FOREACH(u, m->units, i)
+ if (u->bus_track && sd_bus_track_get_bus(u->bus_track) == *bus)
+ u->bus_track = sd_bus_track_unref(u->bus_track);
+
/* Get rid of queued message on this bus */
if (m->queued_message && sd_bus_message_get_bus(m->queued_message) == *bus)
m->queued_message = sd_bus_message_unref(m->queued_message);
@@ -1077,28 +1104,44 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
*bus = sd_bus_unref(*bus);
}
-void bus_done(Manager *m) {
- sd_bus *b;
-
+void bus_done_api(Manager *m) {
assert(m);
if (m->api_bus)
destroy_bus(m, &m->api_bus);
+}
+
+void bus_done_system(Manager *m) {
+ assert(m);
+
if (m->system_bus)
destroy_bus(m, &m->system_bus);
+}
+
+void bus_done_private(Manager *m) {
+ sd_bus *b;
+
+ assert(m);
+
while ((b = set_steal_first(m->private_buses)))
destroy_bus(m, &b);
m->private_buses = set_free(m->private_buses);
- m->subscribed = sd_bus_track_unref(m->subscribed);
- m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
+ m->private_listen_event_source = sd_event_source_unref(m->private_listen_event_source);
+ m->private_listen_fd = safe_close(m->private_listen_fd);
+}
- if (m->private_listen_event_source)
- m->private_listen_event_source = sd_event_source_unref(m->private_listen_event_source);
+void bus_done(Manager *m) {
+ assert(m);
- m->private_listen_fd = safe_close(m->private_listen_fd);
+ bus_done_api(m);
+ bus_done_system(m);
+ bus_done_private(m);
+ assert(!m->subscribed);
+
+ m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
bus_verify_polkit_async_registry_free(m->polkit_registry);
}
@@ -1160,8 +1203,9 @@ int bus_foreach_bus(
}
/* Send to API bus, but only if somebody is subscribed */
- if (sd_bus_track_count(m->subscribed) > 0 ||
- sd_bus_track_count(subscribed2) > 0) {
+ if (m->api_bus &&
+ (sd_bus_track_count(m->subscribed) > 0 ||
+ sd_bus_track_count(subscribed2) > 0)) {
r = send_message(m->api_bus, userdata);
if (r < 0)
ret = r;
diff --git a/src/core/dbus.h b/src/core/dbus.h
index fe50fdd5f7..3051ec0665 100644
--- a/src/core/dbus.h
+++ b/src/core/dbus.h
@@ -24,7 +24,13 @@
int bus_send_queued_message(Manager *m);
-int bus_init(Manager *m, bool try_bus_connect);
+int bus_init_private(Manager *m);
+int bus_init_api(Manager *m);
+int bus_init_system(Manager *m);
+
+void bus_done_private(Manager *m);
+void bus_done_api(Manager *m);
+void bus_done_system(Manager *m);
void bus_done(Manager *m);
int bus_fdset_add_all(Manager *m, FDSet *fds);
@@ -32,7 +38,7 @@ int bus_fdset_add_all(Manager *m, FDSet *fds);
void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix);
int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l);
-int manager_sync_bus_names(Manager *m, sd_bus *bus);
+int manager_enqueue_sync_bus_names(Manager *m);
int bus_foreach_bus(Manager *m, sd_bus_track *subscribed2, int (*send_message)(sd_bus *bus, void *userdata), void *userdata);
diff --git a/src/core/manager.c b/src/core/manager.c
index 08aee31751..e4ef461ac7 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -985,26 +985,6 @@ static int manager_setup_user_lookup_fd(Manager *m) {
return 0;
}
-static int manager_connect_bus(Manager *m, bool reexecuting) {
- bool try_bus_connect;
- Unit *u = NULL;
-
- assert(m);
-
- if (m->test_run_flags)
- return 0;
-
- u = manager_get_unit(m, SPECIAL_DBUS_SERVICE);
-
- try_bus_connect =
- (u && SERVICE(u)->deserialized_state == SERVICE_RUNNING) &&
- (reexecuting ||
- (MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS")));
-
- /* Try to connect to the buses, if possible. */
- return bus_init(m, try_bus_connect);
-}
-
static unsigned manager_dispatch_cleanup_queue(Manager *m) {
Unit *u;
unsigned n = 0;
@@ -1223,6 +1203,7 @@ Manager* manager_free(Manager *m) {
sd_event_source_unref(m->jobs_in_progress_event_source);
sd_event_source_unref(m->run_queue_event_source);
sd_event_source_unref(m->user_lookup_event_source);
+ sd_event_source_unref(m->sync_bus_names_event_source);
safe_close(m->signal_fd);
safe_close(m->notify_fd);
@@ -1374,6 +1355,38 @@ static void manager_distribute_fds(Manager *m, FDSet *fds) {
}
}
+static bool manager_dbus_is_running(Manager *m, bool deserialized) {
+ Unit *u;
+
+ assert(m);
+
+ /* This checks whether the dbus instance we are supposed to expose our APIs on is up. We check both the socket
+ * and the service unit. If the 'deserialized' parameter is true we'll check the deserialized state of the unit
+ * rather than the current one. */
+
+ if (m->test_run_flags != 0)
+ return false;
+
+ /* If we are in the user instance, and the env var is already set for us, then this means D-Bus is ran
+ * somewhere outside of our own logic. Let's use it */
+ if (MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS"))
+ return true;
+
+ u = manager_get_unit(m, SPECIAL_DBUS_SOCKET);
+ if (!u)
+ return false;
+ if ((deserialized ? SOCKET(u)->deserialized_state : SOCKET(u)->state) != SOCKET_RUNNING)
+ return false;
+
+ u = manager_get_unit(m, SPECIAL_DBUS_SERVICE);
+ if (!u)
+ return false;
+ if (!IN_SET((deserialized ? SERVICE(u)->deserialized_state : SERVICE(u)->state), SERVICE_RUNNING, SERVICE_RELOAD))
+ return false;
+
+ return true;
+}
+
int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
int r;
@@ -1454,9 +1467,22 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
/* This shouldn't fail, except if things are really broken. */
return r;
- /* Let's connect to the bus now. */
- (void) manager_connect_bus(m, !!serialization);
+ /* Let's set up our private bus connection now, unconditionally */
+ (void) bus_init_private(m);
+
+ /* If we are in --user mode also connect to the system bus now */
+ if (MANAGER_IS_USER(m))
+ (void) bus_init_system(m);
+
+ /* Let's connect to the bus now, but only if the unit is supposed to be up */
+ if (manager_dbus_is_running(m, !!serialization)) {
+ (void) bus_init_api(m);
+ if (MANAGER_IS_SYSTEM(m))
+ (void) bus_init_system(m);
+ }
+
+ /* Now that we are connected to all possible busses, let's deserialize who is tracking us. */
(void) bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed);
m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
@@ -2294,23 +2320,21 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
/* This is a nop on non-init */
break;
- case SIGUSR1: {
- Unit *u;
-
- u = manager_get_unit(m, SPECIAL_DBUS_SERVICE);
+ case SIGUSR1:
- if (!u || UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) {
+ if (manager_dbus_is_running(m, false)) {
log_info("Trying to reconnect to bus...");
- bus_init(m, true);
- }
- if (!u || !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) {
- log_info("Loading D-Bus service...");
+ (void) bus_init_api(m);
+
+ if (MANAGER_IS_SYSTEM(m))
+ (void) bus_init_system(m);
+ } else {
+ log_info("Starting D-Bus service...");
manager_start_target(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE);
}
break;
- }
case SIGUSR2: {
_cleanup_free_ char *dump = NULL;
@@ -3153,12 +3177,14 @@ int manager_reload(Manager *m) {
exec_runtime_vacuum(m);
- /* It might be safe to log to the journal now. */
+ /* It might be safe to log to the journal now and connect to dbus */
manager_recheck_journal(m);
+ manager_recheck_dbus(m);
/* Sync current state of bus names with our set of listening units */
- if (m->api_bus)
- manager_sync_bus_names(m, m->api_bus);
+ q = manager_enqueue_sync_bus_names(m);
+ if (q < 0 && r >= 0)
+ r = q;
assert(m->n_reloading > 0);
m->n_reloading--;
@@ -3518,11 +3544,35 @@ int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) {
return 0;
}
+void manager_recheck_dbus(Manager *m) {
+ assert(m);
+
+ /* Connects to the bus if the dbus service and socket are running. If we are running in user mode this is all
+ * it does. In system mode we'll also connect to the system bus (which will most likely just reuse the
+ * connection of the API bus). That's because the system bus after all runs as service of the system instance,
+ * while in the user instance we can assume it's already there. */
+
+ if (manager_dbus_is_running(m, false)) {
+ (void) bus_init_api(m);
+
+ if (MANAGER_IS_SYSTEM(m))
+ (void) bus_init_system(m);
+ } else {
+ (void) bus_done_api(m);
+
+ if (MANAGER_IS_SYSTEM(m))
+ (void) bus_done_system(m);
+ }
+}
+
static bool manager_journal_is_running(Manager *m) {
Unit *u;
assert(m);
+ if (m->test_run_flags != 0)
+ return false;
+
/* If we are the user manager we can safely assume that the journal is up */
if (!MANAGER_IS_SYSTEM(m))
return true;
@@ -3538,7 +3588,7 @@ static bool manager_journal_is_running(Manager *m) {
u = manager_get_unit(m, SPECIAL_JOURNALD_SERVICE);
if (!u)
return false;
- if (SERVICE(u)->state != SERVICE_RUNNING)
+ if (!IN_SET(SERVICE(u)->state, SERVICE_RELOAD, SERVICE_RUNNING))
return false;
return true;
@@ -3552,16 +3602,10 @@ void manager_recheck_journal(Manager *m) {
if (getpid_cached() != 1)
return;
- if (manager_journal_is_running(m)) {
-
- /* The journal is fully and entirely up? If so, let's permit logging to it, if that's configured. */
- log_set_prohibit_ipc(false);
- } else {
-
- /* If the journal is down, don't ever log to it, otherwise we might end up deadlocking ourselves as we
- * might trigger an activation ourselves we can't fulfill */
- log_set_prohibit_ipc(true);
- }
+ /* The journal is fully and entirely up? If so, let's permit logging to it, if that's configured. If the
+ * journal is down, don't ever log to it, otherwise we might end up deadlocking ourselves as we might trigger
+ * an activation ourselves we can't fulfill. */
+ log_set_prohibit_ipc(!manager_journal_is_running(m));
log_open();
}
@@ -3702,18 +3746,6 @@ Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) {
return hashmap_get(m->units_requiring_mounts_for, streq(p, "/") ? "" : p);
}
-void manager_set_exec_params(Manager *m, ExecParameters *p) {
- assert(m);
- assert(p);
-
- p->environment = m->environment;
- p->confirm_spawn = manager_get_confirm_spawn(m);
- p->cgroup_supported = m->cgroup_supported;
- p->prefix = m->prefix;
-
- SET_FLAG(p->flags, EXEC_PASS_LOG_UNIT|EXEC_CHOWN_DIRECTORIES, MANAGER_IS_SYSTEM(m));
-}
-
int manager_update_failed_units(Manager *m, Unit *u, bool failed) {
unsigned size;
int r;
diff --git a/src/core/manager.h b/src/core/manager.h
index c785861bfb..d4eaaa1c4b 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -182,6 +182,8 @@ struct Manager {
int user_lookup_fds[2];
sd_event_source *user_lookup_event_source;
+ sd_event_source *sync_bus_names_event_source;
+
UnitFileScope unit_file_scope;
LookupPaths lookup_paths;
Set *unit_path_cache;
@@ -425,6 +427,7 @@ bool manager_unit_inactive_or_pending(Manager *m, const char *name);
void manager_check_finished(Manager *m);
+void manager_recheck_dbus(Manager *m);
void manager_recheck_journal(Manager *m);
void manager_set_show_status(Manager *m, ShowStatus mode);
@@ -435,8 +438,6 @@ void manager_flip_auto_status(Manager *m, bool enable);
Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path);
-void manager_set_exec_params(Manager *m, ExecParameters *p);
-
ManagerState manager_state(Manager *m);
int manager_update_failed_units(Manager *m, Unit *u, bool failed);
diff --git a/src/core/mount.c b/src/core/mount.c
index 7be4e89a61..4d80918b89 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -781,7 +781,6 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
if (r < 0)
return r;
- manager_set_exec_params(UNIT(m)->manager, &exec_params);
unit_set_exec_params(UNIT(m), &exec_params);
r = exec_spawn(UNIT(m),
diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf
index 97f6094b66..645c8f1659 100644
--- a/src/core/org.freedesktop.systemd1.conf
+++ b/src/core/org.freedesktop.systemd1.conf
@@ -30,7 +30,7 @@
<policy context="default">
<deny send_destination="org.freedesktop.systemd1"/>
- <!-- Completely open to anyone -->
+ <!-- Completely open to anyone: org.freedesktop.DBus.* interfaces -->
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.DBus.Introspectable"/>
@@ -46,6 +46,8 @@
send_interface="org.freedesktop.DBus.Properties"
send_member="GetAll"/>
+ <!-- Completely open to anyone: org.freedesktop.systemd1.Manager interface -->
+
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
send_member="GetUnit"/>
@@ -64,6 +66,10 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="GetUnitProcesses"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="GetJob"/>
<allow send_destination="org.freedesktop.systemd1"
@@ -88,43 +94,43 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="ListUnitFiles"/>
+ send_member="ListUnitsByNames"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="ListUnitFilesByPatterns"/>
+ send_member="ListJobs"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="GetUnitFileState"/>
+ send_member="Subscribe"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="GetUnitProcesses"/>
+ send_member="Unsubscribe"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="GetUnitFileLinks"/>
+ send_member="Dump"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="ListJobs"/>
+ send_member="ListUnitFiles"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="Subscribe"/>
+ send_member="ListUnitFilesByPatterns"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="Unsubscribe"/>
+ send_member="GetUnitFileState"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="Dump"/>
+ send_member="GetDefaultTarget"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="GetDefaultTarget"/>
+ send_member="GetUnitFileLinks"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
@@ -134,7 +140,43 @@
send_interface="org.freedesktop.systemd1.Manager"
send_member="LookupDynamicUserByUID"/>
- <!-- Managed via polkit or other criteria -->
+ <!-- Completely open to anyone: org.freedesktop.systemd1.Unit interface -->
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Service"
+ send_member="GetProcesses"/>
+
+ <!-- Completely open to anyone: org.freedesktop.systemd1.Slice interface -->
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Slice"
+ send_member="GetProcesses"/>
+
+ <!-- Completely open to anyone: org.freedesktop.systemd1.Scope interface -->
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Scope"
+ send_member="GetProcesses"/>
+
+ <!-- Completely open to anyone: org.freedesktop.systemd1.Socket interface -->
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Socket"
+ send_member="GetProcesses"/>
+
+ <!-- Completely open to anyone: org.freedesktop.systemd1.Mount interface -->
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Mount"
+ send_member="GetProcesses"/>
+
+ <!-- Completely open to anyone: org.freedesktop.systemd1.Swap interface -->
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Swap"
+ send_member="GetProcesses"/>
+
+ <!-- Managed via polkit or other criteria: org.freedesktop.systemd1.Manager interface -->
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
@@ -182,7 +224,11 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="ListUnitsByNames"/>
+ send_member="RefUnit"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
+ send_member="UnrefUnit"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
@@ -190,23 +236,27 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="AttachProcessesToUnit"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="CancelJob"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="Reload"/>
+ send_member="ClearJobs"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="Reexecute"/>
+ send_member="ResetFailed"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="RefUnit"/>
+ send_member="Reload"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="UnrefUnit"/>
+ send_member="Reexecute"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
@@ -226,10 +276,6 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
- send_member="RevertUnitFiles"/>
-
- <allow send_destination="org.freedesktop.systemd1"
- send_interface="org.freedesktop.systemd1.Manager"
send_member="PresetUnitFiles"/>
<allow send_destination="org.freedesktop.systemd1"
@@ -246,6 +292,10 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="RevertUnitFiles"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="SetDefaultTarget"/>
<allow send_destination="org.freedesktop.systemd1"
@@ -256,6 +306,8 @@
send_interface="org.freedesktop.systemd1.Manager"
send_member="AddDependencyUnitFiles"/>
+ <!-- Managed via polkit or other criteria: org.freedesktop.systemd1.Job interface -->
+
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Job"
send_member="Cancel"/>
@@ -268,6 +320,68 @@
send_interface="org.freedesktop.systemd1.Job"
send_member="GetBefore"/>
+ <!-- Managed via polkit or other criteria: org.freedesktop.systemd1.Unit interface -->
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Unit"
+ send_member="Start"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Unit"
+ send_member="Stop"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Unit"
+ send_member="Reload"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Unit"
+ send_member="Restart"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Unit"
+ send_member="TryRestart"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Unit"
+ send_member="ReloadOrRestart"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Unit"
+ send_member="ReloadOrTryRestart"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Unit"
+ send_member="Kill"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Unit"
+ send_member="ResetFailed"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Unit"
+ send_member="SetProperties"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Unit"
+ send_member="Ref"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Unit"
+ send_member="Unref"/>
+
+ <!-- Managed via polkit or other criteria: org.freedesktop.systemd1.Service interface -->
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Service"
+ send_member="AttachProcesses"/>
+
+ <!-- Managed via polkit or other criteria: org.freedesktop.systemd1.Scope interface -->
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Scope"
+ send_member="AttachProcesses"/>
+
<allow receive_sender="org.freedesktop.systemd1"/>
</policy>
diff --git a/src/core/scope.c b/src/core/scope.c
index 2831e057a1..5b9c2bb3c4 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -343,7 +343,7 @@ static int scope_start(Unit *u) {
unit_export_state_files(UNIT(s));
- r = unit_attach_pids_to_cgroup(u);
+ r = unit_attach_pids_to_cgroup(u, UNIT(s)->pids, NULL);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to add PIDs to scope's control group: %m");
scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
@@ -599,6 +599,7 @@ const UnitVTable scope_vtable = {
.private_section = "Scope",
.can_transient = true,
+ .can_delegate = true,
.init = scope_init,
.load = scope_load,
diff --git a/src/core/service.c b/src/core/service.c
index 37904874b5..ab4626f51b 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1449,7 +1449,6 @@ static int service_spawn(
}
}
- manager_set_exec_params(UNIT(s)->manager, &exec_params);
unit_set_exec_params(UNIT(s), &exec_params);
final_env = strv_env_merge(2, exec_params.environment, our_env, NULL);
@@ -3909,6 +3908,9 @@ const UnitVTable service_vtable = {
"Install\0",
.private_section = "Service",
+ .can_transient = true,
+ .can_delegate = true,
+
.init = service_init,
.done = service_done,
.load = service_load,
@@ -3954,7 +3956,6 @@ const UnitVTable service_vtable = {
.get_timeout = service_get_timeout,
.needs_console = service_needs_console,
- .can_transient = true,
.status_message_formats = {
.starting_stopping = {
diff --git a/src/core/socket.c b/src/core/socket.c
index 703f9f760f..38ad8820be 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -1910,7 +1910,6 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
if (r < 0)
return r;
- manager_set_exec_params(UNIT(s)->manager, &exec_params);
unit_set_exec_params(UNIT(s), &exec_params);
exec_params.argv = c->argv;
diff --git a/src/core/swap.c b/src/core/swap.c
index 37c97bc14e..fde0c24089 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -632,7 +632,6 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
if (r < 0)
goto fail;
- manager_set_exec_params(UNIT(s)->manager, &exec_params);
unit_set_exec_params(UNIT(s), &exec_params);
r = exec_spawn(UNIT(s),
diff --git a/src/core/unit.c b/src/core/unit.c
index 32119171fe..bd077cb359 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -2326,18 +2326,16 @@ static void unit_update_on_console(Unit *u) {
}
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) {
- Manager *m;
bool unexpected;
+ Manager *m;
assert(u);
assert(os < _UNIT_ACTIVE_STATE_MAX);
assert(ns < _UNIT_ACTIVE_STATE_MAX);
- /* Note that this is called for all low-level state changes,
- * even if they might map to the same high-level
- * UnitActiveState! That means that ns == os is an expected
- * behavior here. For example: if a mount point is remounted
- * this function will be called too! */
+ /* Note that this is called for all low-level state changes, even if they might map to the same high-level
+ * UnitActiveState! That means that ns == os is an expected behavior here. For example: if a mount point is
+ * remounted this function will be called too! */
m = u->manager;
@@ -2463,12 +2461,6 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
/* Some names are special */
if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
- if (unit_has_name(u, SPECIAL_DBUS_SERVICE))
- /* The bus might have just become available,
- * hence try to connect to it, if we aren't
- * yet connected. */
- bus_init(m, true);
-
if (u->type == UNIT_SERVICE &&
!UNIT_IS_ACTIVE_OR_RELOADING(os) &&
!MANAGER_IS_RELOADING(m)) {
@@ -2481,8 +2473,6 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
manager_send_unit_plymouth(m, u);
} else {
- /* We don't care about D-Bus going down here, since we'll get an asynchronous notification for it
- * anyway. */
if (UNIT_IS_INACTIVE_OR_FAILED(ns) &&
!UNIT_IS_INACTIVE_OR_FAILED(os)
@@ -2512,17 +2502,15 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
}
manager_recheck_journal(m);
+ manager_recheck_dbus(m);
unit_trigger_notify(u);
if (!MANAGER_IS_RELOADING(u->manager)) {
- /* Maybe we finished startup and are now ready for
- * being stopped because unneeded? */
+ /* Maybe we finished startup and are now ready for being stopped because unneeded? */
unit_check_unneeded(u);
- /* Maybe we finished startup, but something we needed
- * has vanished? Let's die then. (This happens when
- * something BindsTo= to a Type=oneshot unit, as these
- * units go directly from starting to inactive,
+ /* Maybe we finished startup, but something we needed has vanished? Let's die then. (This happens when
+ * something BindsTo= to a Type=oneshot unit, as these units go directly from starting to inactive,
* without ever entering started.) */
unit_check_binds_to(u);
@@ -4541,22 +4529,15 @@ int unit_kill_context(
} else if (r > 0) {
- /* FIXME: For now, on the legacy hierarchy, we
- * will not wait for the cgroup members to die
- * if we are running in a container or if this
- * is a delegation unit, simply because cgroup
- * notification is unreliable in these
- * cases. It doesn't work at all in
- * containers, and outside of containers it
- * can be confused easily by left-over
- * directories in the cgroup — which however
- * should not exist in non-delegated units. On
- * the unified hierarchy that's different,
- * there we get proper events. Hence rely on
- * them. */
+ /* FIXME: For now, on the legacy hierarchy, we will not wait for the cgroup members to die if
+ * we are running in a container or if this is a delegation unit, simply because cgroup
+ * notification is unreliable in these cases. It doesn't work at all in containers, and outside
+ * of containers it can be confused easily by left-over directories in the cgroup — which
+ * however should not exist in non-delegated units. On the unified hierarchy that's different,
+ * there we get proper events. Hence rely on them. */
if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0 ||
- (detect_container() == 0 && !UNIT_CGROUP_BOOL(u, delegate)))
+ (detect_container() == 0 && !unit_cgroup_delegate(u)))
wait_for_exit = true;
if (send_sighup) {
@@ -5006,8 +4987,16 @@ void unit_set_exec_params(Unit *u, ExecParameters *p) {
assert(u);
assert(p);
+ /* Copy parameters from manager */
+ p->environment = u->manager->environment;
+ p->confirm_spawn = manager_get_confirm_spawn(u->manager);
+ p->cgroup_supported = u->manager->cgroup_supported;
+ p->prefix = u->manager->prefix;
+ SET_FLAG(p->flags, EXEC_PASS_LOG_UNIT|EXEC_CHOWN_DIRECTORIES, MANAGER_IS_SYSTEM(u->manager));
+
+ /* Copy paramaters from unit */
p->cgroup_path = u->cgroup_path;
- SET_FLAG(p->flags, EXEC_CGROUP_DELEGATE, UNIT_CGROUP_BOOL(u, delegate));
+ SET_FLAG(p->flags, EXEC_CGROUP_DELEGATE, unit_cgroup_delegate(u));
}
int unit_fork_helper_process(Unit *u, const char *name, pid_t *ret) {
@@ -5373,6 +5362,34 @@ const char *unit_label_path(Unit *u) {
return p;
}
+int unit_pid_attachable(Unit *u, pid_t pid, sd_bus_error *error) {
+ int r;
+
+ assert(u);
+
+ /* Checks whether the specified PID is generally good for attaching, i.e. a valid PID, not our manager itself,
+ * and not a kernel thread either */
+
+ /* First, a simple range check */
+ if (!pid_is_valid(pid))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Process identifier " PID_FMT " is not valid.", pid);
+
+ /* Some extra safety check */
+ if (pid == 1 || pid == getpid_cached())
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Process " PID_FMT " is a manager processs, refusing.", pid);
+
+ /* Don't even begin to bother with kernel threads */
+ r = is_kernel_thread(pid);
+ if (r == -ESRCH)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "Process with ID " PID_FMT " does not exist.", pid);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to determine whether process " PID_FMT " is a kernel thread: %m", pid);
+ if (r > 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Process " PID_FMT " is a kernel thread, refusing.", pid);
+
+ return 0;
+}
+
static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
[COLLECT_INACTIVE] = "inactive",
[COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",
diff --git a/src/core/unit.h b/src/core/unit.h
index 3210583050..617f044e24 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -568,6 +568,9 @@ struct UnitVTable {
/* True if transient units of this type are OK */
bool can_transient:1;
+ /* True if cgroup delegation is permissible */
+ bool can_delegate:1;
+
/* True if queued jobs of this type should be GC'ed if no other job needs them anymore */
bool gc_jobs:1;
};
@@ -803,6 +806,8 @@ bool unit_needs_console(Unit *u);
const char *unit_label_path(Unit *u);
+int unit_pid_attachable(Unit *unit, pid_t pid, sd_bus_error *error);
+
/* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full(unit, level, error, ...) \
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index c69c596c59..cbbc6b6f0c 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -3920,7 +3920,15 @@ _public_ int sd_bus_get_description(sd_bus *bus, const char **description) {
assert_return(bus->description, -ENXIO);
assert_return(!bus_pid_changed(bus), -ECHILD);
- *description = bus->description;
+ if (bus->description)
+ *description = bus->description;
+ else if (bus->is_system)
+ *description = "system";
+ else if (bus->is_user)
+ *description = "user";
+ else
+ *description = NULL;
+
return 0;
}