summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2017-06-14 13:31:52 -0400
committerLuke Shumaker <lukeshu@lukeshu.com>2017-06-16 18:52:09 -0400
commit952a18bb379b00dfee0f8a8561556e707d96f931 (patch)
tree96d78ea7b5dfbff73ceee7034393331d1d900de2
parenta2c5a0f3e0f005899fd6de214ff0525b7415cb8a (diff)
nspawn: Add a special "INHERIT" cgroup mode
-rw-r--r--Makefile.am2
-rw-r--r--src/basic/cgroup-util.h1
-rw-r--r--src/nspawn/nspawn-cgroup.c116
-rw-r--r--src/nspawn/nspawn.c16
4 files changed, 126 insertions, 9 deletions
diff --git a/Makefile.am b/Makefile.am
index 35ec60f736..4f1d729a74 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3113,6 +3113,7 @@ systemd_nspawn_CFLAGS = \
$(AM_CFLAGS) \
$(ACL_CFLAGS) \
$(BLKID_CFLAGS) \
+ $(MOUNT_CFLAGS) \
$(SECCOMP_CFLAGS) \
$(SELINUX_CFLAGS)
@@ -3120,6 +3121,7 @@ systemd_nspawn_LDADD = \
libsystemd-shared.la \
$(ACL_LIBS) \
$(BLKID_LIBS) \
+ $(MOUNT_LIBS) \
$(SECCOMP_LIBS) \
$(SELINUX_LIBS)
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
index 0e5b41103e..3406e0b4fb 100644
--- a/src/basic/cgroup-util.h
+++ b/src/basic/cgroup-util.h
@@ -118,6 +118,7 @@ static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) {
#define DEFAULT_USER_TASKS_MAX_PERCENTAGE 33U /* 33% of PIDs, 10813 on default settings */
typedef enum CGroupUnified {
+ CGROUP_UNIFIED_INHERIT = -2, /* special case only used by nspawn */
CGROUP_UNIFIED_UNKNOWN = -1,
CGROUP_UNIFIED_NONE = 0, /* Both systemd and controllers on legacy */
CGROUP_UNIFIED_SYSTEMD = 1, /* Only systemd on unified */
diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c
index dafeb4a1ce..2229f4b42a 100644
--- a/src/nspawn/nspawn-cgroup.c
+++ b/src/nspawn/nspawn-cgroup.c
@@ -18,8 +18,11 @@
***/
#include <sys/mount.h>
+#include <libmount.h>
#include "alloc-util.h"
+#include "dirent-util.h"
+#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
@@ -263,13 +266,24 @@ int cgroup_setup(pid_t pid, CGroupUnified outer_cgver, CGroupUnified inner_cgver
_cleanup_set_free_ Set *peers = NULL;
int r;
- if ((outer_cgver >= CGROUP_UNIFIED_SYSTEMD) != (inner_cgver >= CGROUP_UNIFIED_SYSTEMD)) {
+ if (outer_cgver == CGROUP_UNIFIED_UNKNOWN && inner_cgver != CGROUP_UNIFIED_INHERIT) {
+ log_error("Specifying a cgroup version is only supported when running on a systemd host");
+ return -EINVAL;
+ }
+
+ if ((inner_cgver != CGROUP_UNIFIED_INHERIT) &&
+ ((outer_cgver >= CGROUP_UNIFIED_SYSTEMD) != (inner_cgver >= CGROUP_UNIFIED_SYSTEMD))) {
/* sync the name=systemd hierarchy with the unified hierarchy */
r = sync_cgroup(pid, outer_cgver, inner_cgver, uid_shift);
if (r < 0)
return r;
}
+ /* The rest applies only to the unified hierarchy (which if is in use, the setup is known), or the systemd
+ * hierarchy. */
+ if (outer_cgver == CGROUP_UNIFIED_UNKNOWN)
+ return 0;
+
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
if (r < 0)
return log_error_errno(r, "Failed to get host cgroup of the container: %m");
@@ -312,6 +326,104 @@ int cgroup_setup(pid_t pid, CGroupUnified outer_cgver, CGroupUnified inner_cgver
/********************************************************************/
+static int cgroup_decide_mounts_inherit(CGMounts *ret_mounts) {
+ _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
+ _cleanup_(cgroup_free_mounts) CGMounts mounts = {};
+ int r;
+
+ proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
+ if (!proc_self_mountinfo)
+ return -errno;
+
+ for (;;) {
+ _cleanup_free_ char *escmountpoint = NULL, *mountpoint = NULL, *fstype = NULL, *superopts = NULL, *fsopts = NULL;
+ char *name;
+ CGMountType type;
+ int k;
+
+ k = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) per-mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) separator */
+ "%ms " /* (9) file system type */
+ "%*s" /* (10) mount source */
+ "%ms" /* (11) per-superblock options */
+ "%*[^\n]", /* some rubbish at the end */
+ &escmountpoint,
+ &fstype,
+ &superopts
+ );
+ if (k != 3) {
+ if (k == EOF)
+ break;
+
+ continue;
+ }
+
+ r = cunescape(escmountpoint, UNESCAPE_RELAX, &mountpoint);
+ if (r < 0)
+ return r;
+
+ name = path_startswith(mountpoint, "/sys/fs/cgroup");
+ if (!name)
+ continue;
+
+ if (!filename_is_valid(name) && !isempty(name))
+ continue;
+
+ if (streq(fstype, "tmpfs"))
+ type = CGMOUNT_TMPFS;
+ else if(streq(fstype, "cgroup"))
+ type = CGMOUNT_CGROUP1;
+ else if (streq(fstype, "cgroup2"))
+ type = CGMOUNT_CGROUP2;
+ else
+ continue;
+
+ r = mnt_split_optstr(superopts, NULL, NULL, &fsopts, 0, 0);
+ if (r < 0)
+ return r;
+
+ if (!cgmount_add(&mounts, type, fsopts, name)) {
+ return -ENOMEM;
+ }
+
+ if (type == CGMOUNT_TMPFS) {
+ _cleanup_closedir_ DIR *dir;
+ struct dirent *entry;
+
+ dir = opendir(mountpoint);
+ if (!dir)
+ return log_error_errno(errno, "Failed to open directory %s: %m", mountpoint);
+
+ FOREACH_DIRENT(entry, dir, break) {
+ _cleanup_free_ char *target = NULL;
+ r = dirent_ensure_type(dir, entry);
+ if (r < 0)
+ return r;
+ if (entry->d_type != DT_LNK)
+ continue;
+ r = readlinkat_malloc(dirfd(dir), entry->d_name, &target);
+ if (r < 0)
+ return r;
+ if (!cgmount_add(&mounts, CGMOUNT_SYMLINK, target, entry->d_name))
+ return -ENOMEM;
+ }
+ }
+ }
+
+ *ret_mounts = mounts;
+ mounts.mounts = NULL;
+ mounts.n = 0;
+
+ return 0;
+}
+
/* Retrieve a list of cgroup v1 hierarchies. */
static int get_v1_hierarchies(Set *subsystems) {
_cleanup_fclose_ FILE *f = NULL;
@@ -515,6 +627,8 @@ int cgroup_decide_mounts(
CGroupUnified outer_cgver, CGroupUnified inner_cgver,
bool use_cgns) {
switch (inner_cgver) {
+ case CGROUP_UNIFIED_INHERIT:
+ return cgroup_decide_mounts_inherit(ret_mounts);
case CGROUP_UNIFIED_NONE:
case CGROUP_UNIFIED_SYSTEMD:
if (use_cgns)
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 94c7eea9b7..3a39b4a6f5 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -327,8 +327,12 @@ static int pick_cgroup_version(const char *directory, CGroupUnified outer) {
* the container doesn't have a new enough systemd (detected
* by checking libsystemd-shared). */
switch (outer) {
+ default:
+ case CGROUP_UNIFIED_INHERIT:
+ assert_not_reached("Invalid host cgroup version");
+ return -EINVAL;
case CGROUP_UNIFIED_UNKNOWN:
- assert_not_reached("Unknown host cgroup version");
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_INHERIT;
break;
case CGROUP_UNIFIED_NONE: /* cgroup v1-sd */
arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
@@ -4058,6 +4062,9 @@ int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
cg_unified_flush();
+ r = cg_version(&outer_cgver);
+ if (r < 0)
+ outer_cgver = CGROUP_UNIFIED_UNKNOWN;
/* Make sure rename_process() in the stub init process can work */
saved_argv = argv;
@@ -4072,13 +4079,6 @@ int main(int argc, char *argv[]) {
r = -EPERM;
goto finish;
}
-
- r = cg_version(&outer_cgver);
- if (r < 0) {
- log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m");
- goto finish;
- }
-
r = determine_names();
if (r < 0)
goto finish;