[lxc-devel] [PATCHv2 06/12] lxc-attach: unify code for attaching a pid to a cgroup

David Ward david.ward at ll.mit.edu
Sun Mar 11 17:00:48 UTC 2012


To attach a new pid to the cgroups for an existing container, we can use
the same method that we did when we started the container: iterate over
all the mounted cgroup hierarchies; find the cgroup that pid 1 is in for
each hierarchy; add 'lxc/<name>' to the end of it; then write the pid to
the 'tasks' file in that cgroup. (The only difference is that we do not
create the cgroup again.) Note that we follow exactly the same iteration
pattern to delete our cgroups when a container is shutdown.

There may be situations where additional cgroups hierarchies are mounted
after the container is started, or the cgroup for pid 1 gets reassigned.
But we currently don't handle any of these cases in the shutdown code or
anywhere else, so it doesn't make sense to try to handle these cases for
lxc-attach by itself. Aside from simplifying the code, this change makes
it easier to solve a different problem: ignoring hierarchies that are
not bound to any subsystems (like 'systemd').

Signed-off-by: David Ward <david.ward at ll.mit.edu>
---
 src/lxc/attach.c     |  110 +------------------------------------------------
 src/lxc/attach.h     |    9 ----
 src/lxc/cgroup.c     |   91 +++++++++++++++++++++++------------------
 src/lxc/cgroup.h     |    2 +-
 src/lxc/lxc_attach.c |    4 +-
 5 files changed, 56 insertions(+), 160 deletions(-)

diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index 0ca8b39..a95b3d3 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -60,10 +60,9 @@ struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
 	struct lxc_proc_context_info *info = calloc(1, sizeof(*info));
 	FILE *proc_file;
 	char proc_fn[MAXPATHLEN];
-	char *line = NULL, *ptr, *ptr2;
+	char *line = NULL;
 	size_t line_bufsz = 0;
-	int ret, found, l;
-	int i;
+	int ret, found;
 
 	if (!info) {
 		SYSERROR("Could not allocate memory.");
@@ -114,117 +113,14 @@ struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
 		goto out_error;
 	}
 
-	/* read cgroups */
-	snprintf(proc_fn, MAXPATHLEN, "/proc/%d/cgroup", pid);
-
-	proc_file = fopen(proc_fn, "r");
-	if (!proc_file) {
-		SYSERROR("Could not open %s", proc_fn);
-		goto out_error;
-	}
-
-	/* we don't really know how many cgroup subsystems there are
-	 * mounted, so we go through the whole file twice */
-	i = 0;
-	while (getline(&line, &line_bufsz, proc_file) != -1) {
-		/* we assume that all lines containing at least two colons
-		 * are valid */
-		ptr = strchr(line, ':');
-		if (ptr && strchr(ptr + 1, ':'))
-			i++;
-	}
-
-	rewind(proc_file);
-
-	info->cgroups = calloc(i, sizeof(*(info->cgroups)));
-	info->cgroups_count = i;
-
-	i = 0;
-	while (getline(&line, &line_bufsz, proc_file) != -1 && i < info->cgroups_count) {
-		/* format of the lines is:
-		 * id:subsystems:path, where subsystems are separated by
-		 * commas and each subsystem may also be of the form
-		 * name=xxx if it describes a private named hierarchy
-		 * we will ignore the id in the following */
-		ptr = strchr(line, ':');
-		ptr2 = ptr ? strchr(ptr + 1, ':') : NULL;
-
-		/* ignore invalid lines */
-		if (!ptr || !ptr2) continue;
-
-		l = strlen(ptr2) - 1;
-		if (ptr2[l] == '\n')
-			ptr2[l] = '\0';
-
-		info->cgroups[i].subsystems = strndup(ptr + 1, ptr2 - (ptr + 1));
-		info->cgroups[i].cgroup = strdup(ptr2 + 1);
-
-		i++;
-	}
-
-	free(line);
-	fclose(proc_file);
-
 	return info;
 
 out_error:
-	lxc_proc_free_context_info(info);
+	free(info);
 	free(line);
 	return NULL;
 }
 
-void lxc_proc_free_context_info(struct lxc_proc_context_info *info)
-{
-	if (!info)
-		return;
-
-	if (info->cgroups) {
-		int i;
-		for (i = 0; i < info->cgroups_count; i++) {
-			free(info->cgroups[i].subsystems);
-			free(info->cgroups[i].cgroup);
-		}
-	}
-	free(info->cgroups);
-	free(info);
-}
-
-int lxc_attach_proc_to_cgroups(pid_t pid, struct lxc_proc_context_info *ctx)
-{
-	int i, ret;
-
-	if (!ctx) {
-		ERROR("No valid context supplied when asked to attach "
-		      "process to cgroups.");
-		return -1;
-	}
-
-	for (i = 0; i < ctx->cgroups_count; i++) {
-		char *path;
-
-		/* the kernel should return paths that start with '/' */
-		if (ctx->cgroups[i].cgroup[0] != '/') {
-			ERROR("For cgroup subsystem(s) %s the path '%s' does "
-			      "not start with a '/'",
-			      ctx->cgroups[i].subsystems,
-			      ctx->cgroups[i].cgroup);
-			return -1;
-		}
-
-		/* lxc_cgroup_path_get can process multiple subsystems */
-		ret = lxc_cgroup_path_get(&path, ctx->cgroups[i].subsystems,
-		                          &ctx->cgroups[i].cgroup[1]);
-		if (ret)
-			return -1;
-
-		ret = lxc_cgroup_attach(path, pid);
-		if (ret)
-			return -1;
-	}
-
-	return 0;
-}
-
 int lxc_attach_to_ns(pid_t pid)
 {
 	char path[MAXPATHLEN];
diff --git a/src/lxc/attach.h b/src/lxc/attach.h
index d2b7533..2d46c83 100644
--- a/src/lxc/attach.h
+++ b/src/lxc/attach.h
@@ -26,22 +26,13 @@
 
 #include <sys/types.h>
 
-struct lxc_proc_cgroup_info {
-	char *subsystems;
-	char *cgroup;
-};
-
 struct lxc_proc_context_info {
 	unsigned long personality;
 	unsigned long long capability_mask;
-	struct lxc_proc_cgroup_info* cgroups;
-	int cgroups_count;
 };
 
 extern struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid);
-extern void lxc_proc_free_context_info(struct lxc_proc_context_info *info);
 
-extern int lxc_attach_proc_to_cgroups(pid_t pid, struct lxc_proc_context_info *ctx);
 extern int lxc_attach_to_ns(pid_t other_pid);
 extern int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx);
 
diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c
index c915b52..06aa1a0 100644
--- a/src/lxc/cgroup.c
+++ b/src/lxc/cgroup.c
@@ -53,35 +53,6 @@ enum {
 	CGROUP_CLONE_CHILDREN,
 };
 
-static char *hasmntopt_multiple(struct mntent *mntent, const char *options)
-{
-	const char *ptr = options;
-	const char *ptr2 = strchr(options, ',');
-	char *result;
-
-	while (ptr2 != NULL) {
-		char *option = strndup(ptr, ptr2 - ptr);
-		if (!option) {
-			SYSERROR("Temporary memory allocation error");
-			return NULL;
-		}
-
-		result = hasmntopt(mntent, option);
-		free(option);
-
-		if (!result) {
-			return NULL;
-		}
-
-		ptr = ptr2 + 1;
-		ptr2 = strchr(ptr, ',');
-	}
-
-	/* for multiple mount options, the return value is basically NULL
-	 * or non-NULL, so this should suffice for our purposes */
-	return hasmntopt(mntent, ptr);
-}
-
 /*
  * get_init_cgroup: get the cgroup init is in.
  *  dsg: preallocated buffer to put the output in
@@ -153,7 +124,7 @@ static int get_cgroup_mount(const char *subsystem, char *mnt)
 	while ((mntent = getmntent(file))) {
 		if (strcmp(mntent->mnt_type, "cgroup"))
 			continue;
-		if (subsystem && !hasmntopt_multiple(mntent, subsystem))
+		if (subsystem && !hasmntopt(mntent, subsystem))
 			continue;
 
 		ret = snprintf(mnt, MAXPATHLEN, "%s%s/lxc", mntent->mnt_dir,
@@ -233,13 +204,16 @@ static int cgroup_enable_clone_children(const char *path)
 	return ret;
 }
 
-int lxc_cgroup_attach(const char *path, pid_t pid)
+static int lxc_one_cgroup_attach(const char *name,
+				 struct mntent *mntent, pid_t pid)
 {
 	FILE *f;
-	char tasks[MAXPATHLEN];
+	char tasks[MAXPATHLEN], initcgroup[MAXPATHLEN];
+	char *cgmnt = mntent->mnt_dir;
 	int ret = 0;
 
-	snprintf(tasks, MAXPATHLEN, "%s/tasks", path);
+	snprintf(tasks, MAXPATHLEN, "%s%s/lxc/%s/tasks", cgmnt,
+		get_init_cgroup(NULL, mntent, initcgroup), name);
 
 	f = fopen(tasks, "w");
 	if (!f) {
@@ -258,6 +232,44 @@ int lxc_cgroup_attach(const char *path, pid_t pid)
 }
 
 /*
+ * for each mounted cgroup, attach a pid to the cgroup for the container
+ */
+int lxc_cgroup_attach(const char *name, pid_t pid)
+{
+	struct mntent *mntent;
+	FILE *file = NULL;
+	int err = -1;
+	int found = 0;
+
+	file = setmntent(MTAB, "r");
+	if (!file) {
+		SYSERROR("failed to open %s", MTAB);
+		return -1;
+	}
+
+	while ((mntent = getmntent(file))) {
+		DEBUG("checking '%s' (%s)", mntent->mnt_dir, mntent->mnt_type);
+
+		if (strcmp(mntent->mnt_type, "cgroup"))
+			continue;
+
+		INFO("[%d] found cgroup mounted at '%s',opts='%s'",
+		     ++found, mntent->mnt_dir, mntent->mnt_opts);
+
+		err = lxc_one_cgroup_attach(name, mntent, pid);
+		if (err)
+			goto out;
+	};
+
+	if (!found)
+		ERROR("No cgroup mounted on the system");
+
+out:
+	endmntent(file);
+	return err;
+}
+
+/*
  * rename cgname, which is under cgparent, to a new name starting
  * with 'cgparent/dead'.  That way cgname can be reused.  Return
  * 0 on success, -1 on failure.
@@ -367,20 +379,13 @@ static int lxc_one_cgroup_create(const char *name,
 		return -1;
 	}
 
-	/* Let's add the pid to the 'tasks' file */
-	if (lxc_cgroup_attach(cgname, pid)) {
-		SYSERROR("failed to attach pid '%d' to '%s'", pid, cgname);
-		rmdir(cgname);
-		return -1;
-	}
-
 	INFO("created cgroup '%s'", cgname);
 
 	return 0;
 }
 
 /*
- * for each mounted cgroup, create a cgroup for the container
+ * for each mounted cgroup, create a cgroup for the container and attach a pid
  */
 int lxc_cgroup_create(const char *name, pid_t pid)
 {
@@ -407,6 +412,10 @@ int lxc_cgroup_create(const char *name, pid_t pid)
 		err = lxc_one_cgroup_create(name, mntent, pid);
 		if (err)
 			goto out;
+
+		err = lxc_one_cgroup_attach(name, mntent, pid);
+		if (err)
+			goto out;
 	};
 
 	if (!found)
diff --git a/src/lxc/cgroup.h b/src/lxc/cgroup.h
index 611d9f4..3c90696 100644
--- a/src/lxc/cgroup.h
+++ b/src/lxc/cgroup.h
@@ -30,6 +30,6 @@ extern int lxc_cgroup_create(const char *name, pid_t pid);
 extern int lxc_cgroup_destroy(const char *name);
 extern int lxc_cgroup_path_get(char **path, const char *subsystem, const char *name);
 extern int lxc_cgroup_nrtasks(const char *name);
-extern int lxc_cgroup_attach(const char *path, pid_t pid);
+extern int lxc_cgroup_attach(const char *name, pid_t pid);
 extern int lxc_ns_is_mounted(void);
 #endif
diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c
index 4883327..955e9f4 100644
--- a/src/lxc/lxc_attach.c
+++ b/src/lxc/lxc_attach.c
@@ -35,7 +35,7 @@
 #include "commands.h"
 #include "arguments.h"
 #include "caps.h"
-#include "attach.h"
+#include "cgroup.h"
 #include "confile.h"
 #include "start.h"
 #include "sync.h"
@@ -150,7 +150,7 @@ int main(int argc, char *argv[])
 		if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE))
 			return -1;
 
-		if (!elevated_privileges && lxc_attach_proc_to_cgroups(pid, init_ctx))
+		if (!elevated_privileges && lxc_cgroup_attach(my_args.name, pid))
 			return -1;
 
 		/* tell the child we are done initializing */
-- 
1.7.1





More information about the lxc-devel mailing list