[lxc-devel] [RFC PATCH 1/1] Handle running on unified hierarchy

Serge Hallyn serge.hallyn at ubuntu.com
Tue Jun 16 14:34:57 UTC 2015


The unified hierachy has some very different behavior from legacy hierachies.
Perhaps most intrusively, tasks may only exist in leaf nodes.  To deal with
this, any Create request will create a cgroup with all controllers enabled
that were enabled in the parent, while MovePid will move a process into a
child of the requested cgroup with no enabled controllers, called ".cgm_leaf".

We declare the current cgmanager API to be a legacy API which may sit on
top of either the legacy of unified hierarchy.  A new v2 API, designed around
the unified hierarchy, will be introduced alongside the legacy API later.

If unified hierarchy is mounted, recognize it as such.  We also in that case pin
the controllers into the unified hierarchy (in a .cgpin directory), as we don't
want the mechanism for dealing with the controller to change mid-way.

When calculating or showing a task's cgroup, drop the ".cgm_leaf" part.

create: copy the parent's list of active controllers into the newly
created one.  Otherwise the controllers cannot be used.

update movepid to handle unified hierarchy.  this involves:
  creating the leafdir if it doesn't already exists
  chowning the leafdir contents to match the parent dir contents
  (don't enable any controllers in the leafdir)

ignore error chowning tasks file, since it won't exist in unified hierarchy

chown: also chown leaf dir if it exists

handle leafdir in remove

use NIH_MUST to avoid some null checks

set_value: set cgm_leaf value as well as parent's

Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>
---
 cgmanager.c |  54 ++++++-
 fs.c        | 489 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 fs.h        |  11 +-
 3 files changed, 518 insertions(+), 36 deletions(-)

diff --git a/cgmanager.c b/cgmanager.c
index 6219cba..ead4284 100644
--- a/cgmanager.c
+++ b/cgmanager.c
@@ -132,8 +132,13 @@ int per_ctrl_move_pid_main(const char *controller, const char *cgroup, struct uc
 		struct ucred r, struct ucred v, bool escape)
 {
 	char rcgpath[MAXPATHLEN], path[MAXPATHLEN];
+	bool unified = false;
 	FILE *f;
 	pid_t query = r.pid;
+	size_t maxlen;
+
+	if (is_unified_controller(controller))
+		unified = true;
 
 	// Get r's current cgroup in rcgpath
 	if (escape)
@@ -150,8 +155,14 @@ int per_ctrl_move_pid_main(const char *controller, const char *cgroup, struct uc
 		return -1;
 	}
 
-	/* rcgpath + / + cgroup + /tasks + \0 */
-	if (strlen(rcgpath) + strlen(cgroup) > MAXPATHLEN - 8) {
+	if (unified) {
+		/* rcgpath + / + cgroup + "/.cgm_leaf/cgroup.procs" + \0 */
+		maxlen = strlen(rcgpath) + strlen(cgroup) + strlen(U_LEAF "/cgroup.procs") + 2;
+	} else {
+		/* rcgpath + / + cgroup + /tasks + \0 */
+		maxlen = strlen(rcgpath) + strlen(cgroup) + 8;
+	}
+	if (maxlen > MAXPATHLEN) {
 		nih_error("%s: Path name too long", __func__);
 		return -1;
 	}
@@ -168,8 +179,17 @@ int per_ctrl_move_pid_main(const char *controller, const char *cgroup, struct uc
 			r.pid, r.uid, r.gid, path);
 		return -1;
 	}
+
 	// is r allowed to write to tasks file?
-	strncat(path, "/tasks", MAXPATHLEN-1);
+	if (unified) {
+		if (!ensure_leafdir(controller, path))
+			return -1;
+		strcat(path, U_LEAF);
+		strcat(path, "/cgroup.procs");
+	} else {
+		strncat(path, "/tasks", MAXPATHLEN-1);
+	}
+
 	if (!may_access(r.pid, r.uid, r.gid, path, O_WRONLY)) {
 		nih_error("%s: pid %d (uid %u gid %u) may not write to %s", __func__,
 			r.pid, r.uid, r.gid, path);
@@ -266,6 +286,7 @@ int do_create_main(const char *controller, const char *cgroup, struct ucred p,
 	size_t cgroup_len;
 	char *p1, *p2, oldp2;
 
+nih_info("%s: controller is %s\n", __func__, controller);
 	*existed = 1;
 	// Get r's current cgroup in rcgpath
 	if (!compute_proxy_cgroup(r.pid, controller, "", rcgpath, &depth)) {
@@ -324,6 +345,12 @@ int do_create_main(const char *controller, const char *cgroup, struct ucred p,
 			nih_error("%s: failed to create %s", __func__, path);
 			return -2;
 		}
+		if (!unified_copy_controllers(controller, path)) {
+			nih_error("%s: Failed to set cg controllers on %s", __func__,
+					path);
+			rmdir(path);
+			return -1;
+		}
 		if (!chown_cgroup_path(path, r.uid, r.gid, true)) {
 			nih_error("%s: Failed to change ownership on %s to %u:%u", __func__,
 				path, r.uid, r.gid);
@@ -367,10 +394,13 @@ int create_main(const char *controller, const char *cgroup, struct ucred p,
 	if (strcmp(controller, "all") == 0) {
 		if (!all_controllers)
 			return 0;
+nih_info("%s: all_controllers is %s\n", __func__, all_controllers);
 		c = NIH_MUST( nih_strdup(NULL, all_controllers) );
 	} else {
 		c = NIH_MUST( nih_strdup(NULL, controller) );
+nih_info("c is %s before prune", c);
 		do_prune_comounts(c);
+nih_info("c is %s after prune", c);
 	}
 	tok = strtok(c, ",");
 	while (tok) {
@@ -430,6 +460,14 @@ int do_chown_main(const char *controller, const char *cgroup, struct ucred p,
 			path, v.uid, v.gid);
 		return -2;
 	}
+	if (is_unified_controller(controller)) {
+		NIH_MUST( nih_strcat(&path, NULL, U_LEAF) );
+		if (dir_exists(path) && !chown_cgroup_path(path, v.uid, v.gid, false)) {
+			nih_warn("%s: Failed to chown leaf directory for %s to %u:%u",
+				__func__, path, v.uid, v.gid);
+			return -2;
+		}
+	}
 
 	return 0;
 }
@@ -678,7 +716,7 @@ int set_value_main(char *controller, const char *cgroup,
 	}
 
 	/* read and return the value */
-	if (!set_value(path, value)) {
+	if (!set_value(controller, path, value)) {
 		nih_error("%s: Failed to set value %s to %s", __func__, path, value);
 		return -1;
 	}
@@ -801,6 +839,14 @@ int do_remove_main(const char *controller, const char *cgroup, struct ucred p,
 		return -2;
 	}
 
+	if (is_unified_controller(controller)) {
+		nih_local char *fpath = NULL;
+		fpath = NIH_MUST( nih_sprintf(NULL, "%s%s", working, U_LEAF) );
+		if (rmdir(fpath) < 0) {
+			nih_error("%s: Failed to remove %s: %s", __func__, fpath, strerror(errno));
+			return errno == EPERM ? -2 : -1;
+		}
+	}
 	if (!recursive) {
 		if (rmdir(working) < 0) {
 			nih_error("%s: Failed to remove %s: %s", __func__, working, strerror(errno));
diff --git a/fs.c b/fs.c
index 231e98e..ddfc48f 100644
--- a/fs.c
+++ b/fs.c
@@ -51,6 +51,7 @@
 #include <nih-dbus/dbus_proxy.h>
 
 #include "frontend.h"  // for keys_return_type
+#include "fs.h"        // for #defines
 
 /* defines relating to the release agent */
 #define AGENT SBINDIR "/cgm-release-agent"
@@ -83,6 +84,7 @@ struct controller_mounts {
 	bool premounted;
 	bool visited;
 	bool skip;
+	bool unified;
 };
 
 static struct controller_mounts *all_mounts;
@@ -246,19 +248,10 @@ static bool fill_in_controller(struct controller_mounts *m, char *controller,
 	nih_local char *dest = NULL;
 
 	dest = NIH_MUST( nih_sprintf(NULL, "%s/%s", base_path, src) );
-	m->controller = strdup(controller);
-	if (!m->controller) {
-		nih_fatal("Out of memory mounting controllers");
-		return false;
-	}
+	m->controller = NIH_MUST( strdup(controller) );
 	m->options = NULL;
-	m->path = strdup(dest);
-	m->src = strdup(src);
-	if (!m->path ||
-			!m->src) {
-		nih_fatal("Out of memory mounting controllers");
-		return false;
-	}
+	m->path = NIH_MUST( strdup(dest) );
+	m->src = NIH_MUST( strdup(src) );
 	nih_info(_("Arranged to mount %s onto %s"), m->controller, m->path);
 	return true;
 }
@@ -295,7 +288,6 @@ static bool save_mount_subsys(char *s)
 	insert_pt = find_controller_in_mounts(controller, &found);
 	if (found)
 		return true;
-
 	tmp = realloc(all_mounts, (num_controllers+1) * sizeof(*all_mounts));
 	if (!tmp) {
 		nih_fatal("Out of memory mounting controllers");
@@ -366,6 +358,16 @@ static bool do_mount_subsys(int i)
 		nih_fatal("Failed to create %s: %s", dest, strerror(errno));
 		return false;
 	}
+
+	if (m->unified) {
+		if (mount(m->controller, dest, "cgroup", 0, "__DEVEL__sane_behavior") < 0) {
+			nih_error("Failed mounting %s: %s\n", m->controller,
+					strerror(errno));
+			return false;
+		}
+		return true;
+	}
+
 	if (m->premounted)
 		ret = mount(src, dest, "cgroup", 0, m->options);
 	else
@@ -426,8 +428,11 @@ static bool process_mounted_subsystem(char *options)
 	while (tok) {
 		if (strncmp(tok, "name=", 5) == 0) {
 			i = find_controller_in_mounts(tok+5, &found);
-			if (found) // jinkeys, multiple mounts already
+			if (found) {
+				if (all_mounts[i].unified)
+					return true;
 				goto next;
+			}
 			if (!save_mount_subsys(tok))
 				return false;
 			i = find_controller_in_mounts(tok+5, &found);
@@ -440,8 +445,11 @@ static bool process_mounted_subsystem(char *options)
 				NIH_MUST( nih_strcat_sprintf(&cp_opts, NULL, ",%s", tok) );
 		} else if (is_kernel_controller(tok)) {
 			i = find_controller_in_mounts(tok, &found);
-			if (found) // jinkeys, multiple mounts already
+			if (found) {
+				if (all_mounts[i].unified)
+					return true;
 				goto next;
+			}
 			if (!save_mount_subsys(tok))
 				return false;
 			i = find_controller_in_mounts(tok, &found);
@@ -532,6 +540,8 @@ static bool collate_premounted_subsystems(void)
 		first = &all_mounts[i];
 		if (!first->premounted)
 			continue;
+		if (first->unified)
+			continue;
 		if (first->comounted) // already linked
 			continue;
 		if (!first->options)
@@ -700,6 +710,7 @@ static void print_debug_controller_info(void)
 		nih_debug("    premounted: %s comounted: %s",
 			m->premounted ? "yes" : "no",
 			m->comounted ? m->comounted->controller : "(none)");
+		nih_debug("    unified: %s", m->unified ? "yes" : "no");
 	}
 }
 
@@ -710,7 +721,7 @@ void do_list_controllers(void *parent, char ***output)
 	nih_assert(output);
 	*output = NIH_MUST( nih_alloc(parent, (num_controller_mnts+1) * sizeof(char *)) );
 	(*output)[num_controller_mnts] = NULL;
-	
+
 	/* XXX
 	 * This will actually not be right.
 	 * if we have freezer,devices co-mounted, we'll have two separate
@@ -818,9 +829,257 @@ static void build_all_controllers(char *skip_mounts)
 	do_prune_comounts(all_controllers);
 }
 
+/*
+ * Check whether the unified hierarchy is available
+ */
+static bool unified_hierarchy_present(void)
+{
+	FILE *f;
+	char *line = NULL;
+	size_t len = 0;
+	bool ret = false;
+
+	if ((f = fopen("/proc/self/cgroup", "r")) == NULL)
+		return false;
+
+	while (getline(&line, &len, f) != -1) {
+		if (strncmp(line, "0:", 2) == 0) {
+			ret = true;
+			break;
+		}
+	}
+
+	fclose(f);
+	free(line);
+	return ret;
+}
+
+/*
+ * Mount a transient instance of the unified hierarchy, so that
+ * we can pin the controllers currently enabled there
+ */
+static bool mount_transient_unified(void)
+{
+	if (mkdir(UNIFIED_DIR, 0755) < 0 && errno != EEXIST)
+		return false;
+	if (mount("cgroup", UNIFIED_DIR, "cgroup", 0, "__DEVEL__sane_behavior") < 0) {
+		nih_error("Error mounting unified: %s\n", strerror(errno));
+		return false;
+	}
+	return true;
+}
+
+static void mark_unified_controllers_comounted(void)
+{
+	int i;
+	struct controller_mounts *first = NULL, *last = NULL;
+
+	for (i = 0; i < num_controllers; i++) {
+		if (!all_mounts[i].unified)
+			continue;
+		if (!first) {
+			first = last = &all_mounts[i];
+			continue;
+		}
+		last->comounted = &all_mounts[i];
+		last = &all_mounts[i];
+	}
+	if (last && last != first)
+		last->comounted = first;
+}
+
+static bool record_unified_controllers(char *ctrl_list)
+{
+	struct controller_mounts *tmp;
+	int i, insert_pt;
+	bool found;
+	char *tok;
+
+	tok = strtok(ctrl_list, " ");
+	while (tok) {
+		insert_pt = find_controller_in_mounts(tok, &found);
+		if (found) {
+			/*
+			 * Something in the program flow is not right.
+			 * We must not know what's actually going on.
+			 */
+			nih_error("Impossible: found duplicate in unified (%s)", tok);
+			return false;
+		}
+
+		tmp = realloc(all_mounts, (num_controllers+1) * sizeof(*all_mounts));
+		if (!tmp) {
+			nih_fatal("Out of memory mounting controllers");
+			return false;
+		}
+		all_mounts = tmp;
+
+		for (i = num_controllers; i > insert_pt; i--)
+			all_mounts[i] = all_mounts[i-1];
+		zero_out(&all_mounts[insert_pt]);
+
+		if (!fill_in_controller(&all_mounts[insert_pt], tok, tok))
+			return false;
+		all_mounts[insert_pt].unified = true;
+		num_controllers++;
+		tok = strtok(NULL, " ");
+	}
+
+	return true;
+}
+
+static inline void drop_newlines(char *s)
+{
+	int l;
+
+	for (l=strlen(s); l>0 && s[l-1] == '\n'; l--)
+		s[l-1] = '\0';
+}
+
+static char *read_oneline(const char *from)
+{
+	char *line = NULL;
+	FILE *f = fopen(from, "r");
+	size_t len = 0;
+	if (!f)
+		return NULL;
+	if (getline(&line, &len, f) == -1) {
+		fclose(f);
+		return NULL;
+	}
+	fclose(f);
+
+	drop_newlines(line);
+	return line;
+}
+
+static bool write_string(const char *fnam, char *string)
+{
+	FILE *f;
+	size_t len, ret;
+
+	if (!(f = fopen(fnam, "w")))
+		return false;
+	len = strlen(string);
+	ret = fwrite(string, 1, len, f);
+	if (ret != len) {
+		nih_error("Error writing to file: %s", strerror(errno));
+		fclose(f);
+		return false;
+	}
+	if (fclose(f) < 0) {
+		nih_error("Error writing to file: %s", strerror(errno));
+		return false;
+	}
+	return true;
+}
+
+static bool pin_and_process_unified(void)
+{
+	nih_local char  *ctrlcopy = NULL,
+			*ctrlline = NULL;
+	char path[MAXPATHLEN];
+	char *line = NULL, *tok;
+	FILE *f;
+
+	if (mkdir(UNIFIED_PIN, 0755) < 0 && errno != EEXIST)
+		return false;
+
+	sprintf(path, "%s/cgroup.controllers", UNIFIED_DIR);
+	line = read_oneline(path);
+	if (!line || strlen(line) == 0) {
+		free(line);
+		return true;
+	}
+
+	ctrlcopy = NIH_MUST( nih_strdup(NULL, line) );
+	tok = strtok(line, " ");
+	while (tok) {
+		NIH_MUST( nih_strcat_sprintf(&ctrlline, NULL, "+%s ", tok) );
+		tok = strtok(NULL, " ");
+	}
+	free(line);
+
+	sprintf(path, "%s/cgroup.subtree_control", UNIFIED_DIR);
+	if (!write_string(path, ctrlline)) {
+		nih_error("Error pinning unified controllers");
+		return false;
+	}
+
+	sprintf(path, "%s/cgroup.subtree_control", UNIFIED_PIN);
+	if (!write_string(path, ctrlline)) {
+		nih_error("Error pinning unified controllers");
+		return false;
+	}
+
+	sprintf(path, "%s/cgroup.procs", UNIFIED_DIR);
+	if (!(f = fopen(path, "w")))
+		return true;
+	fprintf(f, "%d", (int)getpid());
+	fclose(f);
+
+	nih_debug("pinned the following controllers unified: %s\n", ctrlline);
+
+	return record_unified_controllers(ctrlcopy);
+}
+
+static bool do_mount_unified(void)
+{
+	int i;
+	bool found = false;
+	nih_local char *dest = NULL;
+
+	for (i = 0; i < num_controllers; i++) {
+		if (all_mounts[i].unified) {
+			found = true;
+			break;
+		}
+	}
+	if (!found)
+		return true;
+
+	dest = NIH_MUST( nih_sprintf(NULL, "%s/%s", base_path, ".cgm_unified") );
+	if (mkdir(dest, 0755) < 0 && errno != EEXIST) {
+		nih_fatal("Failed to create %s: %s", dest, strerror(errno));
+		return false;
+	}
+
+	if (mount("unified", dest, "cgroup", 0, "__DEVEL__sane_behavior") < 0) {
+		nih_fatal("Failed to mount unified hierarchy: %s\n", strerror(errno));
+		return false;
+	}
+
+	return true;
+}
+
+static void umount_transient_unified(void)
+{
+	umount(UNIFIED_DIR);
+	rmdir(UNIFIED_DIR);
+}
+
+static bool process_unified_hierarchy(void)
+{
+	bool ret = false;
+
+	if (!unified_hierarchy_present())
+		return true;
+	if (!mount_transient_unified())
+		return false;
+	if (pin_and_process_unified())
+		ret = true;
+
+	umount_transient_unified();
+	return ret;
+}
+
 int collect_subsystems(char *extra_mounts, char *skip_mounts)
 {
-	/* first collect all already-mounted subsystems */
+	/* first mount and pin anything currently in the unified hierarchy */
+	if (!process_unified_hierarchy())
+		return -1;
+
+	/* next collect all already-mounted subsystems */
 	if (!collect_premounted_subsystems())
 		return -1;
 
@@ -841,6 +1100,7 @@ int collect_subsystems(char *extra_mounts, char *skip_mounts)
 	if (!collate_premounted_subsystems())
 		return -1;
 
+	mark_unified_controllers_comounted();
 	build_all_controllers(skip_mounts);
 
 	build_controller_mntlist();
@@ -985,6 +1245,9 @@ int setup_cgroup_mounts(void)
 		return -1;
 	}
 
+	if (!do_mount_unified())
+		return -1;
+
 	for (i=0; i<num_controllers; i++) {
 		if (!do_mount_subsys(i)) {
 			nih_fatal("Failed mounting cgroups");
@@ -1063,14 +1326,6 @@ bool create_agent_symlinks(void)
 	return true;
 }
 
-static inline void drop_newlines(char *s)
-{
-	int l;
-
-	for (l=strlen(s); l>0 && s[l-1] == '\n'; l--)
-		s[l-1] = '\0';
-}
-
 /*
  * The user will pass in 'cpuset' or 'systemd'.  /proc/self/cgroup will
  * show 'cpuset:' or 'name=systemd:'.  We have to account for that.
@@ -1087,6 +1342,26 @@ static bool is_same_controller(const char *cmp, const char *cnt)
 }
 
 /*
+ * In unified hierarchy tasks must be in a leaf node.  Cgmanager
+ * creates .cgm_leaf for tasks.  Ignore that.
+ */
+static void chop_leaf(char *path)
+{
+	size_t len;
+	char *cmp;
+
+	if (!path)
+		return;
+
+	len = strlen(path);
+	if (len < strlen(U_LEAF))
+		return;
+	cmp = path + len - strlen(U_LEAF_NAME);
+	if (strcmp(cmp, U_LEAF_NAME) == 0)
+		*cmp = '\0';
+}
+
+/*
  * pid_cgroup: return the cgroup of @pid for @controller.
  * retv must be a (at least) MAXPATHLEN size buffer into
  * which the answer will be copied.
@@ -1127,6 +1402,8 @@ static inline char *pid_cgroup(pid_t pid, const char *controller, char *retv)
 found:
 	fclose(f);
 	free(line);
+	if (is_unified_controller(controller))
+		chop_leaf(cgroup);
 	return cgroup;
 }
 
@@ -1277,6 +1554,16 @@ const char *get_controller_path(const char *controller)
 	return all_mounts[i].path;
 }
 
+bool is_unified_controller(const char *controller)
+{
+	int i;
+	for (i = 0; i < num_controllers; i++)
+		if (strcmp(all_mounts[i].controller, controller) == 0 &&
+				all_mounts[i].unified)
+			return true;
+	return false;
+}
+
 int get_path_depth(const char *p)
 {
 	int depth = 0;
@@ -1635,12 +1922,12 @@ bool chown_cgroup_path(const char *path, uid_t uid, gid_t gid, bool all_children
 		nih_local char *fpath = NULL;
 		fpath = NIH_MUST( nih_sprintf(NULL, "%s/cgroup.procs", path) );
 		if (chown(fpath, uid, gid) < 0)
-			nih_error("Failed to chown procs file %s: %s", fpath,
-				strerror(errno));
+			nih_error("%s: Failed to chown procs file %s: %s", __func__,
+					fpath, strerror(errno));
 		sprintf(fpath+len, "/tasks");
 		if (chown(fpath, uid, gid) < 0)
-			nih_error("Failed to chown tasks file %s: %s", fpath,
-				strerror(errno));
+			nih_warn("%s: Failed to chown the tasks file %s: %s\n",
+					__func__, fpath, strerror(errno));
 	}
 
 out:
@@ -1706,9 +1993,11 @@ bool set_value_trusted(const char *path, const char *value)
 	}
 	return true;
 }
-bool set_value(const char *path, const char *value)
+bool set_value(const char *controller, const char *path, const char *value)
 {
 	int i;
+	char *p;
+	nih_local char *upath = NULL, *file = NULL;
 
 	nih_assert (path);
 
@@ -1724,7 +2013,24 @@ bool set_value(const char *path, const char *value)
 		}
 	}
 
-	return set_value_trusted(path, value);
+	if (!set_value_trusted(path, value))
+		return false;
+
+	if (!is_unified_controller(controller))
+		return true;
+	upath = NIH_MUST( nih_strdup(NULL, path) );
+	p = strrchr(upath, '/');
+	if (!p)
+		return false;  // can't happen
+	file = NIH_MUST( nih_strdup(NULL, p+1) );
+	*p = '\0';
+	NIH_MUST( nih_strcat(&upath, NULL, U_LEAF) );
+	if (!dir_exists(upath))
+		return true;
+	NIH_MUST( nih_strcat(&upath, NULL, "/") );
+	NIH_MUST( nih_strcat(&upath, NULL, file) );
+
+	return (set_value_trusted(upath, value));
 }
 
 /*
@@ -1798,6 +2104,8 @@ bool move_self_to_root(void)
 			continue;
 		if (all_mounts[i].skip)
 			continue;
+		if (all_mounts[i].unified)
+			continue;
 		path = NIH_MUST( nih_sprintf(NULL, "%s/tasks", all_mounts[i].path) );
 		if ((f = fopen(path, "w")) == NULL)
 			return false;
@@ -1843,6 +2151,8 @@ int get_directory_children(void *parent, const char *path, char ***output)
 	while (readdir_r(d, &dirent, &direntp) == 0 && direntp) {
 		if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, ".."))
 			continue;
+		if (!strcmp(direntp->d_name, U_LEAF_NAME))
+			continue;
 		if (direntp->d_type != DT_DIR)
 			continue;
 		if (used+1 >= alloced) {
@@ -1980,3 +2290,120 @@ bool path_is_under_taskcg(pid_t pid, const char *contr,const char *path)
 		return true;
 	return false;
 }
+
+static bool do_copy_controllers(const char *from, const char *to)
+{
+	nih_local char  *src = NULL,
+		  *dest = NULL,
+		  *ctrlline = NULL;
+	char *line = NULL, *tok, *savetok;
+	src = NIH_MUST( nih_sprintf(NULL, "%s/cgroup.subtree_control", from) );
+	dest = NIH_MUST( nih_sprintf(NULL, "%s/cgroup.subtree_control", to) );
+	line = read_oneline(src);
+	if (!line)
+		return true;
+
+	tok = strtok_r(line, " ", &savetok);
+	while (tok) {
+		NIH_MUST( nih_strcat_sprintf(&ctrlline, NULL, "+%s ", tok) );
+		tok = strtok_r(NULL, " ", &savetok);
+	}
+	free(line);
+	return write_string(dest, ctrlline);
+}
+
+bool unified_copy_controllers(const char *controller, const char *path)
+{
+	nih_local char *p = NULL;
+	char *pe;
+
+	if (!is_unified_controller(controller))
+		return true;
+
+	p = NIH_MUST( nih_strdup(NULL, path) );
+	pe = strrchr(p, '/');
+	if (pe)
+		*pe = '\0';
+	return do_copy_controllers(p, path);
+}
+
+bool create_leaf(const char *controller, const char *path, uid_t u, gid_t g)
+{
+	nih_local char *p = NULL;
+
+	if (!is_unified_controller(controller))
+		return true;
+
+	NIH_MUST( nih_strcat_sprintf(&p, NULL, "%s%s", path, U_LEAF) );
+	if (mkdir(p, 755) < 0 && errno != EEXIST)
+		return false;
+	if (mkdir(p, 755) < 0 && errno == EEXIST)
+		return true;
+	if (chown(p, u, g) < 0)
+		return false;
+	return true;
+}
+
+static bool copy_owner_perms_from_to(const char *from, const char *to)
+{
+	struct stat sb;
+	struct dirent dirent, *direntp;
+	bool error = false;
+	DIR *d;
+
+	if (stat(from, &sb) < 0)
+		return false;
+	if (chown(to, sb.st_uid, sb.st_gid) < 0)
+		return false;
+	if (chmod(to, sb.st_mode) < 0)
+		return false;
+
+	d = opendir(from);
+	if (!d)
+		return false;
+
+	while (readdir_r(d, &dirent, &direntp) == 0 && direntp) {
+		nih_local char  *srcp = NULL,
+			  *dstp = NULL;
+
+		if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, ".."))
+			continue;
+		srcp = NIH_MUST( nih_sprintf(NULL, "%s/%s", from, direntp->d_name) );
+		dstp = NIH_MUST( nih_sprintf(NULL, "%s/%s", to, direntp->d_name) );
+		if (!file_exists(dstp))
+			continue;
+		if (stat(srcp, &sb) < 0)
+			continue;
+		if (chown(dstp, sb.st_uid, sb.st_gid) < 0) {
+			nih_error("Failed to chown file %s to %u:%u",
+					dstp, sb.st_uid, sb.st_gid);
+			error = true;
+		}
+		if (chmod(dstp, sb.st_mode) < 0) {
+			nih_error("Failed to chmod file %s to %o",
+					dstp, sb.st_mode);
+			error = true;
+		}
+		nih_debug("chowned %s to %d %d and chmoded it to %o", dstp, (int)sb.st_uid, (int)sb.st_gid, sb.st_mode);
+	}
+	closedir(d);
+
+	return !error;
+}
+
+bool ensure_leafdir(const char *controller, const char *path)
+{
+	nih_local char *p = NIH_MUST( nih_sprintf(NULL, "%s%s", path, U_LEAF) );
+
+	if (mkdir(p, 0755) < 0) {
+		if (errno != EEXIST)
+			return false;
+		// existed, don't change perms
+		return true;
+	}
+	if (!copy_owner_perms_from_to(path, p)) {
+		rmdir(p);
+		return false;
+	}
+	return true;
+}
diff --git a/fs.h b/fs.h
index cae8213..0643aa1 100644
--- a/fs.h
+++ b/fs.h
@@ -24,6 +24,11 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#define UNIFIED_DIR CGDIR "/.cgm_unified"
+#define UNIFIED_PIN UNIFIED_DIR "/.cgpin"
+#define U_LEAF_NAME ".cgm_leaf"
+#define U_LEAF "/" U_LEAF_NAME
+
 extern char *all_controllers;
 struct keys_return_type;
 
@@ -43,7 +48,7 @@ const char *get_controller_path(const char *controller);
 bool hostuid_to_ns(uid_t uid, pid_t pid, uid_t *answer);
 bool chown_cgroup_path(const char *path, uid_t uid, gid_t gid, bool all_children);
 bool chmod_cgroup_path(const char *path, int mode);
-bool set_value(const char *path, const char *value);
+bool set_value(const char *controller, const char *path, const char *value);
 bool set_value_trusted(const char *path, const char *value);
 unsigned long read_pid_ns_link(int pid);
 unsigned long read_user_ns_link(int pid);
@@ -61,3 +66,7 @@ bool prune_verify_comounts(char *controllers);
 void do_list_controllers(void *parent, char ***output);
 void convert_directory_contents(struct keys_return_type **keys, struct ucred r);
 bool path_is_under_taskcg(pid_t pid, const char *contr,const char *path);
+bool unified_copy_controllers(const char *controller, const char *path);
+bool is_unified_controller(const char *controller);
+bool create_leaf(const char *controller, const char *path, uid_t u, gid_t g);
+bool ensure_leafdir(const char *controller, const char *path);
-- 
2.1.4



More information about the lxc-devel mailing list