[CRIU] [RFC 2/2] cgroup: Add ability to dump custom cgroup properties

Cyrill Gorcunov gorcunov at openvz.org
Thu Apr 2 00:14:32 PDT 2015


We have some common predefined properties such as
"cpuset.cpus" and etc gathered in @cgp_predefined
set, but there might be situation when only predefined
ones are not enough so add ability to specify additional
properties via --cgroup-props option.

This option takes either path to a file or plain JSON
stream which describe the properties to dump.

For example one may pass

  --cgroup-props "{\"controller name 1\" : [ \"property-1\", \"property-2\", ... ]}"

to dump custom properties.

At moment we require Jansson library to be present in the
system to support JSON parsing of --cgroup-props. But if
someone doesn't need custom properties lets don't force
him to setup Jansson library for nothing.

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 Makefile.config           |   8 ++
 cgroup-props.c            | 202 ++++++++++++++++++++++++++++++++++++++++++++++
 cr-dump.c                 |   6 ++
 crtools.c                 |   5 ++
 include/cgroup-props.h    |   4 +
 include/cr_options.h      |   1 +
 scripts/feature-tests.mak |  26 ++++++
 7 files changed, 252 insertions(+)

diff --git a/Makefile.config b/Makefile.config
index ac54775fc4cd..cd04e2d0f34d 100644
--- a/Makefile.config
+++ b/Makefile.config
@@ -8,6 +8,11 @@ ifeq ($(call try-cc,$(LIBBSD_DEV_TEST),-lbsd),y)
 	DEFINES += -DCONFIG_HAS_LIBBSD
 endif
 
+ifeq ($(call try-cc,$(LIBJANSSON_TEST),-ljansson),y)
+	LIBS += -ljansson
+	JANSSON = y
+endif
+
 $(CONFIG): scripts/utilities.mak scripts/feature-tests.mak include/config-base.h
 	$(E) "  GEN     " $@
 	$(Q) @echo '#ifndef __CR_CONFIG_H__' > $@
@@ -36,6 +41,9 @@ endif
 ifeq ($(call try-cc,$(SETPROCTITLE_INIT_TEST),-lbsd),y)
 	$(Q) @echo '#define CONFIG_HAS_SETPROCTITLE_INIT' >> $@
 endif
+ifeq ($(JANSSON),y)
+	$(Q) @echo '#define CONFIG_HAS_LIBJANSSON' >> $@
+endif
 	$(Q) @echo '#endif /* __CR_CONFIG_H__ */' >> $@
 
 config: $(CONFIG)
diff --git a/cgroup-props.c b/cgroup-props.c
index 08338667e012..1d81781cddb0 100644
--- a/cgroup-props.c
+++ b/cgroup-props.c
@@ -1,9 +1,24 @@
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include "compiler.h"
 #include "cgroup-props.h"
+#include "config.h"
+#include "xmalloc.h"
+#include "util.h"
+#include "list.h"
+#include "log.h"
+#include "bug.h"
+
+#ifdef CONFIG_HAS_LIBJANSSON
+# include <jansson.h>
+#endif
 
 #undef	LOG_PREFIX
 #define LOG_PREFIX "cg-prop: "
@@ -85,14 +100,201 @@ static const cgp_t cgp_predefined[] = {
 	},
 };
 
+typedef struct {
+	struct list_head	list;
+	cgp_t			cgp;
+} cgp_list_t;
+
+static LIST_HEAD(cgp_list);
+
+static void cgp_free(cgp_list_t *p)
+{
+	size_t i;
+
+	if (p) {
+		for (i = 0; i < p->cgp.nr_props; i++)
+			xfree((void *)p->cgp.props[i]);
+		xfree((void *)p->cgp.name);
+		xfree((void *)p->cgp.props);
+		xfree(p);
+	}
+}
+
+static int cgp_handle_option(char *arg, size_t len)
+{
+	void *mem = MAP_FAILED;
+	int fd = -1, ret = -1;
+	struct stat st;
+
+	/*
+	 * It's either plain JSON stream, then it must
+	 * start with '{', or it's path to a file to parse.
+	 */
+	if (arg[0] == '{') {
+		if (cgp_parse(arg, len)) {
+			pr_err("Failed to parse stream `%s'\n", arg);
+			return -1;
+		}
+		return 0;
+	}
+
+	fd = open(arg, O_RDONLY);
+	if (fd < 0) {
+		pr_perror("Can't open file %s\n", arg);
+		goto err;
+	}
+
+	if (fstat(fd, &st)) {
+		pr_perror("Can't stat file %s\n", arg);
+		goto err;
+	}
+
+	mem = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FILE, fd, 0);
+	if (mem == MAP_FAILED) {
+		pr_perror("Can't mmap file %s\n", arg);
+		goto err;
+	}
+
+	if (cgp_parse(mem, st.st_size)) {
+		pr_err("Failed to parse file `%s'\n", arg);
+		goto err;
+	}
+
+	ret = 0;
+err:
+	if (mem != MAP_FAILED)
+		munmap(mem, st.st_size);
+	close_safe(&fd);
+	return ret;
+}
+
+int cgp_init(char *arg, size_t len)
+{
+	if (arg && len)
+		return cgp_handle_option(arg, len);
+	return 0;
+}
+
+#ifdef CONFIG_HAS_LIBJANSSON
+int cgp_parse(char *stream, size_t len)
+{
+	json_t *root, *controller_obj;
+	json_error_t error;
+
+	const char *controller;
+	int ret = -1;
+
+	cgp_list_t *cgp_p = NULL;
+
+	root = json_loadb(stream, len, JSON_DISABLE_EOF_CHECK, &error);
+	if (!root) {
+		pr_err("json_loadb error: %s\n", error.text);
+		goto err;
+	}
+
+	/*
+	 * The format we expect here is
+	 * {
+	 * 	"controller name 1" : [ "property-1", "property-2", ... ],
+	 * 	"controller name 2" : [ "property-1", "property-2", ... ]
+	 * }
+	 */
+
+	json_object_foreach(root, controller, controller_obj) {
+		json_t *v;
+		size_t i;
+
+		if (!json_is_array(controller_obj)) {
+			pr_err("Expected array props for controller %s\n", controller);
+			goto err;
+		}
+
+		if (!cgp_p) {
+			cgp_p = xzalloc(sizeof(*cgp_p));
+			if (!cgp_p)
+				goto err;
+			INIT_LIST_HEAD(&cgp_p->list);
+		}
+
+		cgp_p->cgp.name = xstrdup(controller);
+		if (!cgp_p->cgp.name)
+			goto err;
+
+		pr_debug("Parsing dynamic controller '%s'\n", controller);
+
+		if (json_array_size(controller_obj) < 1) {
+			pr_err("Expected array of props for controller %s\n", controller);
+			goto err;
+		}
+
+		cgp_p->cgp.props = xzalloc(json_array_size(controller_obj) * sizeof(cgp_p->cgp.props));
+		if (!cgp_p->cgp.props)
+			goto err;
+		cgp_p->cgp.nr_props = json_array_size(controller_obj);
+
+		for (i = 0; i < json_array_size(controller_obj); i++) {
+			v = json_array_get(controller_obj, i);
+
+			if (!json_is_string(v)) {
+				pr_err("Expected strings array props for controller %s\n", controller);
+				goto err;
+			}
+
+			pr_debug("\tproperty '%s'\n", json_string_value(v));
+
+			cgp_p->cgp.props[i] = xstrdup(json_string_value(v));
+			if (!cgp_p->cgp.props[i])
+				goto err;
+		}
+
+		list_add(&cgp_p->list, &cgp_list);
+		cgp_p = NULL;
+	}
+	ret = 0;
+
+err:
+	if (root)
+		json_decref(root);
+	cgp_free(cgp_p);
+	return ret;
+}
+#else
+int cgp_parse(char *stream, size_t len)
+{
+	pr_err("No JSON parser is compiled in. Feature unsupported\n");
+	return -1;
+}
+#endif
+
 const cgp_t *cgp_get_props(const char *name)
 {
+	cgp_list_t *p;
 	size_t i;
 
+	/*
+	 * Lookup over predefined ones.
+	 */
 	for (i = 0; i < ARRAY_SIZE(cgp_predefined); i++) {
 		if (!strcmp(cgp_predefined[i].name, name))
 			return &cgp_predefined[i];
 	}
 
+	/*
+	 * Lookup over dynamically parsed.
+	 */
+	list_for_each_entry(p, &cgp_list, list) {
+		if (!strcmp(p->cgp.name, name))
+			return &p->cgp;
+	}
+
 	return NULL;
 }
+
+void cgp_fini(void)
+{
+	cgp_list_t *p, *t;
+
+	list_for_each_entry_safe(p, t, &cgp_list, list)
+		cgp_free(p);
+	INIT_LIST_HEAD(&cgp_list);
+}
diff --git a/cr-dump.c b/cr-dump.c
index cc524558989a..bdd325420083 100644
--- a/cr-dump.c
+++ b/cr-dump.c
@@ -58,6 +58,7 @@
 #include "cpu.h"
 #include "elf.h"
 #include "cgroup.h"
+#include "cgroup-props.h"
 #include "file-lock.h"
 #include "page-xfer.h"
 #include "kerndat.h"
@@ -1827,6 +1828,10 @@ int cr_dump_tasks(pid_t pid)
 	if (vdso_init())
 		goto err;
 
+	if (cgp_init(opts.cgroup_props,
+		     opts.cgroup_props ? strlen(opts.cgroup_props) : 0))
+		goto err;
+
 	if (parse_cg_info())
 		goto err;
 
@@ -1918,6 +1923,7 @@ err:
 		ret = -1;
 
 	cr_plugin_fini(CR_PLUGIN_STAGE__DUMP, ret);
+	cgp_fini();
 
 	if (!ret) {
 		/*
diff --git a/crtools.c b/crtools.c
index 4412e4cb588c..d6c78f537d5b 100644
--- a/crtools.c
+++ b/crtools.c
@@ -203,6 +203,7 @@ int main(int argc, char *argv[], char *envp[])
 		{ "cgroup-root", required_argument, 0, 1061},
 		{ "inherit-fd", required_argument, 0, 1062},
 		{ "feature", required_argument, 0, 1063},
+		{ "cgroup-props", required_argument, 0, 1064},
 		{ },
 	};
 
@@ -416,6 +417,9 @@ int main(int argc, char *argv[], char *envp[])
 			if (check_add_feature(optarg) < 0)
 				return 1;
 			break;
+		case 1064:
+			opts.cgroup_props = optarg;
+			break;
 		case 'M':
 			{
 				char *aux;
@@ -642,6 +646,7 @@ usage:
 "                        change the root cgroup the controller will be\n"
 "                        installed into. No controller means that root is the\n"
 "                        default for all controllers not specified.\n"
+"  --cgroup-props PROPS  add nonstandart cgroup properties to dump\n"
 "\n"
 "* Logging:\n"
 "  -o|--log-file FILE    log file name\n"
diff --git a/include/cgroup-props.h b/include/cgroup-props.h
index 2b87275122a7..e9fa68030ddf 100644
--- a/include/cgroup-props.h
+++ b/include/cgroup-props.h
@@ -7,6 +7,10 @@ typedef struct {
 	const char	**props;
 } cgp_t;
 
+extern int cgp_init(char *arg, size_t len);
+extern void cgp_fini(void);
+
 extern const cgp_t *cgp_get_props(const char *name);
+extern int cgp_parse(char *stream, size_t len);
 
 #endif /* __CR_CGROUP_PROPS_H__ */
diff --git a/include/cr_options.h b/include/cr_options.h
index d6ce2ae4d1f2..109f0d86c7f8 100644
--- a/include/cr_options.h
+++ b/include/cr_options.h
@@ -57,6 +57,7 @@ struct cr_options {
 	bool			force_irmap;
 	char			**exec_cmd;
 	bool			manage_cgroups;
+	char			*cgroup_props;
 	char			*new_global_cg_root;
 	struct list_head	new_cgroup_roots;
 	bool			aufs;		/* auto-deteced, not via cli */
diff --git a/scripts/feature-tests.mak b/scripts/feature-tests.mak
index 519eb52694f9..de87e14e804d 100644
--- a/scripts/feature-tests.mak
+++ b/scripts/feature-tests.mak
@@ -92,3 +92,29 @@ int main(int argc, char *argv[], char *envp[])
 }
 
 endef
+
+define LIBJANSSON_TEST
+
+#include <jansson.h>
+
+int main(int argc, char *argv[], char *envp[])
+{
+	json_error_t error;
+	const char *v;
+	json_t *obj;
+
+	(void)json_loadb(v, 0, 0, &error);
+	json_object_foreach(obj, v, obj);
+	(void)json_is_array(obj);
+	(void)json_array_size(obj);
+	(void)json_array_get(obj, 0);
+	(void)json_is_string(obj);
+	(void)json_decref(obj);
+
+	(void)error;
+	(void)v;
+	(void)obj;
+
+	return 0;
+}
+endef
-- 
1.9.3



More information about the CRIU mailing list