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

Stéphane Graber stgraber at ubuntu.com
Tue Jun 30 19:23:07 UTC 2015


On Tue, Jun 16, 2015 at 02:34:57PM +0000, Serge Hallyn wrote:
> 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>

Seems like a few debug statements have stuck around and show be dropped
or reworked.

> ---
>  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);

Odd indent ^

>  	*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);

Again ^

>  		c = NIH_MUST( nih_strdup(NULL, all_controllers) );
>  	} else {
>  		c = NIH_MUST( nih_strdup(NULL, controller) );
> +nih_info("c is %s before prune", c);

And again ^

>  		do_prune_comounts(c);
> +nih_info("c is %s after prune", c);

And again ^

>  	}
>  	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
> 
> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel

-- 
Stéphane Graber
Ubuntu developer
http://www.ubuntu.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20150630/c3c93d25/attachment.sig>


More information about the lxc-devel mailing list