From 7b1b0409f5fc1828f0feb2086fe5b8184f7eb2c6 Mon Sep 17 00:00:00 2001
From: Jan Chaloupka <jchaloup@redhat.com>
Date: Sat, 20 Sep 2014 20:02:13 +0200
Subject: [PATCH] loading configuration files from /etc/cgconfig.d/ directory

---
 Makefile.in                |   2 +-
 doc/man/cgconfig.conf.5    |   7 +-
 doc/man/cgrulesengd.8      |   8 +-
 include/libcgroup/config.h |  21 ++++++
 src/config.c               | 181 +++++++++++++++++++++++++++++++++++++++++++--
 src/daemon/Makefile.am     |   4 +-
 src/daemon/Makefile.in     |  25 ++++++-
 src/daemon/cgrulesengd.c   |  51 +++++++++++--
 src/libcgroup-internal.h   |   3 +
 src/libcgroup.map          |   5 ++
 src/tools/tools-common.h   |   2 +-
 11 files changed, 286 insertions(+), 23 deletions(-)

diff --git a/Makefile.in b/Makefile.in
index af6e89c..0349c93 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -84,7 +84,7 @@ DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
 	$(srcdir)/config.h.in $(top_srcdir)/scripts/init.d/cgconfig.in \
 	$(top_srcdir)/scripts/init.d/cgred.in \
 	$(srcdir)/libcgroup.pc.in COPYING INSTALL README config.guess \
-	config.sub install-sh missing ltmain.sh
+	config.sub depcomp install-sh missing ylwrap ltmain.sh
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/configure.in
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
diff --git a/doc/man/cgconfig.conf.5 b/doc/man/cgconfig.conf.5
index be80e4e..a7d9935 100644
--- a/doc/man/cgconfig.conf.5
+++ b/doc/man/cgconfig.conf.5
@@ -251,6 +251,9 @@ Templates does not use
 .B default
 section settings.
 
+.I /etc/cgconfig.d/
+directory can be used for additional configuration files. cgrulesengd searches this directory for additional templates.
+
 .\"********************************************"
 .SH EXAMPLES
 .LP
@@ -783,10 +786,12 @@ related to them.
 .SH FILES
 .LP
 .PD .1v
-.TP 20
+.TP
 .B /etc/cgconfig.conf
 .TP
 default libcgroup configuration file
+.B /etc/cgconfig.d/
+default libcgroup configuration files directory
 .PD 
 
 .SH SEE ALSO
diff --git a/doc/man/cgrulesengd.8 b/doc/man/cgrulesengd.8
index 2e89c5b..749b6fc 100644
--- a/doc/man/cgrulesengd.8
+++ b/doc/man/cgrulesengd.8
@@ -65,10 +65,16 @@ controls verbosity of the tool. Allowed values are \fBDEBUG\fR,
 .SH FILES
 .LP
 .PD .1v
-.TP 20
+.TP
 .B /etc/cgrules.conf
 .TP
 the default libcgroup configuration file
+.TP
+.B /etc/cgconfig.conf
+the default templates file
+.TP
+.B /etc/cgconfig.d/
+the default templates directory
 
 .SH SEE ALSO
 cgrules.conf (5)
diff --git a/include/libcgroup/config.h b/include/libcgroup/config.h
index 43568e1..9aaa390 100644
--- a/include/libcgroup/config.h
+++ b/include/libcgroup/config.h
@@ -84,11 +84,32 @@ int cgroup_init_templates_cache(char *pathname);
 int cgroup_reload_cached_templates(char *pathname);
 
 /**
+ * Load the templates cache from files. Before calling this function,
+ * cgroup_templates_cache_set_source_files has to be called first.
+ * @param file_index index of file which was unable to be parsed
+ * @return 0 on success, > 0 on error
+ */
+int cgroup_load_templates_cache_from_files(int *file_index);
+
+/**
+ * Setting source files of templates. This function has to be called before
+ * any call of cgroup_load_templates_cache_from_files.
+ * @param tmpl_files
+ */
+struct cgroup_string_list;
+void cgroup_templates_cache_set_source_files(
+	struct cgroup_string_list *tmpl_files);
+
+/**
  * Physically create a new control group in kernel, based on given control
  * group template and configuration file. If given template is not set in
  * configuration file, then the procedure works create the control group
  * using  cgroup_create_cgroup() function
  *
+ * Templates are loaded using cgroup_load_templates_cache_from_files
+ * function, which must be preceded by cgroup_templates_cache_set_source_files
+ * call.
+ *
  * The flags can alter the behavior of this function:
  * CGFLAG_USE_TEMPLATE_CACHE: Use cached templates instead of
  * parsing the config file
diff --git a/src/config.c b/src/config.c
index e1ee0a8..1c5fc14 100644
--- a/src/config.c
+++ b/src/config.c
@@ -41,6 +41,8 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include "tools/tools-common.h"
+
 unsigned int MAX_CGROUPS = 64;	/* NOTE: This value changes dynamically */
 unsigned int MAX_TEMPLATES = 64;
 				/* NOTE: This value changes dynamically */
@@ -89,6 +91,7 @@ static int config_template_table_index;
  */
 static struct cgroup *template_table;
 static int template_table_index;
+static struct cgroup_string_list *template_files;
 
 
 /*
@@ -1572,6 +1575,161 @@ int cgroup_init_templates_cache(char *pathname)
 
 }
 
+/**
+ * Setting source files of templates. This function has to be called before
+ * any call of cgroup_load_templates_cache_from_files.
+ * @param tmpl_files
+ */
+void cgroup_templates_cache_set_source_files(
+	struct cgroup_string_list *tmpl_files)
+{
+	template_files = tmpl_files;
+}
+
+/**
+ * Appending cgroup templates parsed by parser to template_table
+ * @param offset number of templates already in the table
+ */
+int cgroup_add_cgroup_templates(int offset)
+{
+	int i, ti, ret;
+
+	for (i = 0; i < config_template_table_index; i++) {
+		ti = i + offset;
+		ret = cgroup_copy_cgroup(&template_table[ti],
+			&config_template_table[i]);
+		if (ret)
+			return ret;
+
+		strcpy((template_table[ti]).name,
+			(config_template_table[i]).name);
+		template_table[ti].tasks_uid =
+			config_template_table[i].tasks_uid;
+		template_table[ti].tasks_gid =
+			config_template_table[i].tasks_gid;
+		template_table[ti].task_fperm =
+			config_template_table[i].task_fperm;
+		template_table[ti].control_uid =
+			config_template_table[i].control_uid;
+		template_table[ti].control_gid =
+			config_template_table[i].control_gid;
+		template_table[ti].control_fperm =
+			config_template_table[i].control_fperm;
+		template_table[ti].control_dperm =
+			config_template_table[i].control_dperm;
+	}
+
+	return 0;
+}
+
+/**
+ * Expand template table based on new number of parsed templates, i.e.
+ * on value of config_template_table_index.
+ * Change value of template_table_index.
+ * @return 0 on success, < 0 on error
+ */
+int cgroup_expand_template_table(void)
+{
+	int i;
+
+	template_table = realloc(template_table,
+		(template_table_index + config_template_table_index)
+		*sizeof(struct cgroup));
+
+	if (template_table == NULL)
+		return -ECGOTHER;
+
+	for (i = 0; i < config_template_table_index; i++)
+		template_table[i + template_table_index].index = 0;
+
+	template_table_index += config_template_table_index;
+
+	return 0;
+}
+
+/**
+ * Load the templates cache from files. Before calling this function,
+ * cgroup_templates_cache_set_source_files has to be called first.
+ * @param file_index index of file which was unable to be parsed
+ * @return 0 on success, > 0 on error
+ */
+int cgroup_load_templates_cache_from_files(int *file_index)
+{
+	int ret;
+	int i, j;
+	int template_table_last_index;
+	char *pathname;
+
+	if (!template_files) {
+		/* source files has not been set */
+		cgroup_dbg("Template source files have not been set. ");
+		cgroup_dbg("Using only %s\n", CGCONFIG_CONF_FILE);
+
+		if (template_table_index == 0)
+			/* the rules cache is empty */
+			return cgroup_init_templates_cache(
+				CGCONFIG_CONF_FILE);
+		else
+			/* cache is not empty */
+			return cgroup_reload_cached_templates(
+				CGCONFIG_CONF_FILE);
+	}
+
+	if (template_table) {
+		/* template structures have to be free */
+		for (i = 0; i < template_table_index; i++)
+			cgroup_free_controllers(&template_table[i]);
+		free(template_table);
+		template_table = NULL;
+	}
+	template_table_index = 0;
+
+	if ((config_template_table_index != 0) || (config_table_index != 0)) {
+		/* config structures have to be clean before parsing */
+		cgroup_free_config();
+	}
+
+	for (j = 0; j < template_files->count; j++) {
+		pathname = template_files->items[j];
+
+		cgroup_dbg("Parsing templates from %s.\n", pathname);
+		/* Attempt to read the configuration file
+		 * and cache the rules. */
+		ret = cgroup_parse_config(pathname);
+		if (ret) {
+			cgroup_dbg("Could not initialize rule cache, ");
+			cgroup_dbg("error was: %d\n", ret);
+			*file_index = j;
+			return ret;
+		}
+
+		if (config_template_table_index > 0) {
+			template_table_last_index = template_table_index;
+			ret = cgroup_expand_template_table();
+			if (ret) {
+				cgroup_dbg("Could not expand template table, ");
+				cgroup_dbg("error was: %d\n", -ret);
+				*file_index = j;
+				return -ret;
+			}
+
+			/* copy template data to templates cache structures */
+			cgroup_dbg("Copying templates to template table ");
+			cgroup_dbg("from %s.\n", pathname);
+			ret = cgroup_add_cgroup_templates(
+				template_table_last_index);
+			if (ret) {
+				cgroup_dbg("Unable to copy cgroup\n");
+				*file_index = j;
+				return ret;
+			}
+			cgroup_dbg("Templates to template table copied\n");
+		}
+	}
+
+	return 0;
+}
+
 /*
  * Create a given cgroup, based on template configuration if it is present
  * if the template is not present cgroup is creted using cgroup_create_cgroup
@@ -1593,13 +1751,22 @@ int cgroup_config_create_template_group(struct cgroup *cgroup,
 	 * use CGCONFIG_CONF_FILE by default
 	 */
 	if (!(flags & CGFLAG_USE_TEMPLATE_CACHE)) {
-		if (template_table_index == 0)
-			/* the rules cache is empty */
-			ret = cgroup_init_templates_cache(CGCONFIG_CONF_FILE);
-		else
-			/* cache is not empty */
-			ret = cgroup_reload_cached_templates(
-				CGCONFIG_CONF_FILE);
+		int fileindex;
+
+		/* the rules cache is empty */
+		ret = cgroup_load_templates_cache_from_files(
+			&fileindex);
+		if (ret != 0) {
+			if (fileindex < 0) {
+				cgroup_dbg("Error: Template source files ");
+				cgroup_dbg("have not been set\n");
+			} else {
+			    cgroup_dbg("Error: Failed to load template");
+			    cgroup_dbg("rules from %s. ",
+				    template_files->items[fileindex]);
+			}
+		}
+
 		if (ret != 0) {
 			cgroup_dbg("Failed initialize templates cache.\n");
 			return ret;
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am
index f3100ed..abbbe30 100644
--- a/src/daemon/Makefile.am
+++ b/src/daemon/Makefile.am
@@ -1,9 +1,9 @@
-INCLUDES = -I $(top_srcdir)/include
+INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/include
 
 if WITH_DAEMON
 
 sbin_PROGRAMS = cgrulesengd
-cgrulesengd_SOURCES = cgrulesengd.c cgrulesengd.h
+cgrulesengd_SOURCES = cgrulesengd.c cgrulesengd.h ../tools/tools-common.h ../tools/tools-common.c
 cgrulesengd_LDADD = $(top_builddir)/src/.libs/libcgroup.la -lrt
 cgrulesengd_LDFLAGS = -L$(top_builddir)/src/.libs
 
diff --git a/src/daemon/Makefile.in b/src/daemon/Makefile.in
index 76f7e07..f3efc65 100644
--- a/src/daemon/Makefile.in
+++ b/src/daemon/Makefile.in
@@ -92,8 +92,10 @@ CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 am__installdirs = "$(DESTDIR)$(sbindir)"
 PROGRAMS = $(sbin_PROGRAMS)
-am__cgrulesengd_SOURCES_DIST = cgrulesengd.c cgrulesengd.h
-@WITH_DAEMON_TRUE@am_cgrulesengd_OBJECTS = cgrulesengd.$(OBJEXT)
+am__cgrulesengd_SOURCES_DIST = cgrulesengd.c cgrulesengd.h \
+	../tools/tools-common.h ../tools/tools-common.c
+@WITH_DAEMON_TRUE@am_cgrulesengd_OBJECTS = cgrulesengd.$(OBJEXT) \
+@WITH_DAEMON_TRUE@	tools-common.$(OBJEXT)
 cgrulesengd_OBJECTS = $(am_cgrulesengd_OBJECTS)
 @WITH_DAEMON_TRUE@cgrulesengd_DEPENDENCIES =  \
 @WITH_DAEMON_TRUE@	$(top_builddir)/src/.libs/libcgroup.la
@@ -294,8 +296,8 @@ target_alias = @target_alias@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-INCLUDES = -I $(top_srcdir)/include
-@WITH_DAEMON_TRUE@cgrulesengd_SOURCES = cgrulesengd.c cgrulesengd.h
+INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/include
+@WITH_DAEMON_TRUE@cgrulesengd_SOURCES = cgrulesengd.c cgrulesengd.h ../tools/tools-common.h ../tools/tools-common.c
 @WITH_DAEMON_TRUE@cgrulesengd_LDADD = $(top_builddir)/src/.libs/libcgroup.la -lrt
 @WITH_DAEMON_TRUE@cgrulesengd_LDFLAGS = -L$(top_builddir)/src/.libs
 all: all-am
@@ -393,6 +395,7 @@ distclean-compile:
 	-rm -f *.tab.c
 
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cgrulesengd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tools-common.Po@am__quote@
 
 .c.o:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -415,6 +418,20 @@ distclean-compile:
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
 
+tools-common.o: ../tools/tools-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tools-common.o -MD -MP -MF $(DEPDIR)/tools-common.Tpo -c -o tools-common.o `test -f '../tools/tools-common.c' || echo '$(srcdir)/'`../tools/tools-common.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/tools-common.Tpo $(DEPDIR)/tools-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../tools/tools-common.c' object='tools-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tools-common.o `test -f '../tools/tools-common.c' || echo '$(srcdir)/'`../tools/tools-common.c
+
+tools-common.obj: ../tools/tools-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tools-common.obj -MD -MP -MF $(DEPDIR)/tools-common.Tpo -c -o tools-common.obj `if test -f '../tools/tools-common.c'; then $(CYGPATH_W) '../tools/tools-common.c'; else $(CYGPATH_W) '$(srcdir)/../tools/tools-common.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/tools-common.Tpo $(DEPDIR)/tools-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../tools/tools-common.c' object='tools-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tools-common.obj `if test -f '../tools/tools-common.c'; then $(CYGPATH_W) '../tools/tools-common.c'; else $(CYGPATH_W) '$(srcdir)/../tools/tools-common.c'; fi`
+
 mostlyclean-libtool:
 	-rm -f *.lo
 
diff --git a/src/daemon/cgrulesengd.c b/src/daemon/cgrulesengd.c
index 170837a..d959eff 100644
--- a/src/daemon/cgrulesengd.c
+++ b/src/daemon/cgrulesengd.c
@@ -34,6 +34,7 @@
 #include "libcgroup.h"
 #include "cgrulesengd.h"
 #include "../libcgroup-internal.h"
+#include "../tools/tools-common.h"
 
 #include <errno.h>
 #include <stdarg.h>
@@ -59,6 +60,9 @@
 
 #define NUM_PER_REALLOCATIOM	(100)
 
+/* list of config files from CGCONFIG_CONF_FILE and CGCONFIG_CONF_DIR */
+static struct cgroup_string_list template_files;
+
 /* Log file, NULL if logging to file is disabled */
 FILE* logfile;
 
@@ -936,6 +940,8 @@ void cgre_flash_rules(int signum)
 	/* Current time */
 	time_t tm = time(0);
 
+	int fileindex;
+
 	flog(LOG_INFO, "Reloading rules configuration\n");
 	flog(LOG_DEBUG, "Current time: %s\n", ctime(&tm));
 
@@ -949,7 +955,7 @@ void cgre_flash_rules(int signum)
 	}
 
 	/* Ask libcgroup to reload the template rules table. */
-	cgroup_reload_cached_templates(CGCONFIG_CONF_FILE);
+	cgroup_load_templates_cache_from_files(&fileindex);
 }
 
 /**
@@ -962,11 +968,13 @@ void cgre_flash_templates(int signum)
 	/* Current time */
 	time_t tm = time(0);
 
+	int fileindex;
+
 	flog(LOG_INFO, "Reloading templates configuration.\n");
 	flog(LOG_DEBUG, "Current time: %s\n", ctime(&tm));
 
 	/* Ask libcgroup to reload the templates table. */
-	cgroup_reload_cached_templates(CGCONFIG_CONF_FILE);
+	cgroup_load_templates_cache_from_files(&fileindex);
 }
 
 /**
@@ -1069,6 +1077,8 @@ int main(int argc, char *argv[])
 		{NULL, 0, NULL, 0}
 	};
 
+	int fileindex;
+
 	/* Make sure the user is root. */
 	if (getuid() != 0) {
 		fprintf(stderr, "Error: Only root can start/stop the control"
@@ -1180,6 +1190,25 @@ int main(int argc, char *argv[])
 	}
 
 	/* Ask libcgroup to load the configuration rules. */
+	ret = cgroup_string_list_init(&template_files,
+		CGCONFIG_CONF_FILES_LIST_MINIMUM_SIZE);
+	if (ret) {
+		fprintf(stderr, "%s: cannot init file list, out of memory?\n",
+			argv[0]);
+		goto finished_without_temp_files;
+	}
+	/* first add CGCONFIG_CONF_FILE into file list */
+	ret = cgroup_string_list_add_item(&template_files, CGCONFIG_CONF_FILE);
+	if (ret) {
+		fprintf(stderr, "%s: cannot add file to list, out of memory?\n"
+			, argv[0]);
+		goto finished;
+	}
+
+	/* then read CGCONFIG_CONF_DIR directory for additional config files */
+	cgroup_string_list_add_directory(&template_files, CGCONFIG_CONF_DIR,
+		argv[0]);
+
 	if ((ret = cgroup_init_rules_cache()) != 0) {
 		fprintf(stderr, "Error: libcgroup failed to initialize rules"
 				"cache from %s. %s\n", CGRULES_CONF_FILE,
@@ -1188,11 +1217,18 @@ int main(int argc, char *argv[])
 	}
 
 	/* ask libcgroup to load template rules as well */
-	ret = cgroup_init_templates_cache(CGCONFIG_CONF_FILE);
+	cgroup_templates_cache_set_source_files(&template_files);
+	ret = cgroup_load_templates_cache_from_files(&fileindex);
 	if (ret != 0) {
-		fprintf(stderr, "Error: libcgroup failed to initialize teplate"\
-				"rules from %s. %s\n", CGCONFIG_CONF_FILE,
-				cgroup_strerror(ret));
+		if (fileindex < 0) {
+			fprintf(stderr, "Error: Template source files ");
+			fprintf(stderr, "have not been set\n");
+		} else {
+			fprintf(stderr, "Error: Failed to initialize template");
+			fprintf(stderr, "rules from %s. ",
+				template_files.items[fileindex]);
+			fprintf(stderr, "%s\n", cgroup_strerror(-ret));
+		}
 		goto finished;
 	}
 
@@ -1259,6 +1295,9 @@ int main(int argc, char *argv[])
 	ret =  cgre_create_netlink_socket_process_msg();
 
 finished:
+	cgroup_string_list_free(&template_files);
+
+finished_without_temp_files:
 	if (logfile && logfile != stdout)
 		fclose(logfile);
 
diff --git a/src/libcgroup-internal.h b/src/libcgroup-internal.h
index 4c0f46c..c128788 100644
--- a/src/libcgroup-internal.h
+++ b/src/libcgroup-internal.h
@@ -48,6 +48,9 @@ __BEGIN_DECLS
 
 
 #define CGCONFIG_CONF_FILE		"/etc/cgconfig.conf"
+/* Minimum number of file in template file list for cgrulesengd */
+#define CGCONFIG_CONF_FILES_LIST_MINIMUM_SIZE   4
+#define CGCONFIG_CONF_DIR               "/etc/cgconfig.d"
 
 #define CGRULES_CONF_FILE       "/etc/cgrules.conf"
 #define CGRULES_MAX_FIELDS_PER_LINE		3
diff --git a/src/libcgroup.map b/src/libcgroup.map
index b0c162c..f8b0fb9 100644
--- a/src/libcgroup.map
+++ b/src/libcgroup.map
@@ -117,3 +117,8 @@ CGROUP_0.39 {
 	cgroup_log;
 	cgroup_parse_log_level_str;
 } CGROUP_0.38;
+
+CGROUP_0.40 {
+	cgroup_templates_cache_set_source_files;
+	cgroup_load_templates_cache_from_files;
+} CGROUP_0.39;
diff --git a/src/tools/tools-common.h b/src/tools/tools-common.h
index e05465f..c723eb4 100644
--- a/src/tools/tools-common.h
+++ b/src/tools/tools-common.h
@@ -20,7 +20,7 @@
 
 #include "config.h"
 #include <libcgroup.h>
-#include <libcgroup-internal.h>
+#include "../libcgroup-internal.h"
 
 #define cgroup_err(x...) cgroup_log(CGROUP_LOG_ERROR, x)
 #define cgroup_warn(x...) cgroup_log(CGROUP_LOG_WARNING, x)
-- 
1.9.3

