LCOV - code coverage report
Current view: top level - nspawn - nspawn-cgroup.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 314 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 12 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 504 0.0 %

           Branch data     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