mirror of
https://github.com/systemd/systemd.git
synced 2024-10-31 16:21:26 +03:00
Merge pull request #8125 from poettering/cgroups-migrate
Trivial merge conflict resolved locally.
This commit is contained in:
commit
648461c07d
@ -22,7 +22,7 @@
|
||||
|
||||
[Distribution]
|
||||
Distribution=fedora
|
||||
Release=26
|
||||
Release=27
|
||||
|
||||
[Output]
|
||||
Format=raw_btrfs
|
||||
|
11
TODO
11
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?
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
/* Skip preceeding whitespace */
|
||||
l = strspn(q, WHITESPACE);
|
||||
if (l < 1)
|
||||
return -EINVAL;
|
||||
q += l;
|
||||
|
||||
count = fread(&c, 1, 1, f);
|
||||
eof = feof(f);
|
||||
fclose(f);
|
||||
/* Truncate the rest */
|
||||
l = strcspn(q, WHITESPACE);
|
||||
if (l < 1)
|
||||
return -EINVAL;
|
||||
q[l] = 0;
|
||||
|
||||
/* Kernel threads have an empty cmdline */
|
||||
r = safe_atollu(q, &flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (count <= 0)
|
||||
return eof ? 1 : -errno;
|
||||
|
||||
return 0;
|
||||
return !!(flags & PF_KTHREAD);
|
||||
}
|
||||
|
||||
int get_process_capeff(pid_t pid, char **capeff) {
|
||||
|
@ -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 &&
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
||||
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 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;
|
||||
|
||||
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;
|
||||
|
||||
r = cg_attach_many_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->pids, migrate_callback, u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (isempty(suffix_path))
|
||||
p = u->cgroup_path;
|
||||
else
|
||||
p = strjoina(u->cgroup_path, "/", suffix_path);
|
||||
|
||||
return 0;
|
||||
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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
162
src/core/dbus.c
162
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);
|
||||
|
||||
r = sd_bus_list_names(bus, &names, NULL);
|
||||
/* 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;
|
||||
|
||||
/* 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,23 +958,22 @@ 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;
|
||||
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 = 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 = 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 = bus_setup_system(m, bus);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up system bus: %m");
|
||||
@ -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);
|
||||
|
||||
if (m->private_listen_event_source)
|
||||
m->private_listen_event_source = sd_event_source_unref(m->private_listen_event_source);
|
||||
|
||||
m->private_listen_event_source = sd_event_source_unref(m->private_listen_event_source);
|
||||
m->private_listen_fd = safe_close(m->private_listen_fd);
|
||||
}
|
||||
|
||||
void bus_done(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
case SIGUSR1:
|
||||
|
||||
u = manager_get_unit(m, SPECIAL_DBUS_SERVICE);
|
||||
|
||||
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;
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -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"/>
|
||||
@ -62,6 +64,10 @@
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="LoadUnit"/>
|
||||
|
||||
<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"/>
|
||||
@ -88,23 +94,7 @@
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="ListUnitFiles"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="ListUnitFilesByPatterns"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="GetUnitFileState"/>
|
||||
|
||||
<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="GetUnitFileLinks"/>
|
||||
send_member="ListUnitsByNames"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
@ -122,10 +112,26 @@
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="Dump"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="ListUnitFiles"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="ListUnitFilesByPatterns"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="GetUnitFileState"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="GetDefaultTarget"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="GetUnitFileLinks"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="LookupDynamicUserByName"/>
|
||||
@ -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,16 +224,32 @@
|
||||
|
||||
<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"
|
||||
send_member="StartTransientUnit"/>
|
||||
|
||||
<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="ClearJobs"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="ResetFailed"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="Reload"/>
|
||||
@ -200,14 +258,6 @@
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="Reexecute"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
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"
|
||||
send_member="EnableUnitFiles"/>
|
||||
@ -224,10 +274,6 @@
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="LinkUnitFiles"/>
|
||||
|
||||
<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"/>
|
||||
@ -244,6 +290,10 @@
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="UnmaskUnitFiles"/>
|
||||
|
||||
<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"/>
|
||||
@ -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>
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 = {
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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",
|
||||
|
@ -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, ...) \
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user