aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/lxc/cgroup.c')
-rw-r--r--src/lxc/cgroup.c171
1 files changed, 146 insertions, 25 deletions
diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c
index a8e6c27..8077a8d 100644
--- a/src/lxc/cgroup.c
+++ b/src/lxc/cgroup.c
@@ -81,9 +81,65 @@ static char *hasmntopt_multiple(struct mntent *mntent, const char *options)
return hasmntopt(mntent, ptr);
}
+/*
+ * get_init_cgroup: get the cgroup init is in.
+ * dsg: preallocated buffer to put the output in
+ * subsystem: the exact cgroup subsystem to look up
+ * mntent: a mntent (from getmntent) whose mntopts contains the
+ * subsystem to look up.
+ *
+ * subsystem and mntent can both be NULL, in which case we return
+ * the first entry in /proc/1/cgroup.
+ *
+ * Returns a pointer to the answer, which may be "".
+ */
+static char *get_init_cgroup(const char *subsystem, struct mntent *mntent,
+ char *dsg)
+{
+ FILE *f;
+ char *c, *c2;
+ char line[MAXPATHLEN];
+
+ *dsg = '\0';
+ f = fopen("/proc/1/cgroup", "r");
+ if (!f)
+ return dsg;
+
+ while (fgets(line, MAXPATHLEN, f)) {
+ c = index(line, ':');
+ if (!c)
+ continue;
+ c++;
+ c2 = index(c, ':');
+ if (!c2)
+ continue;
+ *c2 = '\0';
+ c2++;
+ if (!subsystem && !mntent)
+ goto good;
+ if (subsystem && strcmp(c, subsystem) != 0)
+ continue;
+ if (mntent && !hasmntopt(mntent, c))
+ continue;
+good:
+ DEBUG("get_init_cgroup: found init cgroup for subsys %s at %s\n",
+ subsystem, c2);
+ strncpy(dsg, c2, MAXPATHLEN);
+ c = &dsg[strlen(dsg)-1];
+ if (*c == '\n')
+ *c = '\0';
+ goto found;
+ }
+
+found:
+ fclose(f);
+ return dsg;
+}
+
static int get_cgroup_mount(const char *subsystem, char *mnt)
{
struct mntent *mntent;
+ char initcgroup[MAXPATHLEN];
FILE *file = NULL;
file = setmntent(MTAB, "r");
@@ -97,14 +153,22 @@ static int get_cgroup_mount(const char *subsystem, char *mnt)
if (strcmp(mntent->mnt_type, "cgroup"))
continue;
if (!subsystem || hasmntopt_multiple(mntent, subsystem)) {
- strcpy(mnt, mntent->mnt_dir);
+ int ret;
+ ret = snprintf(mnt, MAXPATHLEN, "%s%s/lxc",
+ mntent->mnt_dir,
+ get_init_cgroup(subsystem, NULL,
+ initcgroup));
+ if (ret < 0 || ret >= MAXPATHLEN)
+ goto fail;
fclose(file);
DEBUG("using cgroup mounted at '%s'", mnt);
return 0;
}
};
- DEBUG("Failed to find cgroup for %s\n", subsystem ? subsystem : "(NULL)");
+fail:
+ DEBUG("Failed to find cgroup for %s\n",
+ subsystem ? subsystem : "(NULL)");
fclose(file);
@@ -195,38 +259,76 @@ int lxc_cgroup_attach(const char *path, pid_t pid)
}
/*
+ * 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.
+ */
+int try_to_move_cgname(char *cgparent, char *cgname)
+{
+ char *newdir;
+
+ /* tempnam problems don't matter here - cgroupfs will prevent
+ * duplicates if we race, and we'll just fail at that (unlikely)
+ * point
+ */
+
+ newdir = tempnam(cgparent, "dead");
+ if (!newdir)
+ return -1;
+ if (rename(cgname, newdir))
+ return -1;
+ WARN("non-empty cgroup %s renamed to %s, please manually inspect it\n",
+ cgname, newdir);
+
+ return 0;
+}
+
+/*
* create a cgroup for the container in a particular subsystem.
- * XXX TODO we will of course want to use cgroup_path{subsystem}/lxc/name,
- * not just cgroup_path{subsystem}/name.
*/
static int lxc_one_cgroup_create(const char *name,
struct mntent *mntent, pid_t pid)
{
- char cgname[MAXPATHLEN];
+ char cginit[MAXPATHLEN], cgname[MAXPATHLEN], cgparent[MAXPATHLEN];
char clonechild[MAXPATHLEN];
- int flags;
-
- snprintf(cgname, MAXPATHLEN, "%s/%s", mntent->mnt_dir, name);
+ char initcgroup[MAXPATHLEN];
+ int flags, ret;
+
+ /* cgparent is the parent dir, /sys/fs/cgroup/<cgroup>/<init-cgroup>/lxc */
+ /* (remember get_init_cgroup() returns a path starting with '/') */
+ /* cgname is the full name, /sys/fs/cgroup/</cgroup>/<init-cgroup>/lxc/name */
+ ret = snprintf(cginit, MAXPATHLEN, "%s%s", mntent->mnt_dir,
+ get_init_cgroup(NULL, mntent, initcgroup));
+ if (ret < 0 || ret >= MAXPATHLEN) {
+ SYSERROR("Failed creating pathname for init's cgroup (%d)\n", ret);
+ return -1;
+ }
- /*
- * There is a previous cgroup, assume it is empty,
- * otherwise that fails
- */
- if (!access(cgname, F_OK) && rmdir(cgname)) {
- SYSERROR("failed to remove previous cgroup '%s'", cgname);
+ ret = snprintf(cgparent, MAXPATHLEN, "%s/lxc", cginit);
+ if (ret < 0 || ret >= MAXPATHLEN) {
+ SYSERROR("Failed creating pathname for cgroup parent (%d)\n", ret);
+ return -1;
+ }
+ ret = snprintf(cgname, MAXPATHLEN, "%s/%s", cgparent, name);
+ if (ret < 0 || ret >= MAXPATHLEN) {
+ SYSERROR("Failed creating pathname for cgroup (%d)\n", ret);
return -1;
}
flags = get_cgroup_flags(mntent);
- /* We have the deprecated ns_cgroup subsystem */
+ /* Do we have the deprecated ns_cgroup subsystem? */
if (flags & CGROUP_NS_CGROUP) {
WARN("using deprecated ns_cgroup");
- return cgroup_rename_nsgroup(mntent->mnt_dir, cgname, pid);
+ return cgroup_rename_nsgroup(cgparent, cgname, pid);
}
- snprintf(clonechild, MAXPATHLEN, "%s/cgroup.clone_children",
- mntent->mnt_dir);
+ ret = snprintf(clonechild, MAXPATHLEN, "%s/cgroup.clone_children",
+ cginit);
+ if (ret < 0 || ret >= MAXPATHLEN) {
+ SYSERROR("Failed creating pathname for clone_children (%d)\n", ret);
+ return -1;
+ }
/* we check if the kernel has clone_children, at this point if there
* no clone_children neither ns_cgroup, that means the cgroup is mounted
@@ -237,14 +339,31 @@ static int lxc_one_cgroup_create(const char *name,
return -1;
}
- /* we enable the clone_children flag of the cgroup */
+ /* enable the clone_children flag of the cgroup */
if (cgroup_enable_clone_children(clonechild)) {
SYSERROR("failed to enable 'clone_children flag");
return -1;
}
+ /* if /sys/fs/cgroup/<cgroup>/<init-cgroup>/lxc does not exist, create it */
+ if (access(cgparent, F_OK) && mkdir(cgparent, 0755)) {
+ SYSERROR("failed to create '%s' directory", cgparent);
+ return -1;
+ }
+
+ /*
+ * There is a previous cgroup. Try to delete it. If that fails
+ * (i.e. it is not empty) try to move it out of the way.
+ */
+ if (!access(cgname, F_OK) && rmdir(cgname)) {
+ if (try_to_move_cgname(cgparent, cgname)) {
+ SYSERROR("failed to remove previous cgroup '%s'", cgname);
+ return -1;
+ }
+ }
+
/* Let's create the cgroup */
- if (mkdir(cgname, 0700)) {
+ if (mkdir(cgname, 0755)) {
SYSERROR("failed to create '%s' directory", cgname);
return -1;
}
@@ -301,11 +420,14 @@ out:
}
-int lxc_one_cgroup_destroy(const char *cgmnt, const char *name)
+int lxc_one_cgroup_destroy(struct mntent *mntent, const char *name)
{
- char cgname[MAXPATHLEN];
+ char cgname[MAXPATHLEN], initcgroup[MAXPATHLEN];
+ char *cgmnt = mntent->mnt_dir;
- snprintf(cgname, MAXPATHLEN, "%s/%s", cgmnt, name);
+ snprintf(cgname, MAXPATHLEN, "%s%s/lxc/%s", cgmnt,
+ get_init_cgroup(NULL, mntent, initcgroup), name);
+ DEBUG("destroying %s\n", cgname);
if (rmdir(cgname)) {
SYSERROR("failed to remove cgroup '%s'", cgname);
return -1;
@@ -333,8 +455,7 @@ int lxc_cgroup_destroy(const char *name)
while ((mntent = getmntent(file))) {
if (!strcmp(mntent->mnt_type, "cgroup")) {
- DEBUG("destroying %s %s\n", mntent->mnt_dir, name);
- ret = lxc_one_cgroup_destroy(mntent->mnt_dir, name);
+ ret = lxc_one_cgroup_destroy(mntent, name);
if (ret) {
fclose(file);
return ret;