Add a monitor command to get the cgroup for a running container.  This
allows container r1 started from /var/lib/lxc and container r1 started
from /home/ubuntu/lxcbase to pick unique cgroup directories (which
will be /sys/fs/cgroup/$subsys/lxc/r1 and .../r1-1), and all the lxc-*
tools to get that path over the monitor at lxcpath.

Rework the cgroup code.  Before, if /sys/fs/cgroup/$subsys/lxc/r1
already existed, it would be moved to 'deadXXXXX', and a new r1 created.
Instead, if r1 exists, use r1-1, r1-2, etc.

I ended up removing both the use of cgroup.clone_children and support
for ns cgroup.  Presumably we'll want to put support for ns cgroup
back in for older kernels.  Instead of guessing whether or not we
have clone_children support, just always explicitly do the only thing
that feature buys us - set cpuset.{cpus,mems} for newly created cgroups.

Note that upstream kernel is working toward strict hierarchical
limit enforcements, which will be good for us.

NOTE - I am changing the lxc_answer struct size.  This means that
upgrades to this version while containers are running will result
in lxc_* commands on pre-running containers will fail.

Changelog: (v3)
   implement cgroup attach
   fix a subtle bug arising when we lxc_get_cgpath() returned
     STOPPED rather than -1 (STOPPED is 0, and 0 meant success).
   Rename some functions and add detailed comments above most.
   Drop all my lxc_attach changes in favor of those by Christian
     Seiler (which are mostly the same, but improved).

Signed-off-by: Serge Hallyn <serge.hal...@ubuntu.com>
---
 src/lxc/attach.c       |   1 -
 src/lxc/cgroup.c       | 941 ++++++++++++++++++++++++-------------------------
 src/lxc/cgroup.h       |  18 +-
 src/lxc/commands.c     |  11 +
 src/lxc/commands.h     |   3 +
 src/lxc/conf.c         |   7 +-
 src/lxc/conf.h         |   2 +-
 src/lxc/freezer.c      |  35 +-
 src/lxc/lxc.h          |  28 +-
 src/lxc/lxc_attach.c   |   2 +-
 src/lxc/lxc_cgroup.c   |   4 +-
 src/lxc/lxc_unshare.c  |  10 -
 src/lxc/lxccontainer.c |   4 +-
 src/lxc/lxcutmp.c      |   2 +-
 src/lxc/start.c        |  54 ++-
 src/lxc/start.h        |   2 +-
 src/lxc/state.c        |   7 +-
 src/lxc/stop.c         |   3 +-
 src/tests/Makefile.am  |   4 +-
 src/tests/cgpath.c     | 164 +++++++++
 src/tests/lxcpath.c    |   2 +-
 21 files changed, 774 insertions(+), 530 deletions(-)
 create mode 100644 src/tests/cgpath.c

diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index af3d7a0..e0a40bd 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -42,7 +42,6 @@
 #include "log.h"
 #include "attach.h"
 #include "caps.h"
-#include "cgroup.h"
 #include "config.h"
 #include "apparmor.h"
 
diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c
index 6630d6c..4d7de02 100644
--- a/src/lxc/cgroup.c
+++ b/src/lxc/cgroup.c
@@ -38,6 +38,7 @@
 
 #include "error.h"
 #include "config.h"
+#include "commands.h"
 
 #include <lxc/log.h>
 #include <lxc/cgroup.h>
@@ -53,11 +54,6 @@ lxc_log_define(lxc_cgroup, lxc);
 
 #define MTAB "/proc/mounts"
 
-enum {
-       CGROUP_NS_CGROUP = 1,
-       CGROUP_CLONE_CHILDREN,
-};
-
 /* Check if a mount is a cgroup hierarchy for any subsystem.
  * Return the first subsystem found (or NULL if none).
  */
@@ -93,22 +89,27 @@ static char *mount_has_subsystem(const struct mntent 
*mntent)
 
 /*
  * 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: the exact cgroup subsystem to look up (I.e. "freezer")
+ * @mntent: a mntent (from getmntent) whose mntopts contains the subsystem to
+ * look up.
+ * @dsg: preallocated buffer of at least size MAXPATHLEN in which the path will
+ * be copied.
+ * @prependslash: if 1, the path will have a '/' prepended for easy of use by
+ * the caller.
  *
  * 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 "".
+ * Returns a pointer to the answer (which is just the passed-in @dsg), which
+ * may be "".
  */
 static char *get_init_cgroup(const char *subsystem, struct mntent *mntent,
-                            char *dsg)
+                            char *dsg, int prependslash)
 {
        FILE *f;
        char *c, *c2;
        char line[MAXPATHLEN];
+       int ret;
 
        *dsg = '\0';
        f = fopen("/proc/1/cgroup", "r");
@@ -134,10 +135,18 @@ static char *get_init_cgroup(const char *subsystem, 
struct mntent *mntent,
 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];
+               ret = snprintf(dsg, MAXPATHLEN, "%s%s", prependslash ? "/" : 
"", c2);
+               if (ret < 0 || ret >= MAXPATHLEN) {
+                       WARN("init cgroup path name was too long.");
+                       goto found;
+               }
+               if (ret < 1)
+                       goto found;
+
+               c = &dsg[ret-1];
                if (*c == '\n')
                        *c = '\0';
+
                goto found;
        }
 
@@ -146,27 +155,22 @@ found:
        return dsg;
 }
 
-static int get_cgroup_flags(struct mntent *mntent)
-{
-       int flags = 0;
-
-
-       if (hasmntopt(mntent, "ns"))
-               flags |= CGROUP_NS_CGROUP;
-
-       if (hasmntopt(mntent, "clone_children"))
-               flags |= CGROUP_CLONE_CHILDREN;
-
-       DEBUG("cgroup %s has flags 0x%x", mntent->mnt_dir, flags);
-       return flags;
-}
-
+/*
+ * Determine mountpoint for a cgroup subsystem, plus the cgroup path in that
+ * subsytem of the container init.
+ * @subsystem: cgroup subsystem (i.e. freezer).  If this is NULL, the first
+ * cgroup mountpoint with any subsystems is used.
+ * @mnt: a passed-in buffer of at least size MAXPATHLEN into which the path
+ * is copied.
+ *
+ * Returns 0 on success, -1 on error.
+ */
 static int get_cgroup_mount(const char *subsystem, char *mnt)
 {
        struct mntent *mntent;
-       char initcgroup[MAXPATHLEN];
+       char initcgroup[MAXPATHLEN], *init;
        FILE *file = NULL;
-       int ret, flags, err = -1;
+       int ret, err = -1;
 
        file = setmntent(MTAB, "r");
        if (!file) {
@@ -181,16 +185,14 @@ static int get_cgroup_mount(const char *subsystem, char 
*mnt)
                if (subsystem) {
                        if (!hasmntopt(mntent, subsystem))
                                continue;
-               }
-               else {
+               } else {
                        if (!mount_has_subsystem(mntent))
                                continue;
                }
 
-               flags = get_cgroup_flags(mntent);
-               ret = snprintf(mnt, MAXPATHLEN, "%s%s%s", mntent->mnt_dir,
-                              get_init_cgroup(subsystem, NULL, initcgroup),
-                              (flags & CGROUP_NS_CGROUP) ? "" : "/lxc");
+               init = get_init_cgroup(NULL, mntent, initcgroup, 1);
+               ret = snprintf(mnt, MAXPATHLEN, "%s%s", mntent->mnt_dir,
+                               init);
                if (ret < 0 || ret >= MAXPATHLEN)
                        goto fail;
 
@@ -207,180 +209,370 @@ out:
        return err;
 }
 
-int lxc_ns_is_mounted(void)
+/*
+ * cgroup_path_get: Calculate the full path for a particular subsystem, plus
+ * a passed-in (to be appended) relative cgpath for a container.
+ * @path: a char** into which a pointer to the answer is copied
+ * @subsystem: subsystem of interest (i.e. freezer).
+ * @cgpath: a container's (relative) cgroup path, i.e. "/lxc/c1".
+ *
+ * Returns 0 on success, -1 on error.
+ *
+ * The answer is written in a static char[MAXPATHLEN] in this function and
+ * should not be freed.
+ */
+extern int cgroup_path_get(char **path, const char *subsystem, const char 
*cgpath)
 {
        static char        buf[MAXPATHLEN];
+       static char        retbuf[MAXPATHLEN];
+       int rc;
 
-       return (get_cgroup_mount("ns", buf) == 0);
+       /* lxc_cgroup_set passes a state object for the subsystem,
+        * so trim it to just the subsystem part */
+       if (subsystem) {
+               rc = snprintf(retbuf, MAXPATHLEN, "%s", subsystem);
+               if (rc < 0 || rc >= MAXPATHLEN) {
+                       ERROR("subsystem name too long");
+                       return -1;
+               }
+               char *s = index(retbuf, '.');
+               if (s)
+                       *s = '\0';
+               DEBUG("%s: called for subsys %s name %s\n", __func__, retbuf, 
cgpath);
+       }
+       if (get_cgroup_mount(subsystem ? retbuf : NULL, buf)) {
+               ERROR("cgroup is not mounted");
+               return -1;
+       }
+
+       rc = snprintf(retbuf, MAXPATHLEN, "%s/%s", buf, cgpath);
+       if (rc < 0 || rc >= MAXPATHLEN) {
+               ERROR("name too long");
+               return -1;
+       }
+
+       DEBUG("%s: returning %s for subsystem %s", __func__, retbuf, subsystem);
+
+       *path = retbuf;
+       return 0;
 }
 
-static int cgroup_rename_nsgroup(const char *mnt, const char *name, pid_t pid)
+/*
+ * Calculate a container's cgroup path for a particular subsystem.  This
+ * is the cgroup path relative to the root of the cgroup filesystem.
+ * @path: A char ** into which we copy the char* containing the answer
+ * @subsystem: the cgroup subsystem of interest (i.e. freezer)
+ * @name: container name
+ * @lxcpath: the lxcpath in which the container is running.
+ *
+ * Returns 0 on success, -1 on error.
+ *
+ * Note that the char* copied into *path is a static char[MAXPATHLEN] in
+ * commands.c:receive_answer().  It should not be freed.
+ */
+extern int lxc_get_cgpath(const char **path, const char *subsystem, const char 
*name, const char *lxcpath)
 {
-       char oldname[MAXPATHLEN];
-       char newname[MAXPATHLEN];
-       int ret;
+       struct lxc_command command = {
+               .request = { .type = LXC_COMMAND_CGROUP },
+       };
+
+       int ret, stopped = 0;
 
-       ret = snprintf(oldname, MAXPATHLEN, "%s/%d", mnt, pid);
-       if (ret >= MAXPATHLEN)
+       ret = lxc_command(name, &command, &stopped, lxcpath);
+       if (ret < 0) {
+               if (!stopped)
+                       ERROR("failed to send command");
                return -1;
+       }
 
-       ret = snprintf(newname, MAXPATHLEN, "%s/%s", mnt, name);
-       if (ret >= MAXPATHLEN)
+       if (!ret) {
+               WARN("'%s' has stopped before sending its state", name);
                return -1;
+       }
 
-       if (rename(oldname, newname)) {
-               SYSERROR("failed to rename cgroup %s->%s", oldname, newname);
+       if (command.answer.ret < 0 || command.answer.pathlen < 0) {
+               ERROR("failed to get state for '%s': %s",
+                       name, strerror(-command.answer.ret));
                return -1;
        }
 
-       DEBUG("'%s' renamed to '%s'", oldname, newname);
+       *path = command.answer.path;
 
        return 0;
 }
 
-static int cgroup_enable_clone_children(const char *path)
+/*
+ * lxc_cgroup_path_get: determine full pathname for a cgroup
+ * file for a specific container.
+ * @path: char ** used to return the answer.  The char * will point
+ * into the static char* retuf from cgroup_path_get() (so no need
+ * to free it).
+ * @subsystem: cgroup subsystem (i.e. "freezer") for which to
+ * return an answer.  If NULL, then the first cgroup entry in
+ * mtab will be used.
+ *
+ * This is the exported function, which determines cgpath from the
+ * monitor running in lxcpath.
+ *
+ * Returns 0 on success, < 0 on error.
+ */
+int lxc_cgroup_path_get(char **path, const char *subsystem, const char *name, 
const char *lxcpath)
 {
-       FILE *f;
-       int ret = 0;
+       const char *cgpath;
 
-       f = fopen(path, "w");
-       if (!f) {
-               SYSERROR("failed to open '%s'", path);
+       if (lxc_get_cgpath(&cgpath, subsystem, name, lxcpath) < 0)
                return -1;
-       }
 
-       if (fprintf(f, "1") < 1) {
-               ERROR("failed to write flag to '%s'", path);
-               ret = -1;
+       return cgroup_path_get(path, subsystem, cgpath);
+}
+
+/*
+ * small helper which simply write a value into a (cgroup) file
+ */
+static int do_cgroup_set(const char *path, const char *value)
+{
+       int fd, ret;
+
+       if ((fd = open(path, O_WRONLY)) < 0) {
+               SYSERROR("open %s : %s", path, strerror(errno));
+               return -1;
        }
 
-       fclose(f);
+       if ((ret = write(fd, value, strlen(value))) < 0) {
+               close(fd);
+               SYSERROR("write %s : %s", path, strerror(errno));
+               return ret;
+       }
 
-       return ret;
+       if ((ret = close(fd)) < 0) {
+               SYSERROR("close %s : %s", path, strerror(errno));
+               return ret;
+       }
+       return 0;
 }
 
-static int lxc_one_cgroup_finish_attach(int fd, pid_t pid)
+/*
+ * small helper to write a value into a file in a particular directory.
+ * @cgpath: the directory in which to find the file
+ * @filename: the file (under cgpath) to which to write
+ * @value: what to write
+ *
+ * Returns 0 on success, < 0 on error.
+ */
+int lxc_cgroup_set_bypath(const char *cgpath, const char *filename, const char 
*value)
 {
-       char buf[32];
-       int ret;
+       int ret;
+       char *dirpath;
+       char path[MAXPATHLEN];
 
-       snprintf(buf, 32, "%ld", (long)pid);
+       ret = cgroup_path_get(&dirpath, filename, cgpath);
+       if (ret)
+               return -1;
 
-       ret = write(fd, buf, strlen(buf));
-       if (ret <= 0) {
-               SYSERROR("failed to write pid '%ld' to fd '%d'", (long)pid, fd);
-               ret = -1;
-       } else {
-               ret = 0;
-       }
+       ret = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename);
+       if (ret < 0 || ret >= MAXPATHLEN) {
+               ERROR("pathname too long");
+               return -1;
+       }
 
-       close(fd);
-       return ret;
+       return do_cgroup_set(path, value);
 }
 
-static int lxc_one_cgroup_dispose_attach(int fd)
+/*
+ * set a cgroup value for a container
+ *
+ * @name: name of the container
+ * @filename: the cgroup file (i.e. freezer.state) whose value to change
+ * @value: the value to write to the file
+ * @lxcpath: the lxcpath under which the container is running.
+ *
+ * Returns 0 on success, < 0 on error.
+ */
+
+int lxc_cgroup_set(const char *name, const char *filename, const char *value,
+                  const char *lxcpath)
 {
-       close(fd);
-       return 0;
+       int ret;
+       char *dirpath;
+       char path[MAXPATHLEN];
+
+       ret = lxc_cgroup_path_get(&dirpath, filename, name, lxcpath);
+       if (ret)
+               return -1;
+
+       ret = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename);
+       if (ret < 0 || ret >= MAXPATHLEN) {
+               ERROR("pathname too long");
+               return -1;
+       }
+
+       return do_cgroup_set(path, value);
 }
 
-static int lxc_one_cgroup_prepare_attach(const char *name,
-                                        struct mntent *mntent)
+/*
+ * Get value of a cgroup setting for a container.
+ *
+ * @name: name of the container
+ * @filename: the cgroup file to read (i.e. 'freezer.state')
+ * @value: a preallocated char* into which to copy the answer
+ * @len: the length of pre-allocated @value
+ * @lxcpath: the lxcpath in which the container is running (i.e.
+ * /var/lib/lxc)
+ *
+ * Returns < 0 on error, or the number of bytes read.
+ *
+ * If you pass in NULL value or 0 len, then you are asking for the size of the
+ * file.
+ *
+ * Note that we can't get the file size quickly through stat or lseek.
+ * Therefore if you pass in len > 0 but less than the file size, your only
+ * indication will be that the return value will be equal to the passed-in ret.
+ * We will not return the actual full file size.
+ */
+int lxc_cgroup_get(const char *name, const char *filename, char *value,
+                  size_t len, const char *lxcpath)
 {
-       int fd;
-       char tasks[MAXPATHLEN], initcgroup[MAXPATHLEN];
-       char *cgmnt = mntent->mnt_dir;
-       int flags;
+       int fd, ret = -1;
+       char *dirpath;
+       char path[MAXPATHLEN];
        int rc;
 
-       flags = get_cgroup_flags(mntent);
+       ret = lxc_cgroup_path_get(&dirpath, filename, name, lxcpath);
+       if (ret)
+               return -1;
 
-       rc = snprintf(tasks, MAXPATHLEN, "%s%s%s/%s/tasks", cgmnt,
-                get_init_cgroup(NULL, mntent, initcgroup),
-                (flags & CGROUP_NS_CGROUP) ? "" : "/lxc",
-                name);
+       rc = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename);
        if (rc < 0 || rc >= MAXPATHLEN) {
                ERROR("pathname too long");
                return -1;
        }
 
-       fd = open(tasks, O_WRONLY);
+       fd = open(path, O_RDONLY);
        if (fd < 0) {
-               SYSERROR("failed to open '%s'", tasks);
+               ERROR("open %s : %s", path, strerror(errno));
                return -1;
        }
 
-       return fd;
-}
-
-static int lxc_one_cgroup_attach(const char *name, struct mntent *mntent, 
pid_t pid)
-{
-       int fd;
-
-       fd = lxc_one_cgroup_prepare_attach(name, mntent);
-       if (fd < 0) {
-               return -1;
+       if (!len || !value) {
+               char buf[100];
+               int count = 0;
+               while ((ret = read(fd, buf, 100)) > 0)
+                       count += ret;
+               if (ret >= 0)
+                       ret = count;
+       } else {
+               memset(value, 0, len);
+               ret = read(fd, value, len);
        }
 
-       return lxc_one_cgroup_finish_attach(fd, pid);
+       if (ret < 0)
+               ERROR("read %s : %s", path, strerror(errno));
+
+       close(fd);
+       return ret;
 }
 
-int lxc_cgroup_dispose_attach(void *data)
+int lxc_cgroup_nrtasks(const char *cgpath)
 {
-       int *fds = data;
-       int ret, err;
+       char *dpath;
+       char path[MAXPATHLEN];
+       int pid, ret, count = 0;
+       FILE *file;
+       int rc;
 
-       if (!fds) {
-               return 0;
-       }
+       ret = cgroup_path_get(&dpath, NULL, cgpath);
+       if (ret)
+               return -1;
 
-       ret = 0;
+       rc = snprintf(path, MAXPATHLEN, "%s/tasks", dpath);
+       if (rc < 0 || rc >= MAXPATHLEN) {
+               ERROR("pathname too long");
+               return -1;
+       }
 
-       for (; *fds >= 0; fds++) {
-               err = lxc_one_cgroup_dispose_attach(*fds);
-               if (err) {
-                       ret = err;
-               }
+       file = fopen(path, "r");
+       if (!file) {
+               SYSERROR("fopen '%s' failed", path);
+               return -1;
        }
 
-       free(data);
+       while (fscanf(file, "%d", &pid) != EOF)
+               count++;
 
-       return ret;
+       fclose(file);
+
+       return count;
 }
 
-int lxc_cgroup_finish_attach(void *data, pid_t pid)
+/*
+ * Set of helper functions to make sure that, when we create a new
+ * cpuset cgroup, its cpus and mems files inherit the values in the
+ * parent cgroup
+ */
+static long get_value(const char *dir, const char *file)
 {
-       int *fds = data;
-       int err;
+       FILE *f;
+       char path[MAXPATHLEN];
+       int ret, retv;
 
-       if (!fds) {
+       retv = snprintf(path, MAXPATHLEN, "%s/%s", dir, file);
+       if (retv < 0 || retv >= MAXPATHLEN)
                return 0;
-       }
+       f = fopen(path, "r");
+       ret = fscanf(f, "%d", &retv);
+       fclose(f);
+       if (ret != 1)
+               return 0;
+       return retv;
+}
 
-       for (; *fds >= 0; fds++) {
-               err = lxc_one_cgroup_finish_attach(*fds, pid);
-               if (err) {
-                       /* get rid of the rest of them */
-                       lxc_cgroup_dispose_attach(data);
-                       return -1;
-               }
-               *fds = -1;
-       }
+static void set_value(const char *dir, const char *file, long v)
+{
+       FILE *f;
+       char path[MAXPATHLEN];
+       int retv;
 
-       free(data);
+       retv = snprintf(path, MAXPATHLEN, "%s/%s", dir, file);
+       if (retv < 0 || retv >= MAXPATHLEN)
+               return;
+       f = fopen(path, "w");
+       fprintf(f, "%ld\n", v);
+       fclose(f);
+}
 
-       return 0;
+static void setup_cpuset(const char *path)
+{
+       /* copy parent values for mems_allowed and cpus_allowed */
+       char *parentpath = strdup(path);
+       char *p;
+       long v;
+       if ((p = rindex(parentpath, '/')) == NULL)
+               goto out;
+       v = get_value(parentpath, "cpuset.mems");
+       set_value(path, "cpuset.mems", v);
+       v = get_value(parentpath, "cpuset.cpus");
+       set_value(path, "cpuset.cpus", v);
+       
+out:
+       free(parentpath);
 }
 
-int lxc_cgroup_prepare_attach(const char *name, void **data)
+/*
+ * Make sure the 'cgroup group' exists, so that we don't have to worry about
+ * that later.
+ *
+ * @lxcgroup: the cgroup group, i.e. 'lxc' by default.
+ *
+ * See detailed comments at lxc_cgroup_path_create for more information.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static int create_lxcgroups(const char *lxcgroup)
 {
-       struct mntent *mntent;
        FILE *file = NULL;
-       int err = -1;
-       int found = 0;
-       int *fds;
-       int i;
-       static const int MAXFDS = 256;
+       struct mntent *mntent;
+       int ret, retv = -1;
+       char path[MAXPATHLEN], initpath[MAXPATHLEN];
 
        file = setmntent(MTAB, "r");
        if (!file) {
@@ -388,205 +580,145 @@ int lxc_cgroup_prepare_attach(const char *name, void 
**data)
                return -1;
        }
 
-       /* create a large enough buffer for all practical
-        * use cases
-        */
-       fds = malloc(sizeof(int) * MAXFDS);
-       if (!fds) {
-               err = -1;
-               goto out;
-       }
-       for (i = 0; i < MAXFDS; i++) {
-               fds[i] = -1;
-       }
-
-       err = 0;
-       i = 0;
        while ((mntent = getmntent(file))) {
-               if (i >= MAXFDS - 1) {
-                       ERROR("too many cgroups to attach to, aborting");
-                       lxc_cgroup_dispose_attach(fds);
-                       errno = ENOMEM;
-                       err = -1;
-                       goto out;
-               }
-
-               DEBUG("checking '%s' (%s)", mntent->mnt_dir, mntent->mnt_type);
+               char *init = get_init_cgroup(NULL, mntent, initpath, 1);
 
                if (strcmp(mntent->mnt_type, "cgroup"))
                        continue;
                if (!mount_has_subsystem(mntent))
                        continue;
 
-               INFO("[%d] found cgroup mounted at '%s',opts='%s'",
-                    ++found, mntent->mnt_dir, mntent->mnt_opts);
-
-               fds[i] = lxc_one_cgroup_prepare_attach(name, mntent);
-               if (fds[i] < 0) {
-                       err = fds[i];
-                       lxc_cgroup_dispose_attach(fds);
-                       goto out;
+               /* 
+                * TODO - handle case where lxcgroup has subdirs?  (i.e. 
build/l1)
+                * May not be worthwhile - remember cgroup depth has perf 
penalties
+                * */
+               ret = snprintf(path, MAXPATHLEN, "%s%s/%s",
+                              mntent->mnt_dir, init, lxcgroup ? lxcgroup : 
"lxc");
+               if (ret < 0 || ret >= MAXPATHLEN)
+                       goto fail;
+               if (access(path, F_OK)) {
+                       ret = mkdir(path, 0755);
+                       if (ret == -1 && errno != EEXIST) {
+                               SYSERROR("failed to create '%s' directory", 
path);
+                               goto fail;
+                       }
+               } else if (hasmntopt(mntent, "cpuset")) {
+                       setup_cpuset(path);
                }
-               i++;
-       };
-
-       if (!found)
-               ERROR("No cgroup mounted on the system");
-
-       *data = fds;
 
-out:
-       endmntent(file);
-       return err;
-}
-
-/*
- * for each mounted cgroup, attach a pid to the cgroup for the container
- */
-int lxc_cgroup_attach(const char *name, pid_t pid)
-{
-       void *data = NULL;
-       int ret;
-
-       ret = lxc_cgroup_prepare_attach(name, &data);
-       if (ret < 0) {
-               return ret;
        }
 
-       return lxc_cgroup_finish_attach(data, pid);
+       retv = 0;
+fail:
+       endmntent(file);
+       return retv;
 }
 
 /*
- * 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.
+ * For a new container, find a cgroup path which is unique in all cgroup 
mounts.
+ * I.e. if r1 is already running, then /lxc/r1-1 may be used.
+ *
+ * @lxcgroup: the cgroup 'group' the contaienr should run in.  By default, this
+ * is just 'lxc'.  Admins may wish to group some containers into other groups,
+ * i.e. 'build', to take advantage of cgroup hierarchy to simplify group
+ * administration.  Also, unprivileged users who are placed into a cgroup by
+ * libcgroup_pam will be using that cgroup rather than the system-wide 'lxc'
+ * group.
+ * @name: the name of the container
+ *
+ * The chosen cgpath is returned as a strdup'd string.  The caller will have to
+ * free that eventually, however the lxc monitor will keep that string so as to
+ * return it in response to a LXC_COMMAND_CGROUP query.
+ *
+ * Note the path is relative to cgroup mounts plus the caller's init task's
+ * cgroup.  I.e. if init is in cgroup /init and the freezer subsystem is at
+ * /sys/fs/cgroup/freezer, and this fn returns '/lxc/r1', then the freezer
+ * cgroup's full path will be /sys/fs/cgroup/freezer/init/lxc/r1/.  Note also
+ * that this should cleanly account for init being in different cgroups for
+ * different subsystems.
+ *
+ * XXX This should probably be locked globally
+ * 
+ * Races won't be determintal, you'll just end up with leftover unused cgroups
  */
-int try_to_move_cgname(char *cgparent, char *cgname)
+char *lxc_cgroup_path_create(const char *lxcgroup, const char *name)
 {
-       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);
+       int i = 0, ret;
+       char *retpath, path[MAXPATHLEN], initpath[MAXPATHLEN], *init;
+       char tail[12];
+       FILE *file = NULL;
+       struct mntent *mntent;
 
-       return 0;
-}
+       if (create_lxcgroups(lxcgroup) < 0)
+               return NULL;
 
-/*
- * create a cgroup for the container in a particular subsystem.
- */
-static int lxc_one_cgroup_create(const char *name,
-                                struct mntent *mntent, pid_t pid)
-{
-       char cginit[MAXPATHLEN], cgname[MAXPATHLEN], cgparent[MAXPATHLEN];
-       char clonechild[MAXPATHLEN];
-       char initcgroup[MAXPATHLEN];
-       int flags, ret;
-
-       /* cgparent is the parent dir, e.g., 
/sys/fs/cgroup/<cgroup>/<init-cgroup>/lxc */
-       /* (remember get_init_cgroup() returns a path starting with '/') */
-       /* cgname is the full name, e.g., 
/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;
+again:
+       file = setmntent(MTAB, "r");
+       if (!file) {
+               SYSERROR("failed to open %s", MTAB);
+               return NULL;
        }
 
-       flags = get_cgroup_flags(mntent);
+       if (i)
+               snprintf(tail, 12, "-%d", i);
+       else
+               *tail = '\0';
 
-       ret = snprintf(cgparent, MAXPATHLEN, "%s%s", cginit,
-                      (flags & CGROUP_NS_CGROUP) ? "" : "/lxc");
-       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;
-       }
-
-       /* Do we have the deprecated ns_cgroup subsystem? */
-       if (flags & CGROUP_NS_CGROUP) {
-               WARN("using deprecated ns_cgroup");
-               return cgroup_rename_nsgroup(cginit, name, pid);
-       }
+       while ((mntent = getmntent(file))) {
 
-       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;
-       }
+               if (strcmp(mntent->mnt_type, "cgroup"))
+                       continue;
+               if (!mount_has_subsystem(mntent))
+                       continue;
 
-       /* 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
-        * without the ns_cgroup and it has not the compatibility flag
-        */
-       if (access(clonechild, F_OK)) {
-               ERROR("no ns_cgroup option specified");
-               return -1;
-       }
+               init = get_init_cgroup(NULL, mntent, initpath, 1);
+               /* find unused mnt_dir + init_cgroup + lxcgroup + name + -$i */
+               ret = snprintf(path, MAXPATHLEN, "%s%s/%s/%s%s",
+                              mntent->mnt_dir, init,
+                              lxcgroup ? lxcgroup : "lxc", name, tail);
+               if (ret < 0 || ret >= MAXPATHLEN)
+                       goto fail;
 
-       /* enable the clone_children flag of the cgroup */
-       if (cgroup_enable_clone_children(clonechild)) {
-               SYSERROR("failed to enable 'clone_children flag");
-               return -1;
-       }
+               if (access(path, F_OK) == 0) goto next;
 
-       /* if cgparent does not exist, create it */
-       if (access(cgparent, F_OK)) {
-               ret = mkdir(cgparent, 0755);
-               if (ret == -1 && errno != EEXIST) {
-                       SYSERROR("failed to create '%s' directory", cgparent);
-                       return -1;
+               if (mkdir(path, 0755)) {
+                       ERROR("Error creating cgroups");
+                       goto fail;
                }
-       }
 
-       /*
-        * 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);
-                       ERROR("##");
-                       ERROR("# The container might be already running!");
-                       ERROR("##");
-                       return -1;
+               if (hasmntopt(mntent, "cpuset")) {
+                       setup_cpuset(path);
                }
        }
 
-       /* Let's create the cgroup */
-       if (mkdir(cgname, 0755)) {
-               SYSERROR("failed to create '%s' directory", cgname);
-               return -1;
-       }
+       endmntent(file);
 
-       INFO("created cgroup '%s'", cgname);
+       // print out the cgpath part
+       ret = snprintf(path, MAXPATHLEN, "%s/%s%s",
+                      lxcgroup ? lxcgroup : "lxc", name, tail);
+       if (ret < 0 || ret >= MAXPATHLEN) // can't happen
+               goto fail;
 
-       return 0;
+       retpath = strdup(path);
+
+       return retpath;
+
+next:
+       endmntent(file);
+       i++;
+       goto again;
+
+fail:
+       endmntent(file);
+       return NULL;
 }
 
-/*
- * for each mounted cgroup, create a cgroup for the container and attach a pid
- */
-int lxc_cgroup_create(const char *name, pid_t pid)
+int lxc_cgroup_enter(const char *cgpath, pid_t pid)
 {
+       char path[MAXPATHLEN], initpath[MAXPATHLEN], *init;
+       FILE *file = NULL, *fout;
        struct mntent *mntent;
-       FILE *file = NULL;
-       int err = -1;
-       int found = 0;
+       int ret, retv = -1;
 
        file = setmntent(MTAB, "r");
        if (!file) {
@@ -595,31 +727,30 @@ int lxc_cgroup_create(const char *name, pid_t pid)
        }
 
        while ((mntent = getmntent(file))) {
-               DEBUG("checking '%s' (%s)", mntent->mnt_dir, mntent->mnt_type);
-
                if (strcmp(mntent->mnt_type, "cgroup"))
                        continue;
                if (!mount_has_subsystem(mntent))
                        continue;
-
-               INFO("[%d] found cgroup mounted at '%s',opts='%s'",
-                    ++found, mntent->mnt_dir, mntent->mnt_opts);
-
-               err = lxc_one_cgroup_create(name, mntent, pid);
-               if (err)
+               init = get_init_cgroup(NULL, mntent, initpath, 1);
+               ret = snprintf(path, MAXPATHLEN, "%s%s/%s/tasks",
+                              mntent->mnt_dir, init, cgpath);
+               if (ret < 0 || ret >= MAXPATHLEN) {
+                       ERROR("entering cgroup");
                        goto out;
-
-               err = lxc_one_cgroup_attach(name, mntent, pid);
-               if (err)
+               }
+               fout = fopen(path, "w");
+               if (!fout) {
+                       ERROR("entering cgroup");
                        goto out;
-       };
-
-       if (!found)
-               ERROR("No cgroup mounted on the system");
+               }
+               fprintf(fout, "%d\n", (int)pid);
+               fclose(fout);
+       }
+       retv = 0;
 
 out:
        endmntent(file);
-       return err;
+       return retv;
 }
 
 int recursive_rmdir(char *dirname)
@@ -667,16 +798,14 @@ int recursive_rmdir(char *dirname)
 
 }
 
-int lxc_one_cgroup_destroy(struct mntent *mntent, const char *name)
+static int lxc_one_cgroup_destroy(struct mntent *mntent, const char *cgpath)
 {
        char cgname[MAXPATHLEN], initcgroup[MAXPATHLEN];
        char *cgmnt = mntent->mnt_dir;
-       int flags = get_cgroup_flags(mntent);
        int rc;
 
-       rc = snprintf(cgname, MAXPATHLEN, "%s%s%s/%s", cgmnt,
-               get_init_cgroup(NULL, mntent, initcgroup),
-               (flags & CGROUP_NS_CGROUP) ? "" : "/lxc", name);
+       rc = snprintf(cgname, MAXPATHLEN, "%s%s/%s", cgmnt,
+               get_init_cgroup(NULL, mntent, initcgroup, 1), cgpath);
        if (rc < 0 || rc >= MAXPATHLEN) {
                ERROR("name too long");
                return -1;
@@ -695,11 +824,11 @@ int lxc_one_cgroup_destroy(struct mntent *mntent, const 
char *name)
 /*
  * for each mounted cgroup, destroy the cgroup for the container
  */
-int lxc_cgroup_destroy(const char *name)
+int lxc_cgroup_destroy(const char *cgpath)
 {
        struct mntent *mntent;
        FILE *file = NULL;
-       int err = -1;
+       int err, retv  = 0;
 
        file = setmntent(MTAB, "r");
        if (!file) {
@@ -713,168 +842,24 @@ int lxc_cgroup_destroy(const char *name)
                if (!mount_has_subsystem(mntent))
                        continue;
 
-               err = lxc_one_cgroup_destroy(mntent, name);
-               if (err)
-                       break;
+               err = lxc_one_cgroup_destroy(mntent, cgpath);
+               if (err)  // keep trying to clean up the others
+                       retv = -1;
        }
 
        endmntent(file);
-       return err;
-}
-/*
- * lxc_cgroup_path_get: put into *path the pathname for
- * %subsystem and cgroup %name.  If %subsystem is NULL, then
- * the first mounted cgroup will be used (for nr_tasks)
- */
-int lxc_cgroup_path_get(char **path, const char *subsystem, const char *name)
-{
-       static char        buf[MAXPATHLEN];
-       static char        retbuf[MAXPATHLEN];
-       int rc;
-
-       /* lxc_cgroup_set passes a state object for the subsystem,
-        * so trim it to just the subsystem part */
-       if (subsystem) {
-               rc = snprintf(retbuf, MAXPATHLEN, "%s", subsystem);
-               if (rc < 0 || rc >= MAXPATHLEN) {
-                       ERROR("subsystem name too long");
-                       return -1;
-               }
-               char *s = index(retbuf, '.');
-               if (s)
-                       *s = '\0';
-               DEBUG("%s: called for subsys %s name %s\n", __func__, retbuf, 
name);
-       }
-       if (get_cgroup_mount(subsystem ? retbuf : NULL, buf)) {
-               ERROR("cgroup is not mounted");
-               return -1;
-       }
-
-       rc = snprintf(retbuf, MAXPATHLEN, "%s/%s", buf, name);
-       if (rc < 0 || rc >= MAXPATHLEN) {
-               ERROR("name too long");
-               return -1;
-       }
-
-       DEBUG("%s: returning %s for subsystem %s", __func__, retbuf, subsystem);
-
-       *path = retbuf;
-       return 0;
+       return retv;
 }
 
-int lxc_cgroup_set(const char *name, const char *filename, const char *value)
+int lxc_cgroup_attach(pid_t pid, const char *name, const char *lxcpath)
 {
-       int fd, ret;
-       char *dirpath;
-       char path[MAXPATHLEN];
-       int rc;
+       const char *dirpath;
 
-       ret = lxc_cgroup_path_get(&dirpath, filename, name);
-       if (ret)
-               return -1;
-
-       rc = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename);
-       if (rc < 0 || rc >= MAXPATHLEN) {
-               ERROR("pathname too long");
-               return -1;
-       }
-
-       fd = open(path, O_WRONLY);
-       if (fd < 0) {
-               ERROR("open %s : %s", path, strerror(errno));
+       if (lxc_get_cgpath(&dirpath, NULL, name, lxcpath) < 0) {
+               ERROR("Error getting cgroup for container %s: %s", lxcpath, 
name);
                return -1;
        }
+       INFO("joining pid %d to cgroup %s", pid, dirpath);
 
-       ret = write(fd, value, strlen(value));
-       if (ret < 0) {
-               ERROR("write %s : %s", path, strerror(errno));
-               goto out;
-       }
-
-       ret = 0;
-out:
-       close(fd);
-       return ret;
-}
-
-/*
- * If you pass in NULL value or 0 len, then you are asking for the size
- * of the file.  Note that we can't get the file size quickly through stat
- * or lseek.  Therefore if you pass in len > 0 but less than the file size,
- * your only indication will be that the return value will be equal to the
- * passed-in ret.  We will not return the actual full file size.
- */
-int lxc_cgroup_get(const char *name, const char *filename,
-                  char *value, size_t len)
-{
-       int fd, ret = -1;
-       char *dirpath;
-       char path[MAXPATHLEN];
-       int rc;
-
-       ret = lxc_cgroup_path_get(&dirpath, filename, name);
-       if (ret)
-               return -1;
-
-       rc = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename);
-       if (rc < 0 || rc >= MAXPATHLEN) {
-               ERROR("pathname too long");
-               return -1;
-       }
-
-       fd = open(path, O_RDONLY);
-       if (fd < 0) {
-               ERROR("open %s : %s", path, strerror(errno));
-               return -1;
-       }
-
-    if (!len || !value) {
-        char buf[100];
-        int count = 0;
-        while ((ret = read(fd, buf, 100)) > 0)
-            count += ret;
-        if (ret >= 0)
-            ret = count;
-    } else {
-        memset(value, 0, len);
-        ret = read(fd, value, len);
-    }
-
-       if (ret < 0)
-               ERROR("read %s : %s", path, strerror(errno));
-
-       close(fd);
-       return ret;
-}
-
-int lxc_cgroup_nrtasks(const char *name)
-{
-       char *dpath;
-       char path[MAXPATHLEN];
-       int pid, ret, count = 0;
-       FILE *file;
-       int rc;
-
-       ret = lxc_cgroup_path_get(&dpath, NULL, name);
-       if (ret)
-               return -1;
-
-       rc = snprintf(path, MAXPATHLEN, "%s/tasks", dpath);
-       if (rc < 0 || rc >= MAXPATHLEN) {
-               ERROR("pathname too long");
-               return -1;
-       }
-
-       file = fopen(path, "r");
-       if (!file) {
-               SYSERROR("fopen '%s' failed", path);
-               return -1;
-       }
-
-       while (fscanf(file, "%d", &pid) != EOF)
-               count++;
-
-       fclose(file);
-
-       return count;
+       return lxc_cgroup_enter(dirpath, pid);
 }
diff --git a/src/lxc/cgroup.h b/src/lxc/cgroup.h
index 8167f39..709615a 100644
--- a/src/lxc/cgroup.h
+++ b/src/lxc/cgroup.h
@@ -26,13 +26,13 @@
 #define MAXPRIOLEN 24
 
 struct lxc_handler;
-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 *name, pid_t pid);
-extern int lxc_cgroup_prepare_attach(const char *name, void **data);
-extern int lxc_cgroup_finish_attach(void *data, pid_t pid);
-extern int lxc_cgroup_dispose_attach(void *data);
-extern int lxc_ns_is_mounted(void);
+extern int lxc_cgroup_destroy(const char *cgpath);
+extern int lxc_cgroup_path_get(char **path, const char *subsystem, const char 
*name,
+                             const char *lxcpath);
+extern int lxc_cgroup_nrtasks(const char *cgpath);
+extern char *lxc_cgroup_path_create(const char *lxcgroup, const char *name);
+extern int lxc_cgroup_enter(const char *cgpath, pid_t pid);
+extern int lxc_cgroup_attach(pid_t pid, const char *name, const char *lxcpath);
+extern int cgroup_path_get(char **path, const char *subsystem, const char 
*cgpath);
+extern int lxc_get_cgpath(const char **path, const char *subsystem, const char 
*name, const char *lxcpath);
 #endif
diff --git a/src/lxc/commands.c b/src/lxc/commands.c
index 83e7df1..de9a7c4 100644
--- a/src/lxc/commands.c
+++ b/src/lxc/commands.c
@@ -84,10 +84,19 @@ static int fill_sock_name(char *path, int len, const char 
*name,
 static int receive_answer(int sock, struct lxc_answer *answer)
 {
        int ret;
+       static char answerpath[MAXPATHLEN];
 
        ret = lxc_af_unix_recv_fd(sock, &answer->fd, answer, sizeof(*answer));
        if (ret < 0)
                ERROR("failed to receive answer for the command");
+       if (answer->pathlen == 0)
+               return ret;
+       ret = recv(sock, answerpath, answer->pathlen, 0);
+       if (ret != answer->pathlen) {
+               ERROR("failed to receive answer for the command");
+               ret = 0;
+       } else
+               answer->path = answerpath;
 
        return ret;
 }
@@ -202,6 +211,7 @@ extern int  lxc_stop_callback(int, struct lxc_request *, 
struct lxc_handler *);
 extern int  lxc_state_callback(int, struct lxc_request *, struct lxc_handler 
*);
 extern int  lxc_pid_callback(int, struct lxc_request *, struct lxc_handler *);
 extern int  lxc_clone_flags_callback(int, struct lxc_request *, struct 
lxc_handler *);
+extern int lxc_cgroup_callback(int, struct lxc_request *, struct lxc_handler 
*);
 
 static int trigger_command(int fd, struct lxc_request *request,
                           struct lxc_handler *handler)
@@ -214,6 +224,7 @@ static int trigger_command(int fd, struct lxc_request 
*request,
                [LXC_COMMAND_STATE]       = lxc_state_callback,
                [LXC_COMMAND_PID]         = lxc_pid_callback,
                [LXC_COMMAND_CLONE_FLAGS] = lxc_clone_flags_callback,
+               [LXC_COMMAND_CGROUP]      = lxc_cgroup_callback,
        };
 
        if (request->type < 0 || request->type >= LXC_COMMAND_MAX)
diff --git a/src/lxc/commands.h b/src/lxc/commands.h
index 0b72cf1..8d77ce8 100644
--- a/src/lxc/commands.h
+++ b/src/lxc/commands.h
@@ -29,6 +29,7 @@ enum {
        LXC_COMMAND_STATE,
        LXC_COMMAND_PID,
        LXC_COMMAND_CLONE_FLAGS,
+       LXC_COMMAND_CGROUP,
        LXC_COMMAND_MAX,
 };
 
@@ -41,6 +42,8 @@ struct lxc_answer {
        int fd;
        int ret; /* 0 on success, -errno on failure */
        pid_t pid;
+       int pathlen;
+       const char *path;
 };
 
 struct lxc_command {
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 7d70c97..add3c74 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -1375,7 +1375,7 @@ static int setup_kmsg(const struct lxc_rootfs *rootfs,
        return 0;
 }
 
-int setup_cgroup(const char *name, struct lxc_list *cgroups)
+int setup_cgroup(const char *cgpath, struct lxc_list *cgroups)
 {
        struct lxc_list *iterator;
        struct lxc_cgroup *cg;
@@ -1388,8 +1388,11 @@ int setup_cgroup(const char *name, struct lxc_list 
*cgroups)
 
                cg = iterator->elem;
 
-               if (lxc_cgroup_set(name, cg->subsystem, cg->value))
+               if (lxc_cgroup_set_bypath(cgpath, cg->subsystem, cg->value)) {
+                       ERROR("Error setting %s to %s for %s\n", cg->subsystem,
+                               cg->value, cgpath);
                        goto out;
+               }
 
                DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
        }
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 4c48b46..f20fb2f 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -282,7 +282,7 @@ struct lxc_conf {
 
 int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf);
 
-extern int setup_cgroup(const char *name, struct lxc_list *cgroups);
+extern int setup_cgroup(const char *cgpath, struct lxc_list *cgroups);
 extern int detect_shared_rootfs(void);
 
 /*
diff --git a/src/lxc/freezer.c b/src/lxc/freezer.c
index e8b1311..261c2c4 100644
--- a/src/lxc/freezer.c
+++ b/src/lxc/freezer.c
@@ -40,16 +40,11 @@
 
 lxc_log_define(lxc_freezer, lxc);
 
-static int freeze_unfreeze(const char *name, int freeze, const char *lxcpath)
+static int do_unfreeze(const char *nsgroup, int freeze, const char *name, 
const char *lxcpath)
 {
-       char *nsgroup;
        char freezer[MAXPATHLEN], *f;
        char tmpf[32];
        int fd, ret;
-       
-       ret = lxc_cgroup_path_get(&nsgroup, "freezer", name);
-       if (ret)
-               return -1;
 
        ret = snprintf(freezer, MAXPATHLEN, "%s/freezer.state", nsgroup);
        if (ret >= MAXPATHLEN) {
@@ -59,7 +54,7 @@ static int freeze_unfreeze(const char *name, int freeze, 
const char *lxcpath)
 
        fd = open(freezer, O_RDWR);
        if (fd < 0) {
-               SYSERROR("failed to open freezer for '%s'", name);
+               SYSERROR("failed to open freezer at '%s'", nsgroup);
                return -1;
        }
 
@@ -98,7 +93,8 @@ static int freeze_unfreeze(const char *name, int freeze, 
const char *lxcpath)
                ret = strncmp(f, tmpf, strlen(f));
                if (!ret)
                {
-                       lxc_monitor_send_state(name, freeze ? FROZEN : THAWED, 
lxcpath);
+                       if (name)
+                               lxc_monitor_send_state(name, freeze ? FROZEN : 
THAWED, lxcpath);
                        break;          /* Success */
                }
 
@@ -122,6 +118,18 @@ out:
        return ret;
 }
 
+static int freeze_unfreeze(const char *name, int freeze, const char *lxcpath)
+{
+       char *nsgroup;
+       int ret;
+       
+       ret = lxc_cgroup_path_get(&nsgroup, "freezer", name, lxcpath);
+       if (ret)
+               return -1;
+
+       return do_unfreeze(nsgroup, freeze, name, lxcpath);
+}
+
 int lxc_freeze(const char *name, const char *lxcpath)
 {
        lxc_monitor_send_state(name, FREEZING, lxcpath);
@@ -133,3 +141,14 @@ int lxc_unfreeze(const char *name, const char *lxcpath)
        return freeze_unfreeze(name, 0, lxcpath);
 }
 
+int lxc_unfreeze_bypath(const char *cgpath)
+{
+       char *nsgroup;
+       int ret;
+       
+       ret = cgroup_path_get(&nsgroup, "freezer", cgpath);
+       if (ret)
+               return -1;
+
+       return do_unfreeze(nsgroup, 0, NULL, NULL);
+}
diff --git a/src/lxc/lxc.h b/src/lxc/lxc.h
index 0fa9480..784b3ca 100644
--- a/src/lxc/lxc.h
+++ b/src/lxc/lxc.h
@@ -118,6 +118,14 @@ extern int lxc_freeze(const char *name, const char 
*lxcpath);
 extern int lxc_unfreeze(const char *name, const char *lxcpath);
 
 /*
+ * Unfreeze all previously frozen tasks.
+ * This is the function to use from inside the monitor
+ * @name : the name of the container
+ * Return 0 on sucess, < 0 otherwise
+ */
+extern int lxc_unfreeze_bypath(const char *cgpath);
+
+/*
  * Retrieve the container state
  * @name : the name of the container
  * Returns the state of the container on success, < 0 otherwise
@@ -127,24 +135,36 @@ extern lxc_state_t lxc_state(const char *name, const char 
*lxcpath);
 /*
  * Set a specified value for a specified subsystem. The specified
  * subsystem must be fully specified, eg. "cpu.shares"
+ * @cgpath    : the cgroup path of the container
+ * @filename  : the cgroup attribute filename
+ * @value     : the value to be set
+ * Returns 0 on success, < 0 otherwise
+ */
+extern int lxc_cgroup_set_bypath(const char *cgpath, const char *filename, 
const char *value);
+
+/*
+ * Set a specified value for a specified subsystem. The specified
+ * subsystem must be fully specified, eg. "cpu.shares"
  * @name      : the name of the container
- * @filename : the cgroup attribute filename
+ * @filename  : the cgroup attribute filename
  * @value     : the value to be set
+ * @lxcpath   : lxc config path for container
  * Returns 0 on success, < 0 otherwise
  */
-extern int lxc_cgroup_set(const char *name, const char *filename, const char 
*value);
+extern int lxc_cgroup_set(const char *name, const char *filename, const char 
*value, const char *lxcpath);
 
 /*
  * Get a specified value for a specified subsystem. The specified
  * subsystem must be fully specified, eg. "cpu.shares"
  * @name      : the name of the container
- * @filename : the cgroup attribute filename
+ * @filename  : the cgroup attribute filename
  * @value     : the value to be set
  * @len       : the len of the value variable
+ * @lxcpath   : lxc config path for container
  * Returns the number of bytes read, < 0 on error
  */
 extern int lxc_cgroup_get(const char *name, const char *filename,
-                         char *value, size_t len);
+                         char *value, size_t len, const char *lxcpath);
 
 /*
  * Retrieve the error string associated with the error returned by
diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c
index 1f60266..af920be 100644
--- a/src/lxc/lxc_attach.c
+++ b/src/lxc/lxc_attach.c
@@ -237,7 +237,7 @@ int main(int argc, char *argv[])
                }
 
                if (!elevated_privileges) {
-                       ret = lxc_cgroup_attach(my_args.name, grandchild);
+                       ret = lxc_cgroup_attach(grandchild, my_args.name, 
my_args.lxcpath);
                        if (ret < 0) {
                                ERROR("failed to attach process to cgroup");
                                return -1;
diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c
index 970391b..4a55633 100644
--- a/src/lxc/lxc_cgroup.c
+++ b/src/lxc/lxc_cgroup.c
@@ -78,7 +78,7 @@ int main(int argc, char *argv[])
                value = my_args.argv[1];
 
        if (value) {
-               if (lxc_cgroup_set(my_args.name, state_object, value)) {
+               if (lxc_cgroup_set(my_args.name, state_object, value, 
my_args.lxcpath)) {
                        ERROR("failed to assign '%s' value to '%s' for '%s'",
                                value, state_object, my_args.name);
                        return -1;
@@ -88,7 +88,7 @@ int main(int argc, char *argv[])
                int ret;
                char buffer[len];
 
-               ret = lxc_cgroup_get(my_args.name, state_object, buffer, len);
+               ret = lxc_cgroup_get(my_args.name, state_object, buffer, len, 
my_args.lxcpath);
                if (ret < 0) {
                        ERROR("failed to retrieve value of '%s' for '%s'",
                              state_object, my_args.name);
diff --git a/src/lxc/lxc_unshare.c b/src/lxc/lxc_unshare.c
index d92a96a..c5d3332 100644
--- a/src/lxc/lxc_unshare.c
+++ b/src/lxc/lxc_unshare.c
@@ -112,7 +112,6 @@ int main(int argc, char *argv[])
 {
        int opt, status;
        int ret;
-       char *pid_name;
        char *namespaces = NULL;
        char **args;
        int flags = 0;
@@ -170,14 +169,5 @@ int main(int argc, char *argv[])
                return -1;
        }
 
-       if (lxc_ns_is_mounted()) {
-               if (asprintf(&pid_name, "%d", pid) == -1) {
-                       ERROR("pid_name: failed to allocate memory");
-                       return -1;
-               }
-               lxc_cgroup_destroy(pid_name);
-               free(pid_name);
-       }
-
        return  lxc_error_set_and_log(pid, status);
 }
diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index 404f60a..6569071 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -945,7 +945,7 @@ static bool lxcapi_set_cgroup_item(struct lxc_container *c, 
const char *subsys,
        if (is_stopped_nolock(c))
                goto err;
 
-       ret = lxc_cgroup_set(c->name, subsys, value);
+       ret = lxc_cgroup_set(c->name, subsys, value, c->config_path);
        if (!ret)
                b = true;
 err:
@@ -966,7 +966,7 @@ static int lxcapi_get_cgroup_item(struct lxc_container *c, 
const char *subsys, c
        if (is_stopped_nolock(c))
                goto out;
 
-       ret = lxc_cgroup_get(c->name, subsys, retv, inlen);
+       ret = lxc_cgroup_get(c->name, subsys, retv, inlen, c->config_path);
 
 out:
        lxcunlock(c->privlock);
diff --git a/src/lxc/lxcutmp.c b/src/lxc/lxcutmp.c
index 4e74904..54db903 100644
--- a/src/lxc/lxcutmp.c
+++ b/src/lxc/lxcutmp.c
@@ -283,7 +283,7 @@ static int utmp_get_ntasks(struct lxc_handler *handler)
 {
        int ntasks;
 
-       ntasks = lxc_cgroup_nrtasks(handler->name);
+       ntasks = lxc_cgroup_nrtasks(handler->cgroup);
 
        if (ntasks < 0) {
                ERROR("failed to get the number of tasks");
diff --git a/src/lxc/start.c b/src/lxc/start.c
index de431be..f0e82a3 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -268,6 +268,7 @@ int lxc_pid_callback(int fd, struct lxc_request *request,
        struct lxc_answer answer;
        int ret;
 
+       memset(&answer, 0, sizeof(answer));
        answer.pid = handler->pid;
        answer.ret = 0;
 
@@ -285,12 +286,49 @@ int lxc_pid_callback(int fd, struct lxc_request *request,
        return 0;
 }
 
+int lxc_cgroup_callback(int fd, struct lxc_request *request,
+                    struct lxc_handler *handler)
+{
+       struct lxc_answer answer;
+       int ret;
+
+       memset(&answer, 0, sizeof(answer));
+       answer.pathlen = strlen(handler->cgroup) + 1;
+       answer.path = handler->cgroup;
+       answer.ret = 0;
+
+       ret = send(fd, &answer, sizeof(answer), 0);
+       if (ret < 0) {
+               WARN("failed to send answer to the peer");
+               return -1;
+       }
+
+       if (ret != sizeof(answer)) {
+               ERROR("partial answer sent");
+               return -1;
+       }
+
+       ret = send(fd, answer.path, answer.pathlen, 0);
+       if (ret < 0) {
+               WARN("failed to send answer to the peer");
+               return -1;
+       }
+
+       if (ret != answer.pathlen) {
+               ERROR("partial answer sent");
+               return -1;
+       }
+
+       return 0;
+}
+
 int lxc_clone_flags_callback(int fd, struct lxc_request *request,
                             struct lxc_handler *handler)
 {
        struct lxc_answer answer;
        int ret;
 
+       memset(&answer, 0, sizeof(answer));
        answer.pid = 0;
        answer.ret = handler->clone_flags;
 
@@ -467,7 +505,7 @@ out_free:
        return NULL;
 }
 
-void lxc_fini(const char *name, struct lxc_handler *handler)
+static void lxc_fini(const char *name, struct lxc_handler *handler)
 {
        /* The STOPPING state is there for future cleanup code
         * which can take awhile
@@ -487,6 +525,11 @@ void lxc_fini(const char *name, struct lxc_handler 
*handler)
        close(handler->conf->maincmd_fd);
        handler->conf->maincmd_fd = -1;
        free(handler->name);
+       if (handler->cgroup) {
+               lxc_cgroup_destroy(handler->cgroup);
+               free(handler->cgroup);
+               handler->cgroup = NULL;
+       }
        free(handler);
 }
 
@@ -756,7 +799,11 @@ int lxc_spawn(struct lxc_handler *handler)
        if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE))
                failed_before_rename = 1;
 
-       if (lxc_cgroup_create(name, handler->pid))
+       /* TODO - pass lxc.cgroup.dir (or user's pam cgroup) in for first 
argument */
+       if ((handler->cgroup = lxc_cgroup_path_create(NULL, name)) == NULL)
+               goto out_delete_net;
+       
+       if (lxc_cgroup_enter(handler->cgroup, handler->pid) < 0)
                goto out_delete_net;
 
        if (failed_before_rename)
@@ -786,7 +833,7 @@ int lxc_spawn(struct lxc_handler *handler)
        if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CONFIGURE))
                goto out_delete_net;
 
-       if (setup_cgroup(name, &handler->conf->cgroup)) {
+       if (setup_cgroup(handler->cgroup, &handler->conf->cgroup)) {
                ERROR("failed to setup the cgroups for '%s'", name);
                goto out_delete_net;
        }
@@ -904,7 +951,6 @@ out_fini:
        lxc_delete_network(handler);
 
 out_fini_nonet:
-       lxc_cgroup_destroy(name);
        lxc_fini(name, handler);
        return err;
 
diff --git a/src/lxc/start.h b/src/lxc/start.h
index a0afa87..5b3488f 100644
--- a/src/lxc/start.h
+++ b/src/lxc/start.h
@@ -51,6 +51,7 @@ struct lxc_handler {
 #endif
        int pinfd;
        const char *lxcpath;
+       char *cgroup;
 };
 
 extern struct lxc_handler *lxc_init(const char *name, struct lxc_conf *, const 
char *);
@@ -58,7 +59,6 @@ extern int lxc_spawn(struct lxc_handler *);
 
 extern int lxc_poll(const char *name, struct lxc_handler *handler);
 extern void lxc_abort(const char *name, struct lxc_handler *handler);
-extern void lxc_fini(const char *name, struct lxc_handler *handler);
 extern int lxc_set_state(const char *, struct lxc_handler *, lxc_state_t);
 extern int lxc_check_inherited(struct lxc_conf *conf, int fd_to_ignore);
 int __lxc_start(const char *, struct lxc_conf *, struct lxc_operations *,
diff --git a/src/lxc/state.c b/src/lxc/state.c
index 9fcb06d..169aaf5 100644
--- a/src/lxc/state.c
+++ b/src/lxc/state.c
@@ -66,7 +66,7 @@ lxc_state_t lxc_str2state(const char *state)
        return -1;
 }
 
-static int freezer_state(const char *name)
+static int freezer_state(const char *name, const char *lxcpath)
 {
        char *nsgroup;
        char freezer[MAXPATHLEN];
@@ -74,7 +74,7 @@ static int freezer_state(const char *name)
        FILE *file;
        int err;
 
-       err = lxc_cgroup_path_get(&nsgroup, "freezer", name);
+       err = lxc_cgroup_path_get(&nsgroup, "freezer", name, lxcpath);
        if (err)
                return -1;
 
@@ -132,7 +132,7 @@ static lxc_state_t __lxc_getstate(const char *name, const 
char *lxcpath)
 
 lxc_state_t lxc_getstate(const char *name, const char *lxcpath)
 {
-       int state = freezer_state(name);
+       int state = freezer_state(name, lxcpath);
        if (state != FROZEN && state != FREEZING)
                state = __lxc_getstate(name, lxcpath);
        return state;
@@ -148,6 +148,7 @@ extern int lxc_state_callback(int fd, struct lxc_request 
*request,
        struct lxc_answer answer;
        int ret;
 
+       memset(&answer, 0, sizeof(answer));
        answer.ret = handler->state;
 
        ret = send(fd, &answer, sizeof(answer), 0);
diff --git a/src/lxc/stop.c b/src/lxc/stop.c
index c796283..851a4bf 100644
--- a/src/lxc/stop.c
+++ b/src/lxc/stop.c
@@ -83,9 +83,10 @@ extern int lxc_stop_callback(int fd, struct lxc_request 
*request,
        struct lxc_answer answer;
        int ret;
 
+       memset(&answer, 0, sizeof(answer));
        answer.ret = kill(handler->pid, SIGKILL);
        if (!answer.ret) {
-               ret = lxc_unfreeze(handler->name, handler->lxcpath);
+               ret = lxc_unfreeze_bypath(handler->cgroup);
                if (!ret)
                        return 0;
 
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index 0f4ee1d..b9f74f0 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -12,6 +12,7 @@ lxc_test_shutdowntest_SOURCES = shutdowntest.c
 lxc_test_get_item_SOURCES = get_item.c
 lxc_test_getkeys_SOURCES = getkeys.c
 lxc_test_lxcpath_SOURCES = lxcpath.c
+lxc_test_cgpath_SOURCES = cgpath.c
 
 AM_CFLAGS=-I$(top_srcdir)/src \
        -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
@@ -21,6 +22,7 @@ AM_CFLAGS=-I$(top_srcdir)/src \
 
 bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \
        lxc-test-destroytest lxc-test-saveconfig lxc-test-createtest \
-       lxc-test-shutdowntest lxc-test-get_item lxc-test-getkeys 
lxc-test-lxcpath
+       lxc-test-shutdowntest lxc-test-get_item lxc-test-getkeys 
lxc-test-lxcpath \
+       lxc-test-cgpath
 
 endif
diff --git a/src/tests/cgpath.c b/src/tests/cgpath.c
new file mode 100644
index 0000000..f05c435
--- /dev/null
+++ b/src/tests/cgpath.c
@@ -0,0 +1,164 @@
+/* liblxcapi
+ *
+ * Copyright � 2012 Serge Hallyn <serge.hal...@ubuntu.com>.
+ * Copyright � 2012 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "../lxc/lxccontainer.h"
+
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "../lxc/cgroup.h"
+#include "../lxc/lxc.h"
+
+#define MYNAME "lxctest1"
+#define MYNAME2 "lxctest2"
+
+#define TSTERR(x) do { \
+       fprintf(stderr, "%d: %s\n", __LINE__, x); \
+} while (0)
+
+int main()
+{
+       struct lxc_container *c = NULL, *c2 = NULL;
+       char *path;
+       int len;
+       int ret, retv = -1;
+
+       /* won't require privilege necessarily once users are classified by
+        * pam_cgroup */
+       if (geteuid() != 0) {
+               TSTERR("requires privilege");
+               exit(0);
+       }
+
+       printf("Basic cgroup path tests...\n");
+       path = lxc_cgroup_path_create(NULL, MYNAME);
+       len = strlen(path);
+       if (!path || !len) {
+               TSTERR("zero result from lxc_cgroup_path_create");
+               exit(1);
+       }
+       if (!strstr(path, "lxc/" MYNAME)) {
+               TSTERR("lxc_cgroup_path_create NULL lxctest1");
+               exit(1);
+       }
+       free(path);
+
+       path = lxc_cgroup_path_create("ab", MYNAME);
+       len = strlen(path);
+       if (!path || !len) {
+               TSTERR("zero result from lxc_cgroup_path_create");
+               exit(1);
+       }
+       if (!strstr(path, "ab/" MYNAME)) {
+               TSTERR("lxc_cgroup_path_create ab lxctest1");
+               exit(1);
+       }
+       free(path);
+       printf("... passed\n");
+
+       printf("Container creation tests...\n");
+
+       if ((c = lxc_container_new(MYNAME, NULL)) == NULL) {
+               TSTERR("instantiating first container");
+               exit(1);
+       }
+       if (c->is_defined(c)) {
+               c->stop(c);
+               c->destroy(c);
+               c = lxc_container_new(MYNAME, NULL);
+       }
+       c->set_config_item(c, "lxc.network.type", "empty");
+       if (!c->createl(c, "ubuntu", NULL)) {
+               TSTERR("creating first container");
+               exit(1);
+       }
+       c->load_config(c, NULL);
+       c->want_daemonize(c);
+       if (!c->startl(c, 0, NULL)) {
+               TSTERR("starting first container");
+               goto out;
+       }
+       printf("first container passed.  Now two containers...\n");
+
+       char *nsgroup;
+#define ALTBASE "/var/lib/lxctest2"
+       ret = mkdir(ALTBASE, 0755);
+
+       ret = lxc_cgroup_path_get(&nsgroup, "freezer", MYNAME, 
c->get_config_path(c));
+       if (ret < 0 || !strstr(nsgroup, "lxc/" MYNAME)) {
+               TSTERR("getting first cgroup path from lxc_command");
+               goto out;
+       }
+
+       /* start second container */
+       if ((c2 = lxc_container_new(MYNAME2, ALTBASE)) == NULL) {
+               TSTERR("instantiating first container");
+               goto out;
+       }
+       if (c2->is_defined(c2)) {
+               c2->stop(c2);
+               c2->destroy(c2);
+               c2 = lxc_container_new(MYNAME2, ALTBASE);
+       }
+       c2->set_config_item(c2, "lxc.network.type", "empty");
+       if (!c2->createl(c2, "ubuntu", NULL)) {
+               TSTERR("creating first container");
+               goto out;
+       }
+
+       c2->load_config(c2, NULL);
+       c2->want_daemonize(c2);
+       if (!c2->startl(c2, 0, NULL)) {
+               TSTERR("starting first container");
+               goto out;
+       }
+
+       ret = lxc_cgroup_path_get(&nsgroup, "freezer", MYNAME2, 
c2->get_config_path(c2));
+       if (ret < 0 || !strstr(nsgroup, "lxc/" MYNAME2)) {
+               TSTERR("getting second cgroup path from lxc_command");
+               goto out;
+       }
+
+       const char *dirpath;
+       if (lxc_get_cgpath(&dirpath, NULL, c2->name, c2->config_path) < 0) {
+               TSTERR("getting second container's cgpath");
+               return -1;
+       }
+
+       if (lxc_cgroup_nrtasks(dirpath) < 1) {
+               TSTERR("getting nrtasks");
+               goto out;
+       }
+       printf("...passed\n");
+
+       retv = 0;
+out:
+       if (c2) {
+               c2->stop(c2);
+               c2->destroy(c2);
+       }
+       if (c) {
+               c->stop(c);
+               c->destroy(c);
+       }
+       return retv;
+}
diff --git a/src/tests/lxcpath.c b/src/tests/lxcpath.c
index 92525ed..a849a38 100644
--- a/src/tests/lxcpath.c
+++ b/src/tests/lxcpath.c
@@ -29,7 +29,7 @@
 #define MYNAME "lxctest1"
 
 #define TSTERR(x) do { \
-       fprintf(stderr, "%d: %s", __LINE__, x); \
+       fprintf(stderr, "%d: %s\n", __LINE__, x); \
 } while (0)
 
 int main()
-- 
1.8.1.2


------------------------------------------------------------------------------
Everyone hates slow websites. So do we.
Make your web apps faster with AppDynamics
Download AppDynamics Lite for free today:
http://p.sf.net/sfu/appdyn_d2d_feb
_______________________________________________
Lxc-devel mailing list
Lxc-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/lxc-devel

Reply via email to