diff --git a/.semaphore/semaphore-runner.sh b/.semaphore/semaphore-runner.sh index e903ced70d..8c99b68156 100755 --- a/.semaphore/semaphore-runner.sh +++ b/.semaphore/semaphore-runner.sh @@ -4,7 +4,6 @@ set -eux set -o pipefail -# default to Debian testing DISTRO="${DISTRO:-debian}" RELEASE="${RELEASE:-bookworm}" SALSA_URL="${SALSA_URL:-https://salsa.debian.org/systemd-team/systemd.git}" @@ -24,9 +23,19 @@ create_container() { sudo lxc-create -n "$CONTAINER" -t download -- -d "$DISTRO" -r "$RELEASE" -a "$ARCH" # unconfine the container, otherwise some tests fail - echo 'lxc.apparmor.profile = unconfined' | sudo tee -a "/var/lib/lxc/$CONTAINER/config" + # + # disable automatic cgroup setup, instead let pid1 figure it out in mount_setup(). + # This is especially important to ensure we get unified cgroup hierarchy + # + # FIXME: remove cgroup workarounds once the host runs on unified hierarchy + sudo tee "/var/lib/lxc/$CONTAINER/config.systemd_upstream" < - sd_pid_get_cgroup() returns the control - group path of the specified process, relative to the root of the - hierarchy. Returns the path without trailing slash, except for - processes located in the root control group, where "/" is - returned. To find the actual control group path in the file system, - the returned path needs to be prefixed with - /sys/fs/cgroup/ (if the unified control group - setup is used), or - /sys/fs/cgroup/HIERARCHY/ - (if the legacy multi-hierarchy control group setup is used). + sd_pid_get_cgroup() returns the control group path of the specified process, + relative to the root of the hierarchy. Returns the path without trailing slash, except for processes + located in the root control group, where "/" is returned. To find the actual control group path + in the file system, the returned path needs to be prefixed with /sys/fs/cgroup/. If the pid parameter of any of these functions is passed as 0, the operation is executed for the diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 8067bc082b..3c3dfdbfab 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -2316,20 +2316,6 @@ static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] = DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType); -bool is_cgroup_fs(const struct statfs *s) { - return is_fs_type(s, CGROUP_SUPER_MAGIC) || - is_fs_type(s, CGROUP2_SUPER_MAGIC); -} - -bool fd_is_cgroup_fs(int fd) { - struct statfs s; - - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_cgroup_fs(&s); -} - static const char *const cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = { [CGROUP_CONTROLLER_CPU] = "cpu", [CGROUP_CONTROLLER_CPUACCT] = "cpuacct", diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index 77294779fe..3e84084794 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -329,9 +329,6 @@ static inline int cg_unified(void) { const char* cgroup_controller_to_string(CGroupController c) _const_; CGroupController cgroup_controller_from_string(const char *s) _pure_; -bool is_cgroup_fs(const struct statfs *s); -bool fd_is_cgroup_fs(int fd); - typedef enum ManagedOOMMode { MANAGED_OOM_AUTO, MANAGED_OOM_KILL, diff --git a/src/core/main.c b/src/core/main.c index ee4b2d6baf..dc93c42f17 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -3201,14 +3201,6 @@ int main(int argc, char *argv[]) { goto finish; } - if (!skip_setup) { - r = mount_cgroup_legacy_controllers(loaded_policy); - if (r < 0) { - error_message = "Failed to mount cgroup v1 hierarchy"; - goto finish; - } - } - /* The efivarfs is now mounted, let's lock down the system token. */ lock_down_efi_variables(); } else { @@ -3310,6 +3302,23 @@ int main(int argc, char *argv[]) { log_execution_mode(&first_boot); + r = cg_has_legacy(); + if (r < 0) { + error_message = "Failed to check cgroup hierarchy"; + goto finish; + } + if (r > 0) { + r = log_full_errno(LOG_EMERG, SYNTHETIC_ERRNO(EPROTO), + "Detected cgroup v1 hierarchy at /sys/fs/cgroup/, which is no longer supported by current version of systemd.\n" + "Please instruct your initrd to mount cgroup v2 (unified) hierarchy,\n" + "possibly by removing any stale kernel command line options, such as:\n" + " systemd.legacy_systemd_cgroup_controller=1\n" + " systemd.unified_cgroup_hierarchy=0"); + + error_message = "Detected unsupported legacy cgroup hierarchy, refusing execution"; + goto finish; + } + r = initialize_runtime(skip_setup, first_boot, &saved_rlimit_nofile, diff --git a/src/core/namespace.c b/src/core/namespace.c index c3e9030dd6..e801b6ea60 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -26,6 +26,7 @@ #include "loopback-setup.h" #include "missing_syscall.h" #include "mkdir-label.h" +#include "mount-setup.h" #include "mount-util.h" #include "mountpoint-util.h" #include "namespace-util.h" @@ -1373,34 +1374,16 @@ static int mount_private_sysfs(const MountEntry *m, const NamespaceParameters *p return mount_private_apivfs("sysfs", mount_entry_path(m), "/sys", /* opts = */ NULL, p->runtime_scope); } -static bool check_recursiveprot_supported(void) { - int r; - - /* memory_recursiveprot is only supported for kernels >= 5.7. Note mount_option_supported uses fsopen() - * and fsconfig() which are supported for kernels >= 5.2. So if mount_option_supported() returns an - * error, we can assume memory_recursiveprot is not supported. */ - r = mount_option_supported("cgroup2", "memory_recursiveprot", NULL); - if (r < 0) - log_debug_errno(r, "Failed to determine whether the 'memory_recursiveprot' mount option is supported, assuming not: %m"); - else if (r == 0) - log_debug("This kernel version does not support 'memory_recursiveprot', not using mount option."); - - return r > 0; -} - static int mount_private_cgroup2fs(const MountEntry *m, const NamespaceParameters *p) { _cleanup_free_ char *opts = NULL; assert(m); assert(p); - if (check_recursiveprot_supported()) { - opts = strdup(strempty(mount_entry_options(m))); + if (cgroupfs_recursiveprot_supported()) { + opts = strextend_with_separator(NULL, ",", mount_entry_options(m) ?: POINTER_MAX, "memory_recursiveprot"); if (!opts) return -ENOMEM; - - if (!strextend_with_separator(&opts, ",", "memory_recursiveprot")) - return -ENOMEM; } return mount_private_apivfs("cgroup2", mount_entry_path(m), "/sys/fs/cgroup", opts ?: mount_entry_options(m), p->runtime_scope); diff --git a/src/core/taint.c b/src/core/taint.c index b7a1c647a2..d02ad7fe53 100644 --- a/src/core/taint.c +++ b/src/core/taint.c @@ -52,9 +52,6 @@ char** taint_strv(void) { if (readlink_malloc("/var/run", &var_run) < 0 || !PATH_IN_SET(var_run, "../run", "/run")) stage[n++] = "var-run-bad"; - if (cg_all_unified() == 0) - stage[n++] = "cgroupsv1"; - if (clock_is_localtime(NULL) > 0) stage[n++] = "local-hwclock"; diff --git a/src/shared/cgroup-setup.c b/src/shared/cgroup-setup.c index c1019f571d..76658df41a 100644 --- a/src/shared/cgroup-setup.c +++ b/src/shared/cgroup-setup.c @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include #include "cgroup-setup.h" @@ -9,171 +8,15 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "missing_magic.h" #include "mkdir.h" #include "parse-util.h" #include "path-util.h" -#include "proc-cmdline.h" #include "process-util.h" #include "recurse-dir.h" #include "stdio-util.h" #include "string-util.h" #include "user-util.h" -#include "virt.h" - -static int cg_any_controller_used_for_v1(void) { - _cleanup_free_ char *buf = NULL; - _cleanup_strv_free_ char **lines = NULL; - int r; - - r = read_full_virtual_file("/proc/cgroups", &buf, NULL); - if (r < 0) - return log_debug_errno(r, "Could not read /proc/cgroups, ignoring: %m"); - - r = strv_split_newlines_full(&lines, buf, 0); - if (r < 0) - return r; - - /* The intention of this is to check if the fully unified cgroup tree setup is possible, meaning all - * enabled kernel cgroup controllers are currently not in use by cgroup1. For reference: - * https://systemd.io/CGROUP_DELEGATION/#three-different-tree-setups- - * - * Note that this is typically only useful to check inside a container where we don't know what - * cgroup tree setup is in use by the host; if the host is using legacy or hybrid, we can't use - * unified since some or all controllers would be missing. This is not the best way to detect this, - * as whatever container manager created our container should have mounted /sys/fs/cgroup - * appropriately, but in case that wasn't done, we try to detect if it's possible for us to use - * unified cgroups. */ - STRV_FOREACH(line, lines) { - _cleanup_free_ char *name = NULL, *hierarchy_id = NULL, *num = NULL, *enabled = NULL; - - /* Skip header line */ - if (startswith(*line, "#")) - continue; - - const char *p = *line; - r = extract_many_words(&p, NULL, 0, &name, &hierarchy_id, &num, &enabled); - if (r < 0) - return log_debug_errno(r, "Error parsing /proc/cgroups line, ignoring: %m"); - else if (r < 4) { - log_debug("Invalid /proc/cgroups line, ignoring."); - continue; - } - - /* Ignore disabled controllers. */ - if (streq(enabled, "0")) - continue; - - /* Ignore controllers we don't care about. */ - if (cgroup_controller_from_string(name) < 0) - continue; - - /* Since the unified cgroup doesn't use multiple hierarchies, if any controller has a - * non-zero hierarchy_id that means it's in use already in a legacy (or hybrid) cgroup v1 - * hierarchy, and can't be used in a unified cgroup. */ - if (!streq(hierarchy_id, "0")) { - log_debug("Cgroup controller %s in use by legacy v1 hierarchy.", name); - return 1; - } - } - - return 0; -} - -bool cg_is_unified_wanted(void) { - static thread_local int wanted = -1; - int r; - - /* If we have a cached value, return that. */ - if (wanted >= 0) - return wanted; - - /* If the hierarchy is already mounted, then follow whatever was chosen for it. */ - r = cg_unified_cached(true); - if (r >= 0) - return (wanted = r >= CGROUP_UNIFIED_ALL); - - /* If we have explicit configuration for v1 or v2, respect that. */ - if (cg_is_legacy_force_enabled()) - return (wanted = false); - - bool b; - r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", /* flags = */ 0, &b); - if (r > 0 && b) - return (wanted = true); - - /* If we passed cgroup_no_v1=all with no other instructions, it seems highly unlikely that we want to - * use hybrid or legacy hierarchy. */ - _cleanup_free_ char *c = NULL; - r = proc_cmdline_get_key("cgroup_no_v1", 0, &c); - if (r > 0 && streq_ptr(c, "all")) - return (wanted = true); - - /* If any controller is in use as v1, don't use unified. */ - if (cg_any_controller_used_for_v1() > 0) - return (wanted = false); - - return (wanted = true); -} - -bool cg_is_legacy_wanted(void) { - /* Check if we have cgroup v2 already mounted. */ - if (cg_unified_cached(true) == CGROUP_UNIFIED_ALL) - return false; - - /* Otherwise, assume that at least partial legacy is wanted, - * since cgroup v2 should already be mounted at this point. */ - return true; -} - -bool cg_is_hybrid_wanted(void) { - static thread_local int wanted = -1; - int r; - - /* If we have a cached value, return that. */ - if (wanted >= 0) - return wanted; - - /* If the hierarchy is already mounted, then follow whatever was chosen for it. */ - if (cg_unified_cached(true) == CGROUP_UNIFIED_ALL) - return (wanted = false); - - /* Otherwise, let's see what the kernel command line has to say. Since checking is expensive, cache - * a non-error result. - * The meaning of the kernel option is reversed wrt. to the return value of this function, hence the - * negation. */ - bool b; - r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", /* flags = */ 0, &b); - if (r > 0) - return (wanted = !b); - - /* The default hierarchy is "unified". But if this is reached, it means that unified hierarchy was - * not mounted, so return true too. */ - return (wanted = true); -} - -bool cg_is_legacy_enabled(void) { - int r; - bool b; - - r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", /* flags = */ 0, &b); - return r > 0 && !b; -} - -bool cg_is_legacy_force_enabled(void) { - int r; - bool b; - - /* Require both systemd.unified_cgroup_hierarchy=0 and SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1. */ - - if (!cg_is_legacy_enabled()) - return false; - - r = proc_cmdline_get_bool("SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE", /* flags = */ 0, &b); - if (r <= 0 || !b) - return false; - - return true; -} int cg_weight_parse(const char *s, uint64_t *ret) { uint64_t u; @@ -1109,3 +952,29 @@ int cg_uninstall_release_agent(const char *controller) { return 0; } + +int cg_has_legacy(void) { + struct statfs fs; + + /* Checks if any legacy controller/hierarchy is mounted. */ + + if (statfs("/sys/fs/cgroup/", &fs) < 0) { + if (errno == ENOENT) /* sysfs not mounted? */ + return false; + + return log_error_errno(errno, "Failed to statfs /sys/fs/cgroup/: %m"); + } + + if (is_fs_type(&fs, CGROUP2_SUPER_MAGIC) || + is_fs_type(&fs, SYSFS_MAGIC)) /* not mounted yet */ + return false; + + if (is_fs_type(&fs, TMPFS_MAGIC)) { + log_info("Found tmpfs on /sys/fs/cgroup/, assuming legacy hierarchy."); + return true; + } + + return log_error_errno(SYNTHETIC_ERRNO(ENOMEDIUM), + "Unknown filesystem type %llx mounted on /sys/fs/cgroup/.", + (unsigned long long) fs.f_type); +} diff --git a/src/shared/cgroup-setup.h b/src/shared/cgroup-setup.h index 31c4ea1ace..7f0a8c73bb 100644 --- a/src/shared/cgroup-setup.h +++ b/src/shared/cgroup-setup.h @@ -7,12 +7,6 @@ #include "cgroup-util.h" -bool cg_is_unified_wanted(void); -bool cg_is_legacy_wanted(void); -bool cg_is_hybrid_wanted(void); -bool cg_is_legacy_enabled(void); -bool cg_is_legacy_force_enabled(void); - int cg_weight_parse(const char *s, uint64_t *ret); int cg_cpu_weight_parse(const char *s, uint64_t *ret); int cg_cpu_shares_parse(const char *s, uint64_t *ret); @@ -46,3 +40,5 @@ int cg_trim_v1_controllers(CGroupMask supported, CGroupMask mask, const char *pa int cg_install_release_agent(const char *controller, const char *agent); int cg_uninstall_release_agent(const char *controller); + +int cg_has_legacy(void); diff --git a/src/shared/mount-setup.c b/src/shared/mount-setup.c index 4c5151c7db..db963df39e 100644 --- a/src/shared/mount-setup.c +++ b/src/shared/mount-setup.c @@ -52,27 +52,16 @@ typedef struct MountPoint { MountMode mode; } MountPoint; -/* The first three entries we might need before SELinux is up. The - * fourth (securityfs) is needed by IMA to load a custom policy. The - * other ones we can delay until SELinux and IMA are loaded. When - * SMACK is enabled we need smackfs, too, so it's a fifth one. */ -#if ENABLE_SMACK -#define N_EARLY_MOUNT 5 -#else -#define N_EARLY_MOUNT 4 -#endif - -static bool check_recursiveprot_supported(void) { +bool cgroupfs_recursiveprot_supported(void) { int r; - if (!cg_is_unified_wanted()) - return false; + /* Added in kernel 5.7 */ - r = mount_option_supported("cgroup2", "memory_recursiveprot", NULL); + r = mount_option_supported("cgroup2", "memory_recursiveprot", /* value = */ NULL); if (r < 0) - log_debug_errno(r, "Failed to determine whether the 'memory_recursiveprot' mount option is supported, assuming not: %m"); + log_debug_errno(r, "Failed to determine whether cgroupfs supports 'memory_recursiveprot' mount option, assuming not: %m"); else if (r == 0) - log_debug("This kernel version does not support 'memory_recursiveprot', not using mount option."); + log_debug("'memory_recursiveprot' not supported by cgroupfs, not using mount option."); return r > 0; } @@ -103,11 +92,9 @@ static const MountPoint mount_table[] = { { "tmpfs", "/run", "tmpfs", "mode=0755" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME, NULL, MNT_FATAL|MNT_IN_CONTAINER }, { "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate,memory_recursiveprot", MS_NOSUID|MS_NOEXEC|MS_NODEV, - check_recursiveprot_supported, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE }, + cgroupfs_recursiveprot_supported, MNT_FATAL|MNT_IN_CONTAINER|MNT_CHECK_WRITABLE }, { "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV, - cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE }, - { "cgroup2", "/sys/fs/cgroup", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE }, + NULL, MNT_FATAL|MNT_IN_CONTAINER|MNT_CHECK_WRITABLE }, #if ENABLE_PSTORE { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL, MNT_NONE }, @@ -120,6 +107,16 @@ static const MountPoint mount_table[] = { NULL, MNT_NONE, }, }; +/* The first three entries we might need before SELinux is up. The + * fourth (securityfs) is needed by IMA to load a custom policy. The + * other ones we can delay until SELinux and IMA are loaded. When + * SMACK is enabled we need smackfs, too, so it's a fifth one. */ +#if ENABLE_SMACK +#define N_EARLY_MOUNT 5 +#else +#define N_EARLY_MOUNT 4 +#endif + assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table)); bool mount_point_is_api(const char *path) { @@ -249,56 +246,6 @@ int mount_setup_early(void) { return mount_points_setup(N_EARLY_MOUNT, /* loaded_policy= */ false); } -static const char *join_with(const char *controller) { - - static const char* const pairs[] = { - "cpu", "cpuacct", - "net_cls", "net_prio", - NULL - }; - - assert(controller); - - /* This will lookup which controller to mount another controller with. Input is a controller name, and output - * is the other controller name. The function works both ways: you can input one and get the other, and input - * the other to get the one. */ - - STRV_FOREACH_PAIR(x, y, pairs) { - if (streq(controller, *x)) - return *y; - if (streq(controller, *y)) - return *x; - } - - return NULL; -} - -static int symlink_controller(const char *target, const char *alias) { - const char *a; - int r; - - assert(target); - assert(alias); - - a = strjoina("/sys/fs/cgroup/", alias); - - r = symlink_idempotent(target, a, false); - if (r < 0) - return log_error_errno(r, "Failed to create symlink %s: %m", a); - -#if HAVE_SMACK_RUN_LABEL - const char *p; - - p = strjoina("/sys/fs/cgroup/", target); - - r = mac_smack_copy(a, p); - if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r)) - return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", p, a); -#endif - - return 0; -} - #if HAVE_SELINUX || ENABLE_SMACK static int relabel_cb( RecurseDirEvent event, @@ -486,143 +433,3 @@ int mount_setup(bool loaded_policy, bool leave_propagation) { return 0; } - -static const MountPoint cgroupv1_mount_table[] = { - { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=0755" TMPFS_LIMITS_SYS_FS_CGROUP, MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, - cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER }, - { "cgroup2", "/sys/fs/cgroup/unified", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV, - cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE }, - { "cgroup2", "/sys/fs/cgroup/unified", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE }, - { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV, - cg_is_legacy_wanted, MNT_IN_CONTAINER }, - { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, - cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER }, -}; - -static void relabel_cgroup_legacy_hierarchy(void) { -#if HAVE_SELINUX || ENABLE_SMACK - struct statfs st; - - assert(cg_is_legacy_wanted()); - - /* Temporarily remount the root cgroup filesystem to give it a proper label. Do this - only when the filesystem has been already populated by a previous instance of systemd - running from initrd. Otherwise don't remount anything and leave the filesystem read-write - for the cgroup filesystems to be mounted inside. */ - if (statfs("/sys/fs/cgroup", &st) < 0) - return (void) log_error_errno(errno, "Failed to determine mount flags for /sys/fs/cgroup/: %m"); - - if (st.f_flags & ST_RDONLY) - (void) mount_nofollow(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT, NULL); - - (void) label_fix("/sys/fs/cgroup", 0); - (void) relabel_tree("/sys/fs/cgroup"); - - if (st.f_flags & ST_RDONLY) - (void) mount_nofollow(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT|MS_RDONLY, NULL); -#endif -} - -int mount_cgroup_legacy_controllers(bool loaded_policy) { - _cleanup_set_free_ Set *controllers = NULL; - int r; - - /* Before we actually start deleting cgroup v1 code, make it harder to boot in cgroupv1 mode first. - * See also #30852. */ - - if (detect_container() <= 0) { /* If in container, we have to follow host's cgroup hierarchy. Only - * do the deprecation checks below if we're not in a container. */ - if (cg_is_legacy_force_enabled()) - log_warning("Legacy support for cgroup v1 enabled via SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1."); - else if (cg_is_legacy_enabled()) { - log_full(LOG_CRIT, - "Legacy cgroup v1 configured. This will stop being supported soon.\n" - "Will proceed with cgroup v2 after 30 s.\n" - "Set systemd.unified_cgroup_hierarchy=1 to switch to cgroup v2 " - "or set SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1 to reenable v1 temporarily."); - (void) usleep_safe(30 * USEC_PER_SEC); - - return 0; - } - } - - if (!cg_is_legacy_wanted()) - return 0; - - FOREACH_ELEMENT(mp, cgroupv1_mount_table) { - r = mount_one(mp, loaded_policy); - if (r < 0) - return r; - } - - if (loaded_policy) - relabel_cgroup_legacy_hierarchy(); - - /* Mount all available cgroup controllers that are built into the kernel. */ - r = cg_kernel_controllers(&controllers); - if (r < 0) - return log_error_errno(r, "Failed to enumerate cgroup controllers: %m"); - - for (;;) { - _cleanup_free_ char *options = NULL, *controller = NULL, *where = NULL; - const char *other_controller; - MountPoint p = { - .what = "cgroup", - .type = "cgroup", - .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV, - .mode = MNT_IN_CONTAINER, - }; - - controller = set_steal_first(controllers); - if (!controller) - break; - - /* Check if we shall mount this together with another controller */ - other_controller = join_with(controller); - if (other_controller) { - _cleanup_free_ char *c = NULL; - - /* Check if the other controller is actually available in the kernel too */ - c = set_remove(controllers, other_controller); - if (c) { - - /* Join the two controllers into one string, and maintain a stable ordering */ - if (strcmp(controller, other_controller) < 0) - options = strjoin(controller, ",", other_controller); - else - options = strjoin(other_controller, ",", controller); - if (!options) - return log_oom(); - } - } - - /* The simple case, where there's only one controller to mount together */ - if (!options) - options = TAKE_PTR(controller); - - where = path_join("/sys/fs/cgroup", options); - if (!where) - return log_oom(); - - p.where = where; - p.options = options; - - r = mount_one(&p, true); - if (r < 0) - return r; - - /* Create symlinks from the individual controller names, in case we have a joined mount */ - if (controller) - (void) symlink_controller(options, controller); - if (other_controller) - (void) symlink_controller(options, other_controller); - } - - /* Now that we mounted everything, let's make the tmpfs the cgroup file systems are mounted into read-only. */ - (void) mount_nofollow("tmpfs", "/sys/fs/cgroup", "tmpfs", - MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, - "mode=0755" TMPFS_LIMITS_SYS_FS_CGROUP); - - return 1; -} diff --git a/src/shared/mount-setup.h b/src/shared/mount-setup.h index 9fc49331d9..c07fe86364 100644 --- a/src/shared/mount-setup.h +++ b/src/shared/mount-setup.h @@ -9,4 +9,4 @@ bool mount_point_ignore(const char *path); int mount_setup_early(void); int mount_setup(bool loaded_policy, bool leave_propagation); -int mount_cgroup_legacy_controllers(bool loaded_policy); +bool cgroupfs_recursiveprot_supported(void); diff --git a/src/shared/rm-rf.c b/src/shared/rm-rf.c index 2f50fa30ea..39866e2258 100644 --- a/src/shared/rm-rf.c +++ b/src/shared/rm-rf.c @@ -8,12 +8,12 @@ #include "alloc-util.h" #include "btrfs-util.h" -#include "cgroup-util.h" #include "dirent-util.h" #include "fd-util.h" #include "fs-util.h" #include "log.h" #include "macro.h" +#include "missing_magic.h" #include "mountpoint-util.h" #include "path-util.h" #include "rm-rf.h" @@ -24,7 +24,7 @@ * after all: we can create arbitrary directory hierarchies in it, and hence can also use rm_rf() on it * to remove those again. */ static bool is_physical_fs(const struct statfs *sfs) { - return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs); + return !is_temporary_fs(sfs) && !is_fs_type(sfs, CGROUP2_SUPER_MAGIC); } static int patch_dirfd_mode( diff --git a/src/test/meson.build b/src/test/meson.build index ec03ad2b7d..e86a28f876 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -55,7 +55,6 @@ simple_tests += files( 'test-build-path.c', 'test-bus-util.c', 'test-calendarspec.c', - 'test-cgroup-setup.c', 'test-cgroup-util.c', 'test-cgroup.c', 'test-chase.c', diff --git a/src/test/test-cgroup-setup.c b/src/test/test-cgroup-setup.c deleted file mode 100644 index af5712060b..0000000000 --- a/src/test/test-cgroup-setup.c +++ /dev/null @@ -1,71 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include - -#include "alloc-util.h" -#include "cgroup-setup.h" -#include "errno-util.h" -#include "log.h" -#include "proc-cmdline.h" -#include "string-util.h" -#include "tests.h" - -static void test_is_wanted_print_one(bool header) { - _cleanup_free_ char *cmdline = NULL; - - log_info("-- %s --", __func__); - ASSERT_OK(proc_cmdline(&cmdline)); - log_info("cmdline: %s", cmdline); - if (header) - (void) system("findmnt -n /sys/fs/cgroup"); - - log_info("is_unified_wanted() → %s", yes_no(cg_is_unified_wanted())); - log_info("is_hybrid_wanted() → %s", yes_no(cg_is_hybrid_wanted())); - log_info("is_legacy_wanted() → %s", yes_no(cg_is_legacy_wanted())); - log_info(" "); -} - -TEST(is_wanted_print) { - test_is_wanted_print_one(true); - test_is_wanted_print_one(false); /* run twice to test caching */ -} - -TEST(is_wanted) { - ASSERT_OK_ERRNO(setenv("SYSTEMD_PROC_CMDLINE", - "systemd.unified_cgroup_hierarchy", 1)); - test_is_wanted_print_one(false); - - ASSERT_OK_ERRNO(setenv("SYSTEMD_PROC_CMDLINE", - "systemd.unified_cgroup_hierarchy=0", 1)); - test_is_wanted_print_one(false); - - ASSERT_OK_ERRNO(setenv("SYSTEMD_PROC_CMDLINE", - "systemd.unified_cgroup_hierarchy=0 " - "systemd.legacy_systemd_cgroup_controller", 1)); - test_is_wanted_print_one(false); - - ASSERT_OK_ERRNO(setenv("SYSTEMD_PROC_CMDLINE", - "systemd.unified_cgroup_hierarchy=0 " - "systemd.legacy_systemd_cgroup_controller=0", 1)); - test_is_wanted_print_one(false); - - /* cgroup_no_v1=all implies unified cgroup hierarchy, unless otherwise - * explicitly specified. */ - ASSERT_OK_ERRNO(setenv("SYSTEMD_PROC_CMDLINE", - "cgroup_no_v1=all", 1)); - test_is_wanted_print_one(false); - - ASSERT_OK_ERRNO(setenv("SYSTEMD_PROC_CMDLINE", - "cgroup_no_v1=all " - "systemd.unified_cgroup_hierarchy=0", 1)); - test_is_wanted_print_one(false); -} - -static int intro(void) { - if (access("/proc/cmdline", R_OK) < 0 && ERRNO_IS_PRIVILEGE(errno)) - return log_tests_skipped("can't read /proc/cmdline"); - - return EXIT_SUCCESS; -} - -DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro); diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c index 18bc998a39..32505f8293 100644 --- a/src/test/test-cgroup-util.c +++ b/src/test/test-cgroup-util.c @@ -375,28 +375,6 @@ TEST(mask_supported, .sd_booted = true) { yes_no(m & CGROUP_CONTROLLER_TO_MASK(c))); } -TEST(is_cgroup_fs, .sd_booted = true) { - struct statfs sfs; - assert_se(statfs("/sys/fs/cgroup", &sfs) == 0); - if (is_temporary_fs(&sfs)) - assert_se(statfs("/sys/fs/cgroup/systemd", &sfs) == 0); - assert_se(is_cgroup_fs(&sfs)); -} - -TEST(fd_is_cgroup_fs, .sd_booted = true) { - int fd; - - fd = open("/sys/fs/cgroup", O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); - assert_se(fd >= 0); - if (fd_is_temporary_fs(fd)) { - fd = safe_close(fd); - fd = open("/sys/fs/cgroup/systemd", O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); - assert_se(fd >= 0); - } - assert_se(fd_is_cgroup_fs(fd)); - fd = safe_close(fd); -} - TEST(cg_tests) { int all, hybrid, systemd, r; diff --git a/src/test/test-taint.c b/src/test/test-taint.c index d9aa79dca5..784885824c 100644 --- a/src/test/test-taint.c +++ b/src/test/test-taint.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "clock-util.h" #include "taint.h" #include "tests.h" @@ -8,7 +9,7 @@ TEST(taint_string) { assert_se(a); log_debug("taint string: '%s'", a); - assert_se(!!strstr(a, "cgroupsv1") == (cg_all_unified() == 0)); + assert_se(!!strstr(a, "local-hwclock") == (clock_is_localtime(NULL) > 0)); } DEFINE_TEST_MAIN(LOG_DEBUG);