diff options
Diffstat (limited to 'src/lxc/cgroup.c')
-rw-r--r-- | src/lxc/cgroup.c | 171 |
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; |