Commit ae5c8b8e by Serge Hallyn

cgroup: improve support for multiple lxcpaths (v3)

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: 's avatarSerge Hallyn <serge.hallyn@ubuntu.com>
parent 7f597314
...@@ -42,7 +42,6 @@ ...@@ -42,7 +42,6 @@
#include "log.h" #include "log.h"
#include "attach.h" #include "attach.h"
#include "caps.h" #include "caps.h"
#include "cgroup.h"
#include "config.h" #include "config.h"
#include "apparmor.h" #include "apparmor.h"
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include "error.h" #include "error.h"
#include "config.h" #include "config.h"
#include "commands.h"
#include <lxc/log.h> #include <lxc/log.h>
#include <lxc/cgroup.h> #include <lxc/cgroup.h>
...@@ -53,11 +54,6 @@ lxc_log_define(lxc_cgroup, lxc); ...@@ -53,11 +54,6 @@ lxc_log_define(lxc_cgroup, lxc);
#define MTAB "/proc/mounts" #define MTAB "/proc/mounts"
enum {
CGROUP_NS_CGROUP = 1,
CGROUP_CLONE_CHILDREN,
};
/* Check if a mount is a cgroup hierarchy for any subsystem. /* Check if a mount is a cgroup hierarchy for any subsystem.
* Return the first subsystem found (or NULL if none). * Return the first subsystem found (or NULL if none).
*/ */
...@@ -93,22 +89,27 @@ static char *mount_has_subsystem(const struct mntent *mntent) ...@@ -93,22 +89,27 @@ static char *mount_has_subsystem(const struct mntent *mntent)
/* /*
* get_init_cgroup: get the cgroup init is in. * 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 (I.e. "freezer")
* subsystem: the exact cgroup subsystem to look up * @mntent: a mntent (from getmntent) whose mntopts contains the subsystem to
* mntent: a mntent (from getmntent) whose mntopts contains the * look up.
* 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 * subsystem and mntent can both be NULL, in which case we return
* the first entry in /proc/1/cgroup. * 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, static char *get_init_cgroup(const char *subsystem, struct mntent *mntent,
char *dsg) char *dsg, int prependslash)
{ {
FILE *f; FILE *f;
char *c, *c2; char *c, *c2;
char line[MAXPATHLEN]; char line[MAXPATHLEN];
int ret;
*dsg = '\0'; *dsg = '\0';
f = fopen("/proc/1/cgroup", "r"); f = fopen("/proc/1/cgroup", "r");
...@@ -134,10 +135,18 @@ static char *get_init_cgroup(const char *subsystem, struct mntent *mntent, ...@@ -134,10 +135,18 @@ static char *get_init_cgroup(const char *subsystem, struct mntent *mntent,
good: good:
DEBUG("get_init_cgroup: found init cgroup for subsys %s at %s\n", DEBUG("get_init_cgroup: found init cgroup for subsys %s at %s\n",
subsystem, c2); subsystem, c2);
strncpy(dsg, c2, MAXPATHLEN); ret = snprintf(dsg, MAXPATHLEN, "%s%s", prependslash ? "/" : "", c2);
c = &dsg[strlen(dsg)-1]; 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') if (*c == '\n')
*c = '\0'; *c = '\0';
goto found; goto found;
} }
...@@ -146,27 +155,22 @@ found: ...@@ -146,27 +155,22 @@ found:
return dsg; return dsg;
} }
static int get_cgroup_flags(struct mntent *mntent) /*
{ * Determine mountpoint for a cgroup subsystem, plus the cgroup path in that
int flags = 0; * subsytem of the container init.
* @subsystem: cgroup subsystem (i.e. freezer). If this is NULL, the first
* cgroup mountpoint with any subsystems is used.
if (hasmntopt(mntent, "ns")) * @mnt: a passed-in buffer of at least size MAXPATHLEN into which the path
flags |= CGROUP_NS_CGROUP; * is copied.
*
if (hasmntopt(mntent, "clone_children")) * Returns 0 on success, -1 on error.
flags |= CGROUP_CLONE_CHILDREN; */
DEBUG("cgroup %s has flags 0x%x", mntent->mnt_dir, flags);
return flags;
}
static int get_cgroup_mount(const char *subsystem, char *mnt) static int get_cgroup_mount(const char *subsystem, char *mnt)
{ {
struct mntent *mntent; struct mntent *mntent;
char initcgroup[MAXPATHLEN]; char initcgroup[MAXPATHLEN], *init;
FILE *file = NULL; FILE *file = NULL;
int ret, flags, err = -1; int ret, err = -1;
file = setmntent(MTAB, "r"); file = setmntent(MTAB, "r");
if (!file) { if (!file) {
...@@ -181,16 +185,14 @@ static int get_cgroup_mount(const char *subsystem, char *mnt) ...@@ -181,16 +185,14 @@ static int get_cgroup_mount(const char *subsystem, char *mnt)
if (subsystem) { if (subsystem) {
if (!hasmntopt(mntent, subsystem)) if (!hasmntopt(mntent, subsystem))
continue; continue;
} } else {
else {
if (!mount_has_subsystem(mntent)) if (!mount_has_subsystem(mntent))
continue; continue;
} }
flags = get_cgroup_flags(mntent); init = get_init_cgroup(NULL, mntent, initcgroup, 1);
ret = snprintf(mnt, MAXPATHLEN, "%s%s%s", mntent->mnt_dir, ret = snprintf(mnt, MAXPATHLEN, "%s%s", mntent->mnt_dir,
get_init_cgroup(subsystem, NULL, initcgroup), init);
(flags & CGROUP_NS_CGROUP) ? "" : "/lxc");
if (ret < 0 || ret >= MAXPATHLEN) if (ret < 0 || ret >= MAXPATHLEN)
goto fail; goto fail;
...@@ -207,180 +209,370 @@ out: ...@@ -207,180 +209,370 @@ out:
return err; 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 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]; struct lxc_command command = {
char newname[MAXPATHLEN]; .request = { .type = LXC_COMMAND_CGROUP },
int ret; };
int ret, stopped = 0;
ret = snprintf(oldname, MAXPATHLEN, "%s/%d", mnt, pid); ret = lxc_command(name, &command, &stopped, lxcpath);
if (ret >= MAXPATHLEN) if (ret < 0) {
if (!stopped)
ERROR("failed to send command");
return -1; return -1;
}
ret = snprintf(newname, MAXPATHLEN, "%s/%s", mnt, name); if (!ret) {
if (ret >= MAXPATHLEN) WARN("'%s' has stopped before sending its state", name);
return -1; return -1;
}
if (rename(oldname, newname)) { if (command.answer.ret < 0 || command.answer.pathlen < 0) {
SYSERROR("failed to rename cgroup %s->%s", oldname, newname); ERROR("failed to get state for '%s': %s",
name, strerror(-command.answer.ret));
return -1; return -1;
} }
DEBUG("'%s' renamed to '%s'", oldname, newname); *path = command.answer.path;
return 0; 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; const char *cgpath;
int ret = 0;
f = fopen(path, "w"); if (lxc_get_cgpath(&cgpath, subsystem, name, lxcpath) < 0)
if (!f) {
SYSERROR("failed to open '%s'", path);
return -1; return -1;
}
if (fprintf(f, "1") < 1) { return cgroup_path_get(path, subsystem, cgpath);
ERROR("failed to write flag to '%s'", path); }
ret = -1;
/*
* 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)); ret = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename);
if (ret <= 0) { if (ret < 0 || ret >= MAXPATHLEN) {
SYSERROR("failed to write pid '%ld' to fd '%d'", (long)pid, fd); ERROR("pathname too long");
ret = -1; return -1;
} else { }
ret = 0;
}
close(fd); return do_cgroup_set(path, value);
return ret;
} }
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); int ret;
return 0; 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; int fd, ret = -1;
char tasks[MAXPATHLEN], initcgroup[MAXPATHLEN]; char *dirpath;
char *cgmnt = mntent->mnt_dir; char path[MAXPATHLEN];
int flags;
int rc; 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, rc = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename);
get_init_cgroup(NULL, mntent, initcgroup),
(flags & CGROUP_NS_CGROUP) ? "" : "/lxc",
name);
if (rc < 0 || rc >= MAXPATHLEN) { if (rc < 0 || rc >= MAXPATHLEN) {
ERROR("pathname too long"); ERROR("pathname too long");
return -1; return -1;
} }
fd = open(tasks, O_WRONLY); fd = open(path, O_RDONLY);
if (fd < 0) { if (fd < 0) {
SYSERROR("failed to open '%s'", tasks); ERROR("open %s : %s", path, strerror(errno));
return -1; return -1;
} }
return fd; if (!len || !value) {
} char buf[100];
int count = 0;
static int lxc_one_cgroup_attach(const char *name, struct mntent *mntent, pid_t pid) while ((ret = read(fd, buf, 100)) > 0)
{ count += ret;
int fd; if (ret >= 0)
ret = count;
fd = lxc_one_cgroup_prepare_attach(name, mntent); } else {
if (fd < 0) { memset(value, 0, len);
return -1; 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; char *dpath;
int ret, err; char path[MAXPATHLEN];
int pid, ret, count = 0;
FILE *file;
int rc;
if (!fds) { ret = cgroup_path_get(&dpath, NULL, cgpath);
return 0; 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++) { file = fopen(path, "r");
err = lxc_one_cgroup_dispose_attach(*fds); if (!file) {
if (err) { SYSERROR("fopen '%s' failed", path);
ret = err; 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; FILE *f;
int err; char path[MAXPATHLEN];
int ret, retv;
if (!fds) { retv = snprintf(path, MAXPATHLEN, "%s/%s", dir, file);
if (retv < 0 || retv >= MAXPATHLEN)
return 0; return 0;
} f = fopen(path, "r");
ret = fscanf(f, "%d", &retv);
fclose(f);
if (ret != 1)
return 0;
return retv;
}
for (; *fds >= 0; fds++) { static void set_value(const char *dir, const char *file, long v)
err = lxc_one_cgroup_finish_attach(*fds, pid); {
if (err) { FILE *f;
/* get rid of the rest of them */ char path[MAXPATHLEN];
lxc_cgroup_dispose_attach(data); int retv;
return -1;
}
*fds = -1;
}
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; FILE *file = NULL;
int err = -1; struct mntent *mntent;
int found = 0; int ret, retv = -1;
int *fds; char path[MAXPATHLEN], initpath[MAXPATHLEN];
int i;
static const int MAXFDS = 256;
file = setmntent(MTAB, "r"); file = setmntent(MTAB, "r");
if (!file) { if (!file) {
...@@ -388,205 +580,145 @@ int lxc_cgroup_prepare_attach(const char *name, void **data) ...@@ -388,205 +580,145 @@ int lxc_cgroup_prepare_attach(const char *name, void **data)
return -1; 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))) { while ((mntent = getmntent(file))) {
if (i >= MAXFDS - 1) { char *init = get_init_cgroup(NULL, mntent, initpath, 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);
if (strcmp(mntent->mnt_type, "cgroup")) if (strcmp(mntent->mnt_type, "cgroup"))
continue; continue;
if (!mount_has_subsystem(mntent)) if (!mount_has_subsystem(mntent))
continue; continue;
INFO("[%d] found cgroup mounted at '%s',opts='%s'", /*
++found, mntent->mnt_dir, mntent->mnt_opts); * TODO - handle case where lxcgroup has subdirs? (i.e. build/l1)
* May not be worthwhile - remember cgroup depth has perf penalties
fds[i] = lxc_one_cgroup_prepare_attach(name, mntent); * */
if (fds[i] < 0) { ret = snprintf(path, MAXPATHLEN, "%s%s/%s",
err = fds[i]; mntent->mnt_dir, init, lxcgroup ? lxcgroup : "lxc");
lxc_cgroup_dispose_attach(fds); if (ret < 0 || ret >= MAXPATHLEN)
goto out; 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 * For a new container, find a cgroup path which is unique in all cgroup mounts.
* with 'cgparent/dead'. That way cgname can be reused. Return * I.e. if r1 is already running, then /lxc/r1-1 may be used.
* 0 on success, -1 on failure. *
* @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; int i = 0, ret;
char *retpath, path[MAXPATHLEN], initpath[MAXPATHLEN], *init;
/* tempnam problems don't matter here - cgroupfs will prevent char tail[12];
* duplicates if we race, and we'll just fail at that (unlikely) FILE *file = NULL;
* point struct mntent *mntent;
*/
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; if (create_lxcgroups(lxcgroup) < 0)
} return NULL;
/* again:
* create a cgroup for the container in a particular subsystem. file = setmntent(MTAB, "r");
*/ if (!file) {
static int lxc_one_cgroup_create(const char *name, SYSERROR("failed to open %s", MTAB);
struct mntent *mntent, pid_t pid) return NULL;
{
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;
} }
flags = get_cgroup_flags(mntent); if (i)
snprintf(tail, 12, "-%d", i);
else
*tail = '\0';
ret = snprintf(cgparent, MAXPATHLEN, "%s%s", cginit, while ((mntent = getmntent(file))) {
(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);
}
ret = snprintf(clonechild, MAXPATHLEN, "%s/cgroup.clone_children", if (strcmp(mntent->mnt_type, "cgroup"))
cginit); continue;
if (ret < 0 || ret >= MAXPATHLEN) { if (!mount_has_subsystem(mntent))
SYSERROR("Failed creating pathname for clone_children (%d)\n", ret); continue;
return -1;
}
/* we check if the kernel has clone_children, at this point if there init = get_init_cgroup(NULL, mntent, initpath, 1);
* no clone_children neither ns_cgroup, that means the cgroup is mounted /* find unused mnt_dir + init_cgroup + lxcgroup + name + -$i */
* without the ns_cgroup and it has not the compatibility flag ret = snprintf(path, MAXPATHLEN, "%s%s/%s/%s%s",
*/ mntent->mnt_dir, init,
if (access(clonechild, F_OK)) { lxcgroup ? lxcgroup : "lxc", name, tail);
ERROR("no ns_cgroup option specified"); if (ret < 0 || ret >= MAXPATHLEN)
return -1; goto fail;
}
/* enable the clone_children flag of the cgroup */ if (access(path, F_OK) == 0) goto next;
if (cgroup_enable_clone_children(clonechild)) {
SYSERROR("failed to enable 'clone_children flag");
return -1;
}
/* if cgparent does not exist, create it */ if (mkdir(path, 0755)) {
if (access(cgparent, F_OK)) { ERROR("Error creating cgroups");
ret = mkdir(cgparent, 0755); goto fail;
if (ret == -1 && errno != EEXIST) {
SYSERROR("failed to create '%s' directory", cgparent);
return -1;
} }
}
/* if (hasmntopt(mntent, "cpuset")) {
* There is a previous cgroup. Try to delete it. If that fails setup_cpuset(path);
* (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;
} }
} }
/* Let's create the cgroup */ endmntent(file);
if (mkdir(cgname, 0755)) {
SYSERROR("failed to create '%s' directory", cgname);
return -1;
}
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;
} }
/* int lxc_cgroup_enter(const char *cgpath, pid_t pid)
* for each mounted cgroup, create a cgroup for the container and attach a pid
*/
int lxc_cgroup_create(const char *name, pid_t pid)
{ {
char path[MAXPATHLEN], initpath[MAXPATHLEN], *init;
FILE *file = NULL, *fout;
struct mntent *mntent; struct mntent *mntent;
FILE *file = NULL; int ret, retv = -1;
int err = -1;
int found = 0;
file = setmntent(MTAB, "r"); file = setmntent(MTAB, "r");
if (!file) { if (!file) {
...@@ -595,31 +727,30 @@ int lxc_cgroup_create(const char *name, pid_t pid) ...@@ -595,31 +727,30 @@ int lxc_cgroup_create(const char *name, pid_t pid)
} }
while ((mntent = getmntent(file))) { while ((mntent = getmntent(file))) {
DEBUG("checking '%s' (%s)", mntent->mnt_dir, mntent->mnt_type);
if (strcmp(mntent->mnt_type, "cgroup")) if (strcmp(mntent->mnt_type, "cgroup"))
continue; continue;
if (!mount_has_subsystem(mntent)) if (!mount_has_subsystem(mntent))
continue; continue;
init = get_init_cgroup(NULL, mntent, initpath, 1);
INFO("[%d] found cgroup mounted at '%s',opts='%s'", ret = snprintf(path, MAXPATHLEN, "%s%s/%s/tasks",
++found, mntent->mnt_dir, mntent->mnt_opts); mntent->mnt_dir, init, cgpath);
if (ret < 0 || ret >= MAXPATHLEN) {
err = lxc_one_cgroup_create(name, mntent, pid); ERROR("entering cgroup");
if (err)
goto out; goto out;
}
err = lxc_one_cgroup_attach(name, mntent, pid); fout = fopen(path, "w");
if (err) if (!fout) {
ERROR("entering cgroup");
goto out; goto out;
}; }
fprintf(fout, "%d\n", (int)pid);
if (!found) fclose(fout);
ERROR("No cgroup mounted on the system"); }
retv = 0;
out: out:
endmntent(file); endmntent(file);
return err; return retv;
} }
int recursive_rmdir(char *dirname) int recursive_rmdir(char *dirname)
...@@ -667,16 +798,14 @@ 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 cgname[MAXPATHLEN], initcgroup[MAXPATHLEN];
char *cgmnt = mntent->mnt_dir; char *cgmnt = mntent->mnt_dir;
int flags = get_cgroup_flags(mntent);
int rc; int rc;
rc = snprintf(cgname, MAXPATHLEN, "%s%s%s/%s", cgmnt, rc = snprintf(cgname, MAXPATHLEN, "%s%s/%s", cgmnt,
get_init_cgroup(NULL, mntent, initcgroup), get_init_cgroup(NULL, mntent, initcgroup, 1), cgpath);
(flags & CGROUP_NS_CGROUP) ? "" : "/lxc", name);
if (rc < 0 || rc >= MAXPATHLEN) { if (rc < 0 || rc >= MAXPATHLEN) {
ERROR("name too long"); ERROR("name too long");
return -1; return -1;
...@@ -695,11 +824,11 @@ int lxc_one_cgroup_destroy(struct mntent *mntent, const char *name) ...@@ -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 * 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; struct mntent *mntent;
FILE *file = NULL; FILE *file = NULL;
int err = -1; int err, retv = 0;
file = setmntent(MTAB, "r"); file = setmntent(MTAB, "r");
if (!file) { if (!file) {
...@@ -713,168 +842,24 @@ int lxc_cgroup_destroy(const char *name) ...@@ -713,168 +842,24 @@ int lxc_cgroup_destroy(const char *name)
if (!mount_has_subsystem(mntent)) if (!mount_has_subsystem(mntent))
continue; continue;
err = lxc_one_cgroup_destroy(mntent, name); err = lxc_one_cgroup_destroy(mntent, cgpath);
if (err) if (err) // keep trying to clean up the others
break; retv = -1;
} }
endmntent(file); endmntent(file);
return err; return retv;
}
/*
* 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;
} }
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; const char *dirpath;
char *dirpath;
char path[MAXPATHLEN];
int rc;
ret = lxc_cgroup_path_get(&dirpath, filename, name); if (lxc_get_cgpath(&dirpath, NULL, name, lxcpath) < 0) {
if (ret) ERROR("Error getting cgroup for container %s: %s", lxcpath, name);
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));
return -1; return -1;
} }
INFO("joining pid %d to cgroup %s", pid, dirpath);
ret = write(fd, value, strlen(value)); return lxc_cgroup_enter(dirpath, pid);
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;
} }
...@@ -26,13 +26,13 @@ ...@@ -26,13 +26,13 @@
#define MAXPRIOLEN 24 #define MAXPRIOLEN 24
struct lxc_handler; struct lxc_handler;
extern int lxc_cgroup_create(const char *name, pid_t pid); extern int lxc_cgroup_destroy(const char *cgpath);
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_path_get(char **path, const char *subsystem, const char *name); const char *lxcpath);
extern int lxc_cgroup_nrtasks(const char *name); extern int lxc_cgroup_nrtasks(const char *cgpath);
extern int lxc_cgroup_attach(const char *name, pid_t pid); extern char *lxc_cgroup_path_create(const char *lxcgroup, const char *name);
extern int lxc_cgroup_prepare_attach(const char *name, void **data); extern int lxc_cgroup_enter(const char *cgpath, pid_t pid);
extern int lxc_cgroup_finish_attach(void *data, pid_t pid); extern int lxc_cgroup_attach(pid_t pid, const char *name, const char *lxcpath);
extern int lxc_cgroup_dispose_attach(void *data); extern int cgroup_path_get(char **path, const char *subsystem, const char *cgpath);
extern int lxc_ns_is_mounted(void); extern int lxc_get_cgpath(const char **path, const char *subsystem, const char *name, const char *lxcpath);
#endif #endif
...@@ -84,10 +84,19 @@ static int fill_sock_name(char *path, int len, const char *name, ...@@ -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) static int receive_answer(int sock, struct lxc_answer *answer)
{ {
int ret; int ret;
static char answerpath[MAXPATHLEN];
ret = lxc_af_unix_recv_fd(sock, &answer->fd, answer, sizeof(*answer)); ret = lxc_af_unix_recv_fd(sock, &answer->fd, answer, sizeof(*answer));
if (ret < 0) if (ret < 0)
ERROR("failed to receive answer for the command"); 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; return ret;
} }
...@@ -202,6 +211,7 @@ extern int lxc_stop_callback(int, struct lxc_request *, struct lxc_handler *); ...@@ -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_state_callback(int, struct lxc_request *, struct lxc_handler *);
extern int lxc_pid_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_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, static int trigger_command(int fd, struct lxc_request *request,
struct lxc_handler *handler) struct lxc_handler *handler)
...@@ -214,6 +224,7 @@ static int trigger_command(int fd, struct lxc_request *request, ...@@ -214,6 +224,7 @@ static int trigger_command(int fd, struct lxc_request *request,
[LXC_COMMAND_STATE] = lxc_state_callback, [LXC_COMMAND_STATE] = lxc_state_callback,
[LXC_COMMAND_PID] = lxc_pid_callback, [LXC_COMMAND_PID] = lxc_pid_callback,
[LXC_COMMAND_CLONE_FLAGS] = lxc_clone_flags_callback, [LXC_COMMAND_CLONE_FLAGS] = lxc_clone_flags_callback,
[LXC_COMMAND_CGROUP] = lxc_cgroup_callback,
}; };
if (request->type < 0 || request->type >= LXC_COMMAND_MAX) if (request->type < 0 || request->type >= LXC_COMMAND_MAX)
......
...@@ -29,6 +29,7 @@ enum { ...@@ -29,6 +29,7 @@ enum {
LXC_COMMAND_STATE, LXC_COMMAND_STATE,
LXC_COMMAND_PID, LXC_COMMAND_PID,
LXC_COMMAND_CLONE_FLAGS, LXC_COMMAND_CLONE_FLAGS,
LXC_COMMAND_CGROUP,
LXC_COMMAND_MAX, LXC_COMMAND_MAX,
}; };
...@@ -41,6 +42,8 @@ struct lxc_answer { ...@@ -41,6 +42,8 @@ struct lxc_answer {
int fd; int fd;
int ret; /* 0 on success, -errno on failure */ int ret; /* 0 on success, -errno on failure */
pid_t pid; pid_t pid;
int pathlen;
const char *path;
}; };
struct lxc_command { struct lxc_command {
......
...@@ -1375,7 +1375,7 @@ static int setup_kmsg(const struct lxc_rootfs *rootfs, ...@@ -1375,7 +1375,7 @@ static int setup_kmsg(const struct lxc_rootfs *rootfs,
return 0; 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_list *iterator;
struct lxc_cgroup *cg; struct lxc_cgroup *cg;
...@@ -1388,8 +1388,11 @@ int setup_cgroup(const char *name, struct lxc_list *cgroups) ...@@ -1388,8 +1388,11 @@ int setup_cgroup(const char *name, struct lxc_list *cgroups)
cg = iterator->elem; 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; goto out;
}
DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value); DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
} }
......
...@@ -282,7 +282,7 @@ struct lxc_conf { ...@@ -282,7 +282,7 @@ struct lxc_conf {
int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *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); extern int detect_shared_rootfs(void);
/* /*
......
...@@ -40,16 +40,11 @@ ...@@ -40,16 +40,11 @@
lxc_log_define(lxc_freezer, lxc); 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 freezer[MAXPATHLEN], *f;
char tmpf[32]; char tmpf[32];
int fd, ret; int fd, ret;
ret = lxc_cgroup_path_get(&nsgroup, "freezer", name);
if (ret)
return -1;
ret = snprintf(freezer, MAXPATHLEN, "%s/freezer.state", nsgroup); ret = snprintf(freezer, MAXPATHLEN, "%s/freezer.state", nsgroup);
if (ret >= MAXPATHLEN) { if (ret >= MAXPATHLEN) {
...@@ -59,7 +54,7 @@ static int freeze_unfreeze(const char *name, int freeze, const char *lxcpath) ...@@ -59,7 +54,7 @@ static int freeze_unfreeze(const char *name, int freeze, const char *lxcpath)
fd = open(freezer, O_RDWR); fd = open(freezer, O_RDWR);
if (fd < 0) { if (fd < 0) {
SYSERROR("failed to open freezer for '%s'", name); SYSERROR("failed to open freezer at '%s'", nsgroup);
return -1; return -1;
} }
...@@ -98,7 +93,8 @@ static int freeze_unfreeze(const char *name, int freeze, const char *lxcpath) ...@@ -98,7 +93,8 @@ static int freeze_unfreeze(const char *name, int freeze, const char *lxcpath)
ret = strncmp(f, tmpf, strlen(f)); ret = strncmp(f, tmpf, strlen(f));
if (!ret) if (!ret)
{ {
lxc_monitor_send_state(name, freeze ? FROZEN : THAWED, lxcpath); if (name)
lxc_monitor_send_state(name, freeze ? FROZEN : THAWED, lxcpath);
break; /* Success */ break; /* Success */
} }
...@@ -122,6 +118,18 @@ out: ...@@ -122,6 +118,18 @@ out:
return ret; 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) int lxc_freeze(const char *name, const char *lxcpath)
{ {
lxc_monitor_send_state(name, FREEZING, lxcpath); lxc_monitor_send_state(name, FREEZING, lxcpath);
...@@ -133,3 +141,14 @@ int lxc_unfreeze(const char *name, const char *lxcpath) ...@@ -133,3 +141,14 @@ int lxc_unfreeze(const char *name, const char *lxcpath)
return freeze_unfreeze(name, 0, 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);
}
...@@ -118,6 +118,14 @@ extern int lxc_freeze(const char *name, const char *lxcpath); ...@@ -118,6 +118,14 @@ extern int lxc_freeze(const char *name, const char *lxcpath);
extern int lxc_unfreeze(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 * Retrieve the container state
* @name : the name of the container * @name : the name of the container
* Returns the state of the container on success, < 0 otherwise * 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); ...@@ -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 * Set a specified value for a specified subsystem. The specified
* subsystem must be fully specified, eg. "cpu.shares" * 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 * @name : the name of the container
* @filename : the cgroup attribute filename * @filename : the cgroup attribute filename
* @value : the value to be set * @value : the value to be set
* @lxcpath : lxc config path for container
* Returns 0 on success, < 0 otherwise * 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 * Get a specified value for a specified subsystem. The specified
* subsystem must be fully specified, eg. "cpu.shares" * subsystem must be fully specified, eg. "cpu.shares"
* @name : the name of the container * @name : the name of the container
* @filename : the cgroup attribute filename * @filename : the cgroup attribute filename
* @value : the value to be set * @value : the value to be set
* @len : the len of the value variable * @len : the len of the value variable
* @lxcpath : lxc config path for container
* Returns the number of bytes read, < 0 on error * Returns the number of bytes read, < 0 on error
*/ */
extern int lxc_cgroup_get(const char *name, const char *filename, 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 * Retrieve the error string associated with the error returned by
......
...@@ -237,7 +237,7 @@ int main(int argc, char *argv[]) ...@@ -237,7 +237,7 @@ int main(int argc, char *argv[])
} }
if (!elevated_privileges) { 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) { if (ret < 0) {
ERROR("failed to attach process to cgroup"); ERROR("failed to attach process to cgroup");
return -1; return -1;
......
...@@ -78,7 +78,7 @@ int main(int argc, char *argv[]) ...@@ -78,7 +78,7 @@ int main(int argc, char *argv[])
value = my_args.argv[1]; value = my_args.argv[1];
if (value) { 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'", ERROR("failed to assign '%s' value to '%s' for '%s'",
value, state_object, my_args.name); value, state_object, my_args.name);
return -1; return -1;
...@@ -88,7 +88,7 @@ int main(int argc, char *argv[]) ...@@ -88,7 +88,7 @@ int main(int argc, char *argv[])
int ret; int ret;
char buffer[len]; 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) { if (ret < 0) {
ERROR("failed to retrieve value of '%s' for '%s'", ERROR("failed to retrieve value of '%s' for '%s'",
state_object, my_args.name); state_object, my_args.name);
......
...@@ -112,7 +112,6 @@ int main(int argc, char *argv[]) ...@@ -112,7 +112,6 @@ int main(int argc, char *argv[])
{ {
int opt, status; int opt, status;
int ret; int ret;
char *pid_name;
char *namespaces = NULL; char *namespaces = NULL;
char **args; char **args;
int flags = 0; int flags = 0;
...@@ -170,14 +169,5 @@ int main(int argc, char *argv[]) ...@@ -170,14 +169,5 @@ int main(int argc, char *argv[])
return -1; 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); return lxc_error_set_and_log(pid, status);
} }
...@@ -945,7 +945,7 @@ static bool lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, ...@@ -945,7 +945,7 @@ static bool lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys,
if (is_stopped_nolock(c)) if (is_stopped_nolock(c))
goto err; goto err;
ret = lxc_cgroup_set(c->name, subsys, value); ret = lxc_cgroup_set(c->name, subsys, value, c->config_path);
if (!ret) if (!ret)
b = true; b = true;
err: err:
...@@ -966,7 +966,7 @@ static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, c ...@@ -966,7 +966,7 @@ static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, c
if (is_stopped_nolock(c)) if (is_stopped_nolock(c))
goto out; goto out;
ret = lxc_cgroup_get(c->name, subsys, retv, inlen); ret = lxc_cgroup_get(c->name, subsys, retv, inlen, c->config_path);
out: out:
lxcunlock(c->privlock); lxcunlock(c->privlock);
......
...@@ -283,7 +283,7 @@ static int utmp_get_ntasks(struct lxc_handler *handler) ...@@ -283,7 +283,7 @@ static int utmp_get_ntasks(struct lxc_handler *handler)
{ {
int ntasks; int ntasks;
ntasks = lxc_cgroup_nrtasks(handler->name); ntasks = lxc_cgroup_nrtasks(handler->cgroup);
if (ntasks < 0) { if (ntasks < 0) {
ERROR("failed to get the number of tasks"); ERROR("failed to get the number of tasks");
......
...@@ -268,6 +268,7 @@ int lxc_pid_callback(int fd, struct lxc_request *request, ...@@ -268,6 +268,7 @@ int lxc_pid_callback(int fd, struct lxc_request *request,
struct lxc_answer answer; struct lxc_answer answer;
int ret; int ret;
memset(&answer, 0, sizeof(answer));
answer.pid = handler->pid; answer.pid = handler->pid;
answer.ret = 0; answer.ret = 0;
...@@ -285,12 +286,49 @@ int lxc_pid_callback(int fd, struct lxc_request *request, ...@@ -285,12 +286,49 @@ int lxc_pid_callback(int fd, struct lxc_request *request,
return 0; 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, int lxc_clone_flags_callback(int fd, struct lxc_request *request,
struct lxc_handler *handler) struct lxc_handler *handler)
{ {
struct lxc_answer answer; struct lxc_answer answer;
int ret; int ret;
memset(&answer, 0, sizeof(answer));
answer.pid = 0; answer.pid = 0;
answer.ret = handler->clone_flags; answer.ret = handler->clone_flags;
...@@ -467,7 +505,7 @@ out_free: ...@@ -467,7 +505,7 @@ out_free:
return NULL; 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 /* The STOPPING state is there for future cleanup code
* which can take awhile * which can take awhile
...@@ -487,6 +525,11 @@ void lxc_fini(const char *name, struct lxc_handler *handler) ...@@ -487,6 +525,11 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
close(handler->conf->maincmd_fd); close(handler->conf->maincmd_fd);
handler->conf->maincmd_fd = -1; handler->conf->maincmd_fd = -1;
free(handler->name); free(handler->name);
if (handler->cgroup) {
lxc_cgroup_destroy(handler->cgroup);
free(handler->cgroup);
handler->cgroup = NULL;
}
free(handler); free(handler);
} }
...@@ -756,7 +799,11 @@ int lxc_spawn(struct lxc_handler *handler) ...@@ -756,7 +799,11 @@ int lxc_spawn(struct lxc_handler *handler)
if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE)) if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE))
failed_before_rename = 1; 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; goto out_delete_net;
if (failed_before_rename) if (failed_before_rename)
...@@ -786,7 +833,7 @@ int lxc_spawn(struct lxc_handler *handler) ...@@ -786,7 +833,7 @@ int lxc_spawn(struct lxc_handler *handler)
if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CONFIGURE)) if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CONFIGURE))
goto out_delete_net; 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); ERROR("failed to setup the cgroups for '%s'", name);
goto out_delete_net; goto out_delete_net;
} }
...@@ -904,7 +951,6 @@ out_fini: ...@@ -904,7 +951,6 @@ out_fini:
lxc_delete_network(handler); lxc_delete_network(handler);
out_fini_nonet: out_fini_nonet:
lxc_cgroup_destroy(name);
lxc_fini(name, handler); lxc_fini(name, handler);
return err; return err;
......
...@@ -51,6 +51,7 @@ struct lxc_handler { ...@@ -51,6 +51,7 @@ struct lxc_handler {
#endif #endif
int pinfd; int pinfd;
const char *lxcpath; const char *lxcpath;
char *cgroup;
}; };
extern struct lxc_handler *lxc_init(const char *name, struct lxc_conf *, const char *); 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 *); ...@@ -58,7 +59,6 @@ extern int lxc_spawn(struct lxc_handler *);
extern int lxc_poll(const char *name, struct lxc_handler *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_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_set_state(const char *, struct lxc_handler *, lxc_state_t);
extern int lxc_check_inherited(struct lxc_conf *conf, int fd_to_ignore); extern int lxc_check_inherited(struct lxc_conf *conf, int fd_to_ignore);
int __lxc_start(const char *, struct lxc_conf *, struct lxc_operations *, int __lxc_start(const char *, struct lxc_conf *, struct lxc_operations *,
......
...@@ -66,7 +66,7 @@ lxc_state_t lxc_str2state(const char *state) ...@@ -66,7 +66,7 @@ lxc_state_t lxc_str2state(const char *state)
return -1; return -1;
} }
static int freezer_state(const char *name) static int freezer_state(const char *name, const char *lxcpath)
{ {
char *nsgroup; char *nsgroup;
char freezer[MAXPATHLEN]; char freezer[MAXPATHLEN];
...@@ -74,7 +74,7 @@ static int freezer_state(const char *name) ...@@ -74,7 +74,7 @@ static int freezer_state(const char *name)
FILE *file; FILE *file;
int err; int err;
err = lxc_cgroup_path_get(&nsgroup, "freezer", name); err = lxc_cgroup_path_get(&nsgroup, "freezer", name, lxcpath);
if (err) if (err)
return -1; return -1;
...@@ -132,7 +132,7 @@ static lxc_state_t __lxc_getstate(const char *name, const char *lxcpath) ...@@ -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) 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) if (state != FROZEN && state != FREEZING)
state = __lxc_getstate(name, lxcpath); state = __lxc_getstate(name, lxcpath);
return state; return state;
...@@ -148,6 +148,7 @@ extern int lxc_state_callback(int fd, struct lxc_request *request, ...@@ -148,6 +148,7 @@ extern int lxc_state_callback(int fd, struct lxc_request *request,
struct lxc_answer answer; struct lxc_answer answer;
int ret; int ret;
memset(&answer, 0, sizeof(answer));
answer.ret = handler->state; answer.ret = handler->state;
ret = send(fd, &answer, sizeof(answer), 0); ret = send(fd, &answer, sizeof(answer), 0);
......
...@@ -83,9 +83,10 @@ extern int lxc_stop_callback(int fd, struct lxc_request *request, ...@@ -83,9 +83,10 @@ extern int lxc_stop_callback(int fd, struct lxc_request *request,
struct lxc_answer answer; struct lxc_answer answer;
int ret; int ret;
memset(&answer, 0, sizeof(answer));
answer.ret = kill(handler->pid, SIGKILL); answer.ret = kill(handler->pid, SIGKILL);
if (!answer.ret) { if (!answer.ret) {
ret = lxc_unfreeze(handler->name, handler->lxcpath); ret = lxc_unfreeze_bypath(handler->cgroup);
if (!ret) if (!ret)
return 0; return 0;
......
...@@ -12,6 +12,7 @@ lxc_test_shutdowntest_SOURCES = shutdowntest.c ...@@ -12,6 +12,7 @@ lxc_test_shutdowntest_SOURCES = shutdowntest.c
lxc_test_get_item_SOURCES = get_item.c lxc_test_get_item_SOURCES = get_item.c
lxc_test_getkeys_SOURCES = getkeys.c lxc_test_getkeys_SOURCES = getkeys.c
lxc_test_lxcpath_SOURCES = lxcpath.c lxc_test_lxcpath_SOURCES = lxcpath.c
lxc_test_cgpath_SOURCES = cgpath.c
AM_CFLAGS=-I$(top_srcdir)/src \ AM_CFLAGS=-I$(top_srcdir)/src \
-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
...@@ -21,6 +22,7 @@ AM_CFLAGS=-I$(top_srcdir)/src \ ...@@ -21,6 +22,7 @@ AM_CFLAGS=-I$(top_srcdir)/src \
bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \ bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \
lxc-test-destroytest lxc-test-saveconfig lxc-test-createtest \ 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 endif
/* liblxcapi
*
* Copyright 2012 Serge Hallyn <serge.hallyn@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;
}
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
#define MYNAME "lxctest1" #define MYNAME "lxctest1"
#define TSTERR(x) do { \ #define TSTERR(x) do { \
fprintf(stderr, "%d: %s", __LINE__, x); \ fprintf(stderr, "%d: %s\n", __LINE__, x); \
} while (0) } while (0)
int main() int main()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment