[CRIU] [PATCH] Attempt to restore cgroups

Tycho Andersen tycho.andersen at canonical.com
Tue Jul 1 06:36:04 PDT 2014


During the dump phase, /proc/cgroups is parsed to find co-mounted cgroups.
Then, for each task /proc/self/cgroup is parsed for the cgroups that it is a
member of, and that cgroup is traversed to find any child cgroups which may
also need restoring. All of this information is persisted along with the
original cg_sets, which indicate which cgroups a task is a member of.

On restore, an initial phase creates all the cgroups which were saved and
attempts to restore any peroperties they had. Then the tasks are restored into
their respective cgroups via cg_sets as usual.

Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
---
 cgroup.c              | 555 +++++++++++++++++++++++++++++++++++++++++++++++---
 cr-dump.c             |   6 +
 include/cgroup.h      |  43 ++++
 include/proc_parse.h  |   2 +
 include/util.h        |   5 +
 proc_parse.c          |   1 +
 protobuf/cgroup.proto |  26 ++-
 util.c                |  26 +++
 8 files changed, 629 insertions(+), 35 deletions(-)

diff --git a/cgroup.c b/cgroup.c
index 9bf9e45..c980456 100644
--- a/cgroup.c
+++ b/cgroup.c
@@ -5,6 +5,8 @@
 #include <unistd.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
+#include <ftw.h>
+#include <libgen.h>
 #include "xmalloc.h"
 #include "cgroup.h"
 #include "pstree.h"
@@ -28,6 +30,12 @@ struct cg_set {
 	struct list_head	ctls;
 };
 
+struct cgroup_mount {
+	struct list_head	l;
+	char			*controller;
+	char			*path;
+};
+
 static LIST_HEAD(cg_sets);
 static unsigned int n_sets;
 static CgSetEntry **rst_sets;
@@ -36,6 +44,10 @@ static struct cg_set *root_cgset; /* Set root item lives in */
 static struct cg_set *criu_cgset; /* Set criu process lives in */
 static u32 cg_set_ids = 1;
 
+static LIST_HEAD(cgroups);
+static unsigned int n_cgroups;
+static LIST_HEAD(cgroup_mounts);
+
 static CgSetEntry *find_rst_set_by_id(u32 id)
 {
 	int i;
@@ -118,6 +130,357 @@ static struct cg_set *get_cg_set(struct list_head *ctls, unsigned int n_ctls)
 	return cs;
 }
 
+struct cg_controller *new_controller(const char *name, unsigned int heirarchy)
+{
+	struct cg_controller *nc = xmalloc(sizeof(*nc));
+	if (!nc)
+		return NULL;
+
+	nc->controllers = xmalloc(sizeof(char*));
+	if (!nc->controllers) {
+		xfree(nc);
+		return NULL;
+	}
+
+	nc->controllers[0] = xstrdup(name);
+	if (!nc->controllers[0]) {
+		xfree(nc->controllers);
+		xfree(nc);
+		return NULL;
+	}
+
+	nc->n_controllers = 1;
+	nc->heirarchy = heirarchy;
+
+	nc->n_heads = 0;
+	INIT_LIST_HEAD(&nc->heads);
+
+	return nc;
+}
+
+struct cgroup_mount *find_cg_mount(const char *controller)
+{
+	struct cgroup_mount *cur;
+	list_for_each_entry(cur, &cgroup_mounts, l) {
+		if (strcmp(controller, cur->controller) == 0)
+			return cur;
+	}
+
+	return NULL;
+}
+
+int parse_cg_mountinfo()
+{
+	FILE *f = fopen("/proc/mounts", "r");
+	char buf[1024];
+
+	while(fgets(buf, 1024, f)) {
+		char path[PATH_MAX], fs[1024], opts[1024];
+		struct cgroup_mount *cgm;
+
+		if (sscanf(buf, "%*s %s %s %s %*d %*d", path, fs, opts) != 3)
+			return 0;
+
+		if (strcmp(fs, "cgroup") == 0) {
+			char *slash;
+
+			slash = strrchr(path, '/');
+			if (!slash) {
+				pr_err("bad path %s\n", path);
+				return -1;
+			}
+
+			cgm = xmalloc(sizeof(*cgm));
+			if (!cgm)
+				return -1;
+
+
+			*slash = '\0';
+			cgm->path = xstrdup(path);
+			if (!cgm->path) {
+				xfree(cgm);
+				return -1;
+			}
+
+			cgm->controller = xstrdup(slash + 1);
+			if (!cgm->controller) {
+				xfree(cgm->path);
+				xfree(cgm);
+				return -1;
+			}
+
+			list_add_tail(&cgm->l, &cgroup_mounts);
+		}
+	}
+
+	return 0;
+}
+
+/* Parse and create all the real controllers. This does not include things with
+ * the "name=" prefix, e.g. systemd.
+ */
+int parse_cgroups()
+{
+	FILE *f = fopen("/proc/cgroups", "r");
+	char buf[1024], name[1024];
+	uint32_t heirarchy;
+	struct cg_controller *cur = NULL;
+
+	if (!f) {
+		pr_perror("failed opening /proc/cgroups");
+		return -1;
+	}
+
+	/* throw away the header */
+	if (!fgets(buf, 1024, f))
+		return 0;
+
+	while(fgets(buf, 1024, f))
+	{
+		char *n;
+		char found = 0;
+
+		sscanf(buf, "%s" "%" SCNu32, name, &heirarchy);
+		list_for_each_entry(cur, &cgroups, l) {
+			if (cur->heirarchy == heirarchy)
+			{
+				void *m;
+
+				found = 1;
+				cur->n_controllers++;
+				m = xrealloc(cur->controllers, sizeof(char*) * cur->n_controllers);
+				if(!m)
+					return -1;
+
+				cur->controllers = m;
+				if (!cur->controllers)
+					return -1;
+
+				n = xstrdup(name);
+				if (!n)
+					return -1;
+
+				cur->controllers[cur->n_controllers-1] = n;
+				break;
+			}
+		}
+
+		if (!found)
+		{
+			struct cg_controller *nc = new_controller(name, heirarchy);
+			if (!nc)
+				return -1;
+			list_add_tail(&nc->l, &cur->l);
+			n_cgroups++;
+		}
+	}
+
+	return 0;
+}
+
+static struct cg_controller	*current_controller;
+static char			*current_controller_name;
+
+#define EXACT_MATCH	0
+#define PARENT_MATCH	1
+#define NO_MATCH	2
+
+static int find_dir(const char *path, struct list_head *dirs, struct cgroup_dir **rdir)
+{
+	struct cgroup_dir *d;
+	list_for_each_entry(d, dirs, siblings) {
+		pr_info("%s %s\n", d->path, path);
+		if (strcmp(d->path, path) == 0) {
+			*rdir = d;
+			return EXACT_MATCH;
+		}
+
+		if (strncmp(d->path, path, strlen(d->path))) {
+			int ret = find_dir(path, &d->children, rdir);
+			if (ret == NO_MATCH) {
+				*rdir = d;
+				return PARENT_MATCH;
+			}
+			return ret;
+
+		}
+	}
+
+	return NO_MATCH;
+}
+
+static int add_cgroup(const char *fpath, const struct stat *sb, int typeflag)
+{
+	struct cgroup_dir *ncd = NULL, *match;
+	int ret = 0;
+	char pbuf[PATH_MAX], *name, *path;
+
+
+	if (typeflag == FTW_D)
+	{
+		FILE *f;
+		int mtype;
+
+		strncpy(pbuf, fpath, PATH_MAX);
+
+		pr_info("adding cgroup %s\n", fpath);
+
+		ncd = xmalloc(sizeof(*ncd));
+		if(!ncd)
+		{
+			ret = -1;
+			goto out;
+		}
+		ncd->path = NULL;
+		pr_info("foo2\n");
+
+		name = strstr(fpath, current_controller_name);
+		if (!name) {
+			pr_err("%s has no name component (%s)\n", fpath, current_controller_name);
+			ret = -1;
+			goto out;
+		}
+
+		path = strchr(name, '/');
+		if (!path) {
+			pr_err("Bad path %s\n", name);
+			ret = -1;
+			goto out;
+		}
+
+		ncd->path = xstrdup(path);
+		if (!ncd->path) {
+			ret = -1;
+			goto out;
+		}
+
+		mtype = find_dir(path, &current_controller->heads, &match);
+
+		switch(mtype) {
+			/* ignore co-mounted cgroups */
+			case EXACT_MATCH :
+				goto out;
+			case PARENT_MATCH :
+				list_add_tail(&ncd->siblings, &match->children);
+				match->n_children++;
+				break;
+			case NO_MATCH :
+				list_add_tail(&ncd->siblings, &current_controller->heads);
+				current_controller->n_heads++;
+				break;
+		}
+
+		INIT_LIST_HEAD(&ncd->children);
+		ncd->n_children = 0;
+		ncd->controller = current_controller;
+
+		ncd->flags = 0;
+
+		snprintf(pbuf, PATH_MAX, "%s/memory.limit_in_bytes", fpath);
+		f = fopen(pbuf, "r");
+		if (f)
+		{
+			if (fscanf(f, "%" SCNu64, &ncd->mem_limit) != 1)
+			{
+				pr_err("Failed scanning %s\n", pbuf);
+				ret = -1;
+				goto out;
+			}
+			ncd->flags |= HAS_MEM_LIMIT;
+			fclose(f);
+		}
+
+		snprintf(pbuf, PATH_MAX, "%s/cpu.shares", fpath);
+		f = fopen(pbuf, "r");
+		if (f)
+		{
+			if (fscanf(f, "%" SCNu32, &ncd->cpu_shares) != 1)
+			{
+				pr_err("Failed scanning %s for u32\n", pbuf);
+				ret = -1;
+				goto out;
+			}
+			ncd->flags |= HAS_CPU_SHARES;
+			fclose(f);
+		}
+
+		return 0;
+	}
+
+out:
+	if (ncd) {
+		if (ncd->path)
+			xfree(ncd->path);
+		xfree(ncd);
+	}
+
+	return ret;
+}
+
+static int collect_cgroups(struct list_head *ctls)
+{
+	struct cg_ctl *cc;
+	int ret = 0;
+
+	list_for_each_entry(cc, ctls, l) {
+		char path[PATH_MAX];
+		char *name;
+		struct cg_controller *cg;
+		int i;
+		struct cgroup_mount *cgm;
+
+		if (strstartswith(cc->name, "name="))
+			name = cc->name + 5;
+		else
+			name = cc->name;
+
+		cgm = find_cg_mount(name);
+		if (!cgm) {
+			pr_err("cgroup mount point for %s not found!\n", name);
+			return -1;
+		}
+
+		snprintf(path, PATH_MAX, "%s/%s%s", cgm->path, name, cc->path);
+
+		current_controller = NULL;
+		current_controller_name = name;
+
+		/* Use the previously allocated struct for this controller if
+		 * there is one */
+		list_for_each_entry(cg, &cgroups, l) {
+			for (i = 0; i < cg->n_controllers; i++)
+			{
+				if (strcmp(cg->controllers[i], cc->name) == 0) {
+					current_controller = cg;
+					break;
+				}
+			}
+		}
+
+		if (!current_controller)
+		{
+			/* only allow "fake" controllers to be created this way */
+			if (!strstartswith(cc->name, "name=")) {
+				pr_err("controller %s not found\n", cc->name);
+				return -1;
+			} else {
+				struct cg_controller *nc = new_controller(cc->name, -1);
+				list_add_tail(&nc->l, &cg->l);
+				n_cgroups++;
+				current_controller = nc;
+			}
+		}
+
+		if ((ret = ftw(path, add_cgroup, 4)) < 0)
+		{
+			pr_perror("failed walking %s for empty cgroups\n", path);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 int dump_task_cgroup(struct pstree_item *item, u32 *cg_id)
 {
 	int pid;
@@ -134,6 +497,9 @@ int dump_task_cgroup(struct pstree_item *item, u32 *cg_id)
 	if (parse_task_cgroup(pid, &ctls, &n_ctls))
 		return -1;
 
+	if (collect_cgroups(&ctls) < 0)
+		return -1;
+
 	cs = get_cg_set(&ctls, n_ctls);
 	if (!cs)
 		return -1;
@@ -152,6 +518,74 @@ int dump_task_cgroup(struct pstree_item *item, u32 *cg_id)
 	return 0;
 }
 
+int dump_cg_dirs(struct list_head *dirs, size_t n_dirs, CgroupDirEntry ***ents)
+{
+	struct cgroup_dir *cur;
+	CgroupDirEntry *cde;
+	void *m;
+	int i = 0;
+
+	m = xmalloc(n_dirs * (sizeof(CgroupDirEntry *) + sizeof(CgroupDirEntry)));
+	*ents = m;
+	if (!m)
+		return -1;
+
+	cde = m + n_dirs * sizeof(CgroupDirEntry *);
+
+	list_for_each_entry(cur, dirs, siblings) {
+		cgroup_dir_entry__init(cde);
+
+		cde->path = cur->path;
+		cde->has_mem_limit = cur->flags & HAS_MEM_LIMIT;
+		cde->mem_limit = cur->mem_limit;
+		cde->has_cpu_shares = cur->flags & HAS_CPU_SHARES;
+		cde->cpu_shares = cur->cpu_shares;
+
+		cde->n_children = cur->n_children;
+		if (cur->n_children > 0)
+			if (dump_cg_dirs(&cur->children, cur->n_children, &cde->children) < 0) {
+				xfree(*ents);
+				return -1;
+			}
+		(*ents)[i++] = cde++;
+	}
+
+	return 0;
+}
+
+static int dump_controllers(CgroupEntry *cg)
+{
+	struct cg_controller *cur;
+	CgControllerEntry *ce;
+	void *m;
+	int i;
+
+	cg->n_controllers = n_cgroups;
+	m = xmalloc(n_cgroups * (sizeof(CgControllerEntry *) + sizeof(CgControllerEntry)));
+	cg->controllers = m;
+	ce = m + cg->n_controllers * sizeof(CgControllerEntry *);
+	if(!m)
+		return -1;
+
+	i = 0;
+	list_for_each_entry(cur, &cgroups, l) {
+		cg_controller_entry__init(ce);
+
+		ce->controllers = cur->controllers;
+		ce->n_controllers = cur->n_controllers;
+		ce->n_dirs = cur->n_heads;
+		if (ce->n_dirs > 0)
+			if (dump_cg_dirs(&cur->heads, cur->n_heads, &ce->dirs) < 0) {
+				xfree(cg->controllers);
+				return -1;
+			}
+		cg->controllers[i++] = ce++;
+	}
+
+	return 0;
+}
+
+
 static int dump_sets(CgroupEntry *cg)
 {
 	struct cg_set *set;
@@ -159,7 +593,7 @@ static int dump_sets(CgroupEntry *cg)
 	int s, c;
 	void *m;
 	CgSetEntry *se;
-	ControllerEntry *ce;
+	CgMemberEntry *ce;
 
 	pr_info("Dumping %d sets\n", n_sets - 1);
 
@@ -195,16 +629,16 @@ static int dump_sets(CgroupEntry *cg)
 		se->id = set->id;
 
 		se->n_ctls = set->n_ctls;
-		m = xmalloc(se->n_ctls * (sizeof(ControllerEntry *) + sizeof(ControllerEntry)));
+		m = xmalloc(se->n_ctls * (sizeof(CgMemberEntry *) + sizeof(CgMemberEntry)));
 		se->ctls = m;
-		ce = m + se->n_ctls * sizeof(ControllerEntry *);
+		ce = m + se->n_ctls * sizeof(CgMemberEntry *);
 		if (!m)
 			return -1;
 
 		c = 0;
 		list_for_each_entry(ctl, &set->ctls, l) {
 			pr_info("   `- Dumping %s of %s\n", ctl->name, ctl->path);
-			controller_entry__init(ce);
+			cg_member_entry__init(ce);
 			ce->name = ctl->name;
 			ce->path = ctl->path;
 			se->ctls[c++] = ce++;
@@ -242,6 +676,8 @@ int dump_cgroups(void)
 
 	if (dump_sets(&cg))
 		return -1;
+	if (dump_controllers(&cg))
+		return -1;
 
 	pr_info("Writing CG image\n");
 	return pb_write_one(fdset_fd(glob_fdset, CR_FD_CGROUP), &cg, PB_CGROUP);
@@ -256,7 +692,7 @@ static int move_in_cgroup(CgSetEntry *se)
 	for (i = 0; i < se->n_ctls; i++) {
 		char aux[1024];
 		int fd, err;
-		ControllerEntry *ce = se->ctls[i];
+		CgMemberEntry *ce = se->ctls[i];
 
 		if (strstartswith(ce->name, "name="))
 			sprintf(aux, "%s/%s/tasks", ce->name + 5, ce->path);
@@ -323,6 +759,58 @@ void fini_cgroup(void)
 	xfree(cg_yard);
 }
 
+static int prepare_cgroup_dirs(char* paux, size_t off, CgroupDirEntry **ents, size_t n_ents)
+{
+	size_t i, my_off;
+	CgroupDirEntry *e;
+
+	for (i = 0; i < n_ents; i++)
+	{
+		e = ents[i];
+
+		my_off = sprintf(paux + off, "/%s", e->path);
+
+		if (mkdirp(paux)) {
+			pr_perror("Can't make cgroup dir %s", paux);
+			return -1;
+		}
+
+		if (e->has_mem_limit) {
+			FILE *f;
+
+			sprintf(paux + my_off + off, "/memory.limit_in_bytes");
+
+			f = fopen(paux, "w+");
+			if (!f) {
+				pr_perror("Couldn't open %s for writing\n", paux);
+				return -1;
+			}
+
+			fprintf(f, "%" SCNu64, e->mem_limit);
+			fclose(f);
+		}
+
+		if (e->has_cpu_shares) {
+			FILE *f;
+
+			sprintf(paux + my_off + off, "/cpu.shares");
+
+			f = fopen(paux, "w+");
+			if (!f) {
+				pr_perror("Couldn't open %s for writing\n", paux);
+				return -1;
+			}
+
+			fprintf(f, "%" SCNu32, e->cpu_shares);
+			fclose(f);
+		}
+
+		prepare_cgroup_dirs(paux, off, e->children, e->n_children);
+	}
+
+	return 0;
+}
+
 /*
  * Prepare the CGROUP_YARD service descriptor. This guy is
  * tmpfs mount with the set of ctl->name directories each
@@ -341,10 +829,10 @@ void fini_cgroup(void)
  * them in advance.
  */
 
-static int prepare_cgroup_sfd(CgSetEntry *root_set)
+static int prepare_cgroup_sfd(CgroupEntry *ce)
 {
 	int off, i;
-	char paux[PATH_MAX], aux[128];
+	char paux[PATH_MAX];
 
 	pr_info("Preparing cgroups yard\n");
 
@@ -370,26 +858,45 @@ static int prepare_cgroup_sfd(CgSetEntry *root_set)
 		goto err;
 	}
 
-	for (i = 0; i < root_set->n_ctls; i++) {
-		ControllerEntry *ce = root_set->ctls[i];
-		char *opt = ce->name;
+	for (i = 0; i < ce->n_controllers; i++) {
+		CgControllerEntry *ctrl = ce->controllers[i];
+		int j, name_off, opt_off;
+		char *name, opt[1024];
 
-		if (strstartswith(ce->name, "name=")) {
-			sprintf(paux + off, "/%s", ce->name + 5);
-			sprintf(aux, "none,%s", ce->name);
-			opt = aux;
-		} else
-			sprintf(paux + off, "/%s", ce->name);
+		if (ctrl->n_controllers < 1) {
+			pr_err("Each cg_controller_entry must have at least 1 controller");
+			goto err;
+		}
+
+		if (strstartswith(ctrl->controllers[0], "name=")) {
+			name = ctrl->controllers[0] + 5;
+			opt_off = sprintf(opt, "none,%s", ctrl->controllers[0]);
+		} else {
+			name = ctrl->controllers[0];
+			opt_off = sprintf(opt, "%s", ctrl->controllers[0]);
+		}
+
+		for (j = 1; j < ctrl->n_controllers; j++)
+		{
+			name = ctrl->controllers[i];
+			opt_off += sprintf(opt + opt_off, ",%s", ctrl->controllers[i]);
+		}
+
+		name_off = sprintf(paux + off, "/%s", name);
 
 		if (mkdir(paux, 0700)) {
-			pr_perror("Can't make cgyard subdir");
+			pr_perror("Can't make cgyard subdir %s", paux);
 			goto err;
 		}
 
 		if (mount("none", paux, "cgroup", 0, opt) < 0) {
-			pr_perror("Can't mount %s cgyard", ce->name);
+			pr_perror("Can't mount %s cgyard", paux);
 			goto err;
 		}
+
+		if(prepare_cgroup_dirs(paux, off + name_off, ctrl->dirs, ctrl->n_dirs))
+			goto err;
+
 	}
 
 	pr_debug("Opening %s as cg yard\n", cg_yard);
@@ -431,15 +938,5 @@ int prepare_cgroup(void)
 
 	n_sets = ce->n_sets;
 	rst_sets = ce->sets;
-	if (n_sets)
-		/*
-		 * We rely on the fact that all sets contain the same
-		 * set of controllers. This is checked during dump
-		 * with cg_set_compare(CGCMP_ISSUB) call.
-		 */
-		ret = prepare_cgroup_sfd(rst_sets[0]);
-	else
-		ret = 0;
-
-	return ret;
+	return prepare_cgroup_sfd(ce);
 }
diff --git a/cr-dump.c b/cr-dump.c
index 45f1f5f..aab2646 100644
--- a/cr-dump.c
+++ b/cr-dump.c
@@ -1776,6 +1776,12 @@ int cr_dump_tasks(pid_t pid)
 	if (vdso_init())
 		goto err;
 
+	if (parse_cg_mountinfo())
+		goto err;
+
+	if (parse_cgroups())
+		goto err;
+
 	if (write_img_inventory())
 		goto err;
 
diff --git a/include/cgroup.h b/include/cgroup.h
index 148b26f..b051ba9 100644
--- a/include/cgroup.h
+++ b/include/cgroup.h
@@ -1,6 +1,7 @@
 #ifndef __CR_CGROUP_H__
 #define __CR_CGROUP_H__
 #include "asm/int.h"
+#include "list.h"
 struct pstree_item;
 extern u32 root_cg_set;
 int dump_task_cgroup(struct pstree_item *, u32 *);
@@ -8,4 +9,46 @@ int dump_cgroups(void);
 int prepare_task_cgroup(struct pstree_item *);
 int prepare_cgroup(void);
 void fini_cgroup(void);
+
+#define HAS_MEM_LIMIT	(1 << 0)
+#define HAS_CPU_SHARES	(1 << 1)
+
+struct cg_controller;
+
+struct cgroup_dir {
+	char			*path;
+	u64			mem_limit;
+	u32			cpu_shares;
+	unsigned int		flags;
+
+	/* this is how children are linked together */
+	struct list_head	siblings;
+
+	/* more cgroup_dirs */
+	struct list_head	children;
+	unsigned int		n_children;
+
+	struct cg_controller	*controller;
+};
+
+struct cg_controller {
+	unsigned int		heirarchy;
+	unsigned int		n_controllers;
+	char			**controllers;
+
+	/* cgroup_dirs */
+	struct list_head 	heads;
+	unsigned int		n_heads;
+
+	/* for cgroup list in cgroup.c */
+	struct list_head	l;
+};
+
+/* Parse /proc/mountinfo for mount points of cgroups. */
+int parse_cg_mountinfo();
+
+/* Parse /proc/cgroups for co-mounted cgroups and initialize internal
+ * structures. */
+int parse_cgroups();
+
 #endif /* __CR_CGROUP_H__ */
diff --git a/include/proc_parse.h b/include/proc_parse.h
index b153328..c41d664 100644
--- a/include/proc_parse.h
+++ b/include/proc_parse.h
@@ -5,6 +5,7 @@
 #include "asm/types.h"
 #include "image.h"
 #include "list.h"
+#include "cgroup.h"
 
 #include "protobuf/eventfd.pb-c.h"
 #include "protobuf/eventpoll.pb-c.h"
@@ -194,6 +195,7 @@ struct cg_ctl {
 	struct list_head l;
 	char *name;
 	char *path;
+	struct cgroup *cgroup;
 };
 
 /*
diff --git a/include/util.h b/include/util.h
index 22a0f3d..9b3c461 100644
--- a/include/util.h
+++ b/include/util.h
@@ -303,4 +303,9 @@ static inline bool strstartswith(char *str, char *sub)
 	}
 }
 
+/*
+ * mkdir -p
+ */
+int mkdirp(const char* path);
+
 #endif /* __CR_UTIL_H__ */
diff --git a/proc_parse.c b/proc_parse.c
index f2ea897..23d1a49 100644
--- a/proc_parse.c
+++ b/proc_parse.c
@@ -1545,6 +1545,7 @@ int parse_task_cgroup(int pid, struct list_head *retl, unsigned int *n)
 			xfree(ncc);
 			goto err;
 		}
+		ncc->cgroup = NULL;
 
 		list_for_each_entry(cc, retl, l)
 			if (strcmp(cc->name, name) >= 0)
diff --git a/protobuf/cgroup.proto b/protobuf/cgroup.proto
index a497c70..19804a3 100644
--- a/protobuf/cgroup.proto
+++ b/protobuf/cgroup.proto
@@ -1,13 +1,27 @@
-message controller_entry {
-	required string name	= 1;
-	required string path	= 2;
+message cgroup_dir_entry {
+	required string 		path		= 1;
+	optional uint64 		mem_limit 	= 2;
+	optional uint32 		cpu_shares	= 3;
+	repeated cgroup_dir_entry	children 	= 4;
+}
+
+message cg_controller_entry {
+	required uint32			id		= 1;
+	repeated string			controllers	= 2;
+	repeated cgroup_dir_entry	dirs		= 3;
+}
+
+message cg_member_entry {
+	required string		name		= 1;
+	required string		path            = 2;
 }
 
 message cg_set_entry {
-	required uint32			id	= 1;
-	repeated controller_entry	ctls	= 2;
+	required uint32                 id      = 1;
+	repeated cg_member_entry	ctls    = 3;
 }
 
 message cgroup_entry {
-	repeated cg_set_entry	sets	= 1;
+	repeated cg_set_entry		sets		= 1;
+	repeated cg_controller_entry	controllers	= 2;
 }
diff --git a/util.c b/util.c
index d697f7a..bf2eccc 100644
--- a/util.c
+++ b/util.c
@@ -678,3 +678,29 @@ struct vma_area *alloc_vma_area(void)
 
 	return p;
 }
+
+int mkdirp(const char* path)
+{
+	char* tok;
+	char made_path[PATH_MAX] = "", tok_path[PATH_MAX];
+
+	if (strlen(path) >= PATH_MAX)
+	{
+		pr_err("path %s is longer than PATH_MAX", path);
+		return -1;
+	}
+
+	strncpy(tok_path, path, PATH_MAX);
+
+	for(tok = strtok(tok_path, "/"); tok; tok = strtok(NULL, "/")) {
+		strcat(made_path, tok);
+		strcat(made_path, "/");
+		if (mkdir(made_path, 0755) < 0 && errno != EEXIST) {
+			pr_perror("couldn't mkdirpat directory\n");
+			return -1;
+		}
+	}
+
+	return 0;
+
+}
-- 
1.9.1



More information about the CRIU mailing list