LCOV - code coverage report
Current view: top level - nspawn - nspawn-cgroup.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 314 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 12 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <sys/mount.h>
       4             : 
       5             : #include "alloc-util.h"
       6             : #include "fd-util.h"
       7             : #include "fileio.h"
       8             : #include "format-util.h"
       9             : #include "fs-util.h"
      10             : #include "mkdir.h"
      11             : #include "mount-util.h"
      12             : #include "mountpoint-util.h"
      13             : #include "nspawn-cgroup.h"
      14             : #include "nspawn-mount.h"
      15             : #include "path-util.h"
      16             : #include "rm-rf.h"
      17             : #include "string-util.h"
      18             : #include "strv.h"
      19             : #include "user-util.h"
      20             : #include "util.h"
      21             : 
      22           0 : static int chown_cgroup_path(const char *path, uid_t uid_shift) {
      23           0 :         _cleanup_close_ int fd = -1;
      24             :         const char *fn;
      25             : 
      26           0 :         fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
      27           0 :         if (fd < 0)
      28           0 :                 return -errno;
      29             : 
      30           0 :         FOREACH_STRING(fn,
      31             :                        ".",
      32             :                        "cgroup.clone_children",
      33             :                        "cgroup.controllers",
      34             :                        "cgroup.events",
      35             :                        "cgroup.procs",
      36             :                        "cgroup.stat",
      37             :                        "cgroup.subtree_control",
      38             :                        "cgroup.threads",
      39             :                        "notify_on_release",
      40             :                        "tasks")
      41           0 :                 if (fchownat(fd, fn, uid_shift, uid_shift, 0) < 0)
      42           0 :                         log_full_errno(errno == ENOENT ? LOG_DEBUG :  LOG_WARNING, errno,
      43             :                                        "Failed to chown \"%s/%s\", ignoring: %m", path, fn);
      44             : 
      45           0 :         return 0;
      46             : }
      47             : 
      48           0 : int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
      49           0 :         _cleanup_free_ char *path = NULL, *fs = NULL;
      50             :         int r;
      51             : 
      52           0 :         r = cg_pid_get_path(NULL, pid, &path);
      53           0 :         if (r < 0)
      54           0 :                 return log_error_errno(r, "Failed to get container cgroup path: %m");
      55             : 
      56           0 :         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs);
      57           0 :         if (r < 0)
      58           0 :                 return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
      59             : 
      60           0 :         r = chown_cgroup_path(fs, uid_shift);
      61           0 :         if (r < 0)
      62           0 :                 return log_error_errno(r, "Failed to chown() cgroup %s: %m", fs);
      63             : 
      64           0 :         if (unified_requested == CGROUP_UNIFIED_SYSTEMD || (unified_requested == CGROUP_UNIFIED_NONE && cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0)) {
      65           0 :                 _cleanup_free_ char *lfs = NULL;
      66             :                 /* Always propagate access rights from unified to legacy controller */
      67             : 
      68           0 :                 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, NULL, &lfs);
      69           0 :                 if (r < 0)
      70           0 :                         return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
      71             : 
      72           0 :                 r = chown_cgroup_path(lfs, uid_shift);
      73           0 :                 if (r < 0)
      74           0 :                         return log_error_errno(r, "Failed to chown() cgroup %s: %m", lfs);
      75             :         }
      76             : 
      77           0 :         return 0;
      78             : }
      79             : 
      80           0 : int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
      81           0 :         _cleanup_free_ char *cgroup = NULL;
      82           0 :         char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1];
      83           0 :         bool undo_mount = false;
      84             :         const char *fn;
      85             :         int r, unified_controller;
      86             : 
      87           0 :         unified_controller = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
      88           0 :         if (unified_controller < 0)
      89           0 :                 return log_error_errno(unified_controller, "Failed to determine whether the systemd hierarchy is unified: %m");
      90           0 :         if ((unified_controller > 0) == (unified_requested >= CGROUP_UNIFIED_SYSTEMD))
      91           0 :                 return 0;
      92             : 
      93             :         /* When the host uses the legacy cgroup setup, but the
      94             :          * container shall use the unified hierarchy, let's make sure
      95             :          * we copy the path from the name=systemd hierarchy into the
      96             :          * unified hierarchy. Similar for the reverse situation. */
      97             : 
      98           0 :         r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
      99           0 :         if (r < 0)
     100           0 :                 return log_error_errno(r, "Failed to get control group of " PID_FMT ": %m", pid);
     101             : 
     102             :         /* In order to access the unified hierarchy we need to mount it */
     103           0 :         if (!mkdtemp(tree))
     104           0 :                 return log_error_errno(errno, "Failed to generate temporary mount point for unified hierarchy: %m");
     105             : 
     106           0 :         if (unified_controller > 0)
     107           0 :                 r = mount_verbose(LOG_ERR, "cgroup", tree, "cgroup",
     108             :                                   MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr");
     109             :         else
     110           0 :                 r = mount_verbose(LOG_ERR, "cgroup", tree, "cgroup2",
     111             :                                   MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
     112           0 :         if (r < 0)
     113           0 :                 goto finish;
     114             : 
     115           0 :         undo_mount = true;
     116             : 
     117             :         /* If nspawn dies abruptly the cgroup hierarchy created below
     118             :          * its unit isn't cleaned up. So, let's remove it
     119             :          * https://github.com/systemd/systemd/pull/4223#issuecomment-252519810 */
     120           0 :         fn = strjoina(tree, cgroup);
     121           0 :         (void) rm_rf(fn, REMOVE_ROOT|REMOVE_ONLY_DIRECTORIES);
     122             : 
     123           0 :         fn = strjoina(tree, cgroup, "/cgroup.procs");
     124             : 
     125           0 :         sprintf(pid_string, PID_FMT, pid);
     126           0 :         r = write_string_file(fn, pid_string, WRITE_STRING_FILE_DISABLE_BUFFER|WRITE_STRING_FILE_MKDIR_0755);
     127           0 :         if (r < 0) {
     128           0 :                 log_error_errno(r, "Failed to move process: %m");
     129           0 :                 goto finish;
     130             :         }
     131             : 
     132           0 :         fn = strjoina(tree, cgroup);
     133           0 :         r = chown_cgroup_path(fn, uid_shift);
     134           0 :         if (r < 0)
     135           0 :                 log_error_errno(r, "Failed to chown() cgroup %s: %m", fn);
     136           0 : finish:
     137           0 :         if (undo_mount)
     138           0 :                 (void) umount_verbose(tree);
     139             : 
     140           0 :         (void) rmdir(tree);
     141           0 :         return r;
     142             : }
     143             : 
     144           0 : int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested) {
     145           0 :         _cleanup_free_ char *cgroup = NULL;
     146             :         CGroupMask supported;
     147             :         const char *payload;
     148             :         int r;
     149             : 
     150           0 :         assert(pid > 1);
     151             : 
     152             :         /* In the unified hierarchy inner nodes may only contain subgroups, but not processes. Hence, if we running in
     153             :          * the unified hierarchy and the container does the same, and we did not create a scope unit for the container
     154             :          * move us and the container into two separate subcgroups.
     155             :          *
     156             :          * Moreover, container payloads such as systemd try to manage the cgroup they run in in full (i.e. including
     157             :          * its attributes), while the host systemd will only delegate cgroups for children of the cgroup created for a
     158             :          * delegation unit, instead of the cgroup itself. This means, if we'd pass on the cgroup allocated from the
     159             :          * host systemd directly to the payload, the host and payload systemd might fight for the cgroup
     160             :          * attributes. Hence, let's insert an intermediary cgroup to cover that case too.
     161             :          *
     162             :          * Note that we only bother with the main hierarchy here, not with any secondary ones. On the unified setup
     163             :          * that's fine because there's only one hierarchy anyway and controllers are enabled directly on it. On the
     164             :          * legacy setup, this is fine too, since delegation of controllers is generally not safe there, hence we won't
     165             :          * do it. */
     166             : 
     167           0 :         r = cg_mask_supported(&supported);
     168           0 :         if (r < 0)
     169           0 :                 return log_error_errno(r, "Failed to determine supported controllers: %m");
     170             : 
     171           0 :         if (keep_unit)
     172           0 :                 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup);
     173             :         else
     174           0 :                 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
     175           0 :         if (r < 0)
     176           0 :                 return log_error_errno(r, "Failed to get our control group: %m");
     177             : 
     178           0 :         payload = strjoina(cgroup, "/payload");
     179           0 :         r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, payload, pid);
     180           0 :         if (r < 0)
     181           0 :                 return log_error_errno(r, "Failed to create %s subcgroup: %m", payload);
     182             : 
     183           0 :         if (keep_unit) {
     184             :                 const char *supervisor;
     185             : 
     186           0 :                 supervisor = strjoina(cgroup, "/supervisor");
     187           0 :                 r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, supervisor, 0);
     188           0 :                 if (r < 0)
     189           0 :                         return log_error_errno(r, "Failed to create %s subcgroup: %m", supervisor);
     190             :         }
     191             : 
     192             :         /* Try to enable as many controllers as possible for the new payload. */
     193           0 :         (void) cg_enable_everywhere(supported, supported, cgroup, NULL);
     194           0 :         return 0;
     195             : }
     196             : 
     197             : /* Retrieve existing subsystems. This function is called in a new cgroup
     198             :  * namespace.
     199             :  */
     200           0 : static int get_process_controllers(Set **ret) {
     201           0 :         _cleanup_set_free_free_ Set *controllers = NULL;
     202           0 :         _cleanup_fclose_ FILE *f = NULL;
     203             :         int r;
     204             : 
     205           0 :         assert(ret);
     206             : 
     207           0 :         controllers = set_new(&string_hash_ops);
     208           0 :         if (!controllers)
     209           0 :                 return -ENOMEM;
     210             : 
     211           0 :         f = fopen("/proc/self/cgroup", "re");
     212           0 :         if (!f)
     213           0 :                 return errno == ENOENT ? -ESRCH : -errno;
     214             : 
     215           0 :         for (;;) {
     216           0 :                 _cleanup_free_ char *line = NULL;
     217             :                 char *e, *l;
     218             : 
     219           0 :                 r = read_line(f, LONG_LINE_MAX, &line);
     220           0 :                 if (r < 0)
     221           0 :                         return r;
     222           0 :                 if (r == 0)
     223           0 :                         break;
     224             : 
     225           0 :                 l = strchr(line, ':');
     226           0 :                 if (!l)
     227           0 :                         continue;
     228             : 
     229           0 :                 l++;
     230           0 :                 e = strchr(l, ':');
     231           0 :                 if (!e)
     232           0 :                         continue;
     233             : 
     234           0 :                 *e = 0;
     235             : 
     236           0 :                 if (STR_IN_SET(l, "", "name=systemd", "name=unified"))
     237           0 :                         continue;
     238             : 
     239           0 :                 r = set_put_strdup(controllers, l);
     240           0 :                 if (r < 0)
     241           0 :                         return r;
     242             :         }
     243             : 
     244           0 :         *ret = TAKE_PTR(controllers);
     245             : 
     246           0 :         return 0;
     247             : }
     248             : 
     249           0 : static int mount_legacy_cgroup_hierarchy(
     250             :                 const char *dest,
     251             :                 const char *controller,
     252             :                 const char *hierarchy,
     253             :                 bool read_only) {
     254             : 
     255             :         const char *to, *fstype, *opts;
     256             :         int r;
     257             : 
     258           0 :         to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy);
     259             : 
     260           0 :         r = path_is_mount_point(to, dest, 0);
     261           0 :         if (r < 0 && r != -ENOENT)
     262           0 :                 return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
     263           0 :         if (r > 0)
     264           0 :                 return 0;
     265             : 
     266           0 :         (void) mkdir_p(to, 0755);
     267             : 
     268             :         /* The superblock mount options of the mount point need to be
     269             :          * identical to the hosts', and hence writable... */
     270           0 :         if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID)) {
     271           0 :                 fstype = "cgroup2";
     272           0 :                 opts = NULL;
     273           0 :         } else if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_LEGACY)) {
     274           0 :                 fstype = "cgroup";
     275           0 :                 opts = "none,name=systemd,xattr";
     276             :         } else {
     277           0 :                 fstype = "cgroup";
     278           0 :                 opts = controller;
     279             :         }
     280             : 
     281           0 :         r = mount_verbose(LOG_ERR, "cgroup", to, fstype, MS_NOSUID|MS_NOEXEC|MS_NODEV, opts);
     282           0 :         if (r < 0)
     283           0 :                 return r;
     284             : 
     285             :         /* ... hence let's only make the bind mount read-only, not the superblock. */
     286           0 :         if (read_only) {
     287           0 :                 r = mount_verbose(LOG_ERR, NULL, to, NULL,
     288             :                                   MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
     289           0 :                 if (r < 0)
     290           0 :                         return r;
     291             :         }
     292             : 
     293           0 :         return 1;
     294             : }
     295             : 
     296             : /* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */
     297           0 : static int mount_legacy_cgns_supported(
     298             :                 const char *dest,
     299             :                 CGroupUnified unified_requested,
     300             :                 bool userns,
     301             :                 uid_t uid_shift,
     302             :                 uid_t uid_range,
     303             :                 const char *selinux_apifs_context) {
     304             : 
     305           0 :         _cleanup_set_free_free_ Set *controllers = NULL;
     306           0 :         const char *cgroup_root = "/sys/fs/cgroup", *c;
     307             :         int r;
     308             : 
     309           0 :         (void) mkdir_p(cgroup_root, 0755);
     310             : 
     311             :         /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
     312           0 :         r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
     313           0 :         if (r < 0)
     314           0 :                 return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
     315           0 :         if (r == 0) {
     316           0 :                 _cleanup_free_ char *options = NULL;
     317             : 
     318             :                 /* When cgroup namespaces are enabled and user namespaces are
     319             :                  * used then the mount of the cgroupfs is done *inside* the new
     320             :                  * user namespace. We're root in the new user namespace and the
     321             :                  * kernel will happily translate our uid/gid to the correct
     322             :                  * uid/gid as seen from e.g. /proc/1/mountinfo. So we simply
     323             :                  * pass uid 0 and not uid_shift to tmpfs_patch_options().
     324             :                  */
     325           0 :                 r = tmpfs_patch_options("mode=755", 0, selinux_apifs_context, &options);
     326           0 :                 if (r < 0)
     327           0 :                         return log_oom();
     328             : 
     329           0 :                 r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
     330             :                                   MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
     331           0 :                 if (r < 0)
     332           0 :                         return r;
     333             :         }
     334             : 
     335           0 :         r = cg_all_unified();
     336           0 :         if (r < 0)
     337           0 :                 return r;
     338           0 :         if (r > 0)
     339           0 :                 goto skip_controllers;
     340             : 
     341           0 :         r = get_process_controllers(&controllers);
     342           0 :         if (r < 0)
     343           0 :                 return log_error_errno(r, "Failed to determine cgroup controllers: %m");
     344             : 
     345           0 :         for (;;) {
     346           0 :                 _cleanup_free_ const char *controller = NULL;
     347             : 
     348           0 :                 controller = set_steal_first(controllers);
     349           0 :                 if (!controller)
     350           0 :                         break;
     351             : 
     352           0 :                 r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns);
     353           0 :                 if (r < 0)
     354           0 :                         return r;
     355             : 
     356             :                 /* When multiple hierarchies are co-mounted, make their
     357             :                  * constituting individual hierarchies a symlink to the
     358             :                  * co-mount.
     359             :                  */
     360           0 :                 c = controller;
     361           0 :                 for (;;) {
     362           0 :                         _cleanup_free_ char *target = NULL, *tok = NULL;
     363             : 
     364           0 :                         r = extract_first_word(&c, &tok, ",", 0);
     365           0 :                         if (r < 0)
     366           0 :                                 return log_error_errno(r, "Failed to extract co-mounted cgroup controller: %m");
     367           0 :                         if (r == 0)
     368           0 :                                 break;
     369             : 
     370           0 :                         if (streq(controller, tok))
     371           0 :                                 break;
     372             : 
     373           0 :                         target = path_join("/sys/fs/cgroup/", tok);
     374           0 :                         if (!target)
     375           0 :                                 return log_oom();
     376             : 
     377           0 :                         r = symlink_idempotent(controller, target, false);
     378           0 :                         if (r == -EINVAL)
     379           0 :                                 return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
     380           0 :                         if (r < 0)
     381           0 :                                 return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
     382             :                 }
     383             :         }
     384             : 
     385           0 : skip_controllers:
     386           0 :         if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
     387           0 :                 r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
     388           0 :                 if (r < 0)
     389           0 :                         return r;
     390             :         }
     391             : 
     392           0 :         r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
     393           0 :         if (r < 0)
     394           0 :                 return r;
     395             : 
     396           0 :         if (!userns)
     397           0 :                 return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
     398             :                                      MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
     399             : 
     400           0 :         return 0;
     401             : }
     402             : 
     403             : /* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */
     404           0 : static int mount_legacy_cgns_unsupported(
     405             :                 const char *dest,
     406             :                 CGroupUnified unified_requested,
     407             :                 bool userns,
     408             :                 uid_t uid_shift,
     409             :                 uid_t uid_range,
     410             :                 const char *selinux_apifs_context) {
     411             : 
     412           0 :         _cleanup_set_free_free_ Set *controllers = NULL;
     413             :         const char *cgroup_root;
     414             :         int r;
     415             : 
     416           0 :         cgroup_root = prefix_roota(dest, "/sys/fs/cgroup");
     417             : 
     418           0 :         (void) mkdir_p(cgroup_root, 0755);
     419             : 
     420             :         /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
     421           0 :         r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
     422           0 :         if (r < 0)
     423           0 :                 return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
     424           0 :         if (r == 0) {
     425           0 :                 _cleanup_free_ char *options = NULL;
     426             : 
     427           0 :                 r = tmpfs_patch_options("mode=755", uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &options);
     428           0 :                 if (r < 0)
     429           0 :                         return log_oom();
     430             : 
     431           0 :                 r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
     432             :                                   MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
     433           0 :                 if (r < 0)
     434           0 :                         return r;
     435             :         }
     436             : 
     437           0 :         r = cg_all_unified();
     438           0 :         if (r < 0)
     439           0 :                 return r;
     440           0 :         if (r > 0)
     441           0 :                 goto skip_controllers;
     442             : 
     443           0 :         r = cg_kernel_controllers(&controllers);
     444           0 :         if (r < 0)
     445           0 :                 return log_error_errno(r, "Failed to determine cgroup controllers: %m");
     446             : 
     447           0 :         for (;;) {
     448           0 :                 _cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL;
     449             : 
     450           0 :                 controller = set_steal_first(controllers);
     451           0 :                 if (!controller)
     452           0 :                         break;
     453             : 
     454           0 :                 origin = path_join("/sys/fs/cgroup/", controller);
     455           0 :                 if (!origin)
     456           0 :                         return log_oom();
     457             : 
     458           0 :                 r = readlink_malloc(origin, &combined);
     459           0 :                 if (r == -EINVAL) {
     460             :                         /* Not a symbolic link, but directly a single cgroup hierarchy */
     461             : 
     462           0 :                         r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true);
     463           0 :                         if (r < 0)
     464           0 :                                 return r;
     465             : 
     466           0 :                 } else if (r < 0)
     467           0 :                         return log_error_errno(r, "Failed to read link %s: %m", origin);
     468             :                 else {
     469           0 :                         _cleanup_free_ char *target = NULL;
     470             : 
     471           0 :                         target = path_join(dest, origin);
     472           0 :                         if (!target)
     473           0 :                                 return log_oom();
     474             : 
     475             :                         /* A symbolic link, a combination of controllers in one hierarchy */
     476             : 
     477           0 :                         if (!filename_is_valid(combined)) {
     478           0 :                                 log_warning("Ignoring invalid combined hierarchy %s.", combined);
     479           0 :                                 continue;
     480             :                         }
     481             : 
     482           0 :                         r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true);
     483           0 :                         if (r < 0)
     484           0 :                                 return r;
     485             : 
     486           0 :                         r = symlink_idempotent(combined, target, false);
     487           0 :                         if (r == -EINVAL)
     488           0 :                                 return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
     489           0 :                         if (r < 0)
     490           0 :                                 return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
     491             :                 }
     492             :         }
     493             : 
     494           0 : skip_controllers:
     495           0 :         if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
     496           0 :                 r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
     497           0 :                 if (r < 0)
     498           0 :                         return r;
     499             :         }
     500             : 
     501           0 :         r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
     502           0 :         if (r < 0)
     503           0 :                 return r;
     504             : 
     505           0 :         return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
     506             :                              MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
     507             : }
     508             : 
     509           0 : static int mount_unified_cgroups(const char *dest) {
     510             :         const char *p;
     511             :         int r;
     512             : 
     513           0 :         assert(dest);
     514             : 
     515           0 :         p = prefix_roota(dest, "/sys/fs/cgroup");
     516             : 
     517           0 :         (void) mkdir_p(p, 0755);
     518             : 
     519           0 :         r = path_is_mount_point(p, dest, AT_SYMLINK_FOLLOW);
     520           0 :         if (r < 0)
     521           0 :                 return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p);
     522           0 :         if (r > 0) {
     523           0 :                 p = prefix_roota(dest, "/sys/fs/cgroup/cgroup.procs");
     524           0 :                 if (access(p, F_OK) >= 0)
     525           0 :                         return 0;
     526           0 :                 if (errno != ENOENT)
     527           0 :                         return log_error_errno(errno, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p);
     528             : 
     529           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     530             :                                        "%s is already mounted but not a unified cgroup hierarchy. Refusing.", p);
     531             :         }
     532             : 
     533           0 :         return mount_verbose(LOG_ERR, "cgroup", p, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
     534             : }
     535             : 
     536           0 : int mount_cgroups(
     537             :                 const char *dest,
     538             :                 CGroupUnified unified_requested,
     539             :                 bool userns,
     540             :                 uid_t uid_shift,
     541             :                 uid_t uid_range,
     542             :                 const char *selinux_apifs_context,
     543             :                 bool use_cgns) {
     544             : 
     545           0 :         if (unified_requested >= CGROUP_UNIFIED_ALL)
     546           0 :                 return mount_unified_cgroups(dest);
     547           0 :         if (use_cgns)
     548           0 :                 return mount_legacy_cgns_supported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
     549             : 
     550           0 :         return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
     551             : }
     552             : 
     553           0 : static int mount_systemd_cgroup_writable_one(const char *root, const char *own) {
     554             :         int r;
     555             : 
     556           0 :         assert(root);
     557           0 :         assert(own);
     558             : 
     559             :         /* Make our own cgroup a (writable) bind mount */
     560           0 :         r = mount_verbose(LOG_ERR, own, own, NULL, MS_BIND, NULL);
     561           0 :         if (r < 0)
     562           0 :                 return r;
     563             : 
     564             :         /* And then remount the systemd cgroup root read-only */
     565           0 :         return mount_verbose(LOG_ERR, NULL, root, NULL,
     566             :                              MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
     567             : }
     568             : 
     569           0 : int mount_systemd_cgroup_writable(
     570             :                 const char *dest,
     571             :                 CGroupUnified unified_requested) {
     572             : 
     573           0 :         _cleanup_free_ char *own_cgroup_path = NULL;
     574             :         const char *root, *own;
     575             :         int r;
     576             : 
     577           0 :         assert(dest);
     578             : 
     579           0 :         r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
     580           0 :         if (r < 0)
     581           0 :                 return log_error_errno(r, "Failed to determine our own cgroup path: %m");
     582             : 
     583             :         /* If we are living in the top-level, then there's nothing to do... */
     584           0 :         if (path_equal(own_cgroup_path, "/"))
     585           0 :                 return 0;
     586             : 
     587           0 :         if (unified_requested >= CGROUP_UNIFIED_ALL) {
     588             : 
     589           0 :                 root = prefix_roota(dest, "/sys/fs/cgroup");
     590           0 :                 own = strjoina(root, own_cgroup_path);
     591             : 
     592             :         } else {
     593             : 
     594           0 :                 if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
     595           0 :                         root = prefix_roota(dest, "/sys/fs/cgroup/unified");
     596           0 :                         own = strjoina(root, own_cgroup_path);
     597             : 
     598           0 :                         r = mount_systemd_cgroup_writable_one(root, own);
     599           0 :                         if (r < 0)
     600           0 :                                 return r;
     601             :                 }
     602             : 
     603           0 :                 root = prefix_roota(dest, "/sys/fs/cgroup/systemd");
     604           0 :                 own = strjoina(root, own_cgroup_path);
     605             :         }
     606             : 
     607           0 :         return mount_systemd_cgroup_writable_one(root, own);
     608             : }

Generated by: LCOV version 1.14