Commit 460a1cf0 by David Ward Committed by Daniel Lezcano

lxc-attach: unify code for attaching a pid to a cgroup

To attach a new pid to the cgroups for an existing container, we can use the same method that we did when we started the container: iterate over all the mounted cgroup hierarchies; find the cgroup that pid 1 is in for each hierarchy; add 'lxc/<name>' to the end of it; then write the pid to the 'tasks' file in that cgroup. (The only difference is that we do not create the cgroup again.) Note that we follow exactly the same iteration pattern to delete our cgroups when a container is shutdown. There may be situations where additional cgroups hierarchies are mounted after the container is started, or the cgroup for pid 1 gets reassigned. But we currently don't handle any of these cases in the shutdown code or anywhere else, so it doesn't make sense to try to handle these cases for lxc-attach by itself. Aside from simplifying the code, this change makes it easier to solve a different problem: ignoring hierarchies that are not bound to any subsystems (like 'systemd'). Signed-off-by: 's avatarDavid Ward <david.ward@ll.mit.edu> Signed-off-by: 's avatarDaniel Lezcano <dlezcano@fr.ibm.com>
parent ad08bbb7
...@@ -60,10 +60,9 @@ struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) ...@@ -60,10 +60,9 @@ struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
struct lxc_proc_context_info *info = calloc(1, sizeof(*info)); struct lxc_proc_context_info *info = calloc(1, sizeof(*info));
FILE *proc_file; FILE *proc_file;
char proc_fn[MAXPATHLEN]; char proc_fn[MAXPATHLEN];
char *line = NULL, *ptr, *ptr2; char *line = NULL;
size_t line_bufsz = 0; size_t line_bufsz = 0;
int ret, found, l; int ret, found;
int i;
if (!info) { if (!info) {
SYSERROR("Could not allocate memory."); SYSERROR("Could not allocate memory.");
...@@ -114,117 +113,14 @@ struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) ...@@ -114,117 +113,14 @@ struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
goto out_error; goto out_error;
} }
/* read cgroups */
snprintf(proc_fn, MAXPATHLEN, "/proc/%d/cgroup", pid);
proc_file = fopen(proc_fn, "r");
if (!proc_file) {
SYSERROR("Could not open %s", proc_fn);
goto out_error;
}
/* we don't really know how many cgroup subsystems there are
* mounted, so we go through the whole file twice */
i = 0;
while (getline(&line, &line_bufsz, proc_file) != -1) {
/* we assume that all lines containing at least two colons
* are valid */
ptr = strchr(line, ':');
if (ptr && strchr(ptr + 1, ':'))
i++;
}
rewind(proc_file);
info->cgroups = calloc(i, sizeof(*(info->cgroups)));
info->cgroups_count = i;
i = 0;
while (getline(&line, &line_bufsz, proc_file) != -1 && i < info->cgroups_count) {
/* format of the lines is:
* id:subsystems:path, where subsystems are separated by
* commas and each subsystem may also be of the form
* name=xxx if it describes a private named hierarchy
* we will ignore the id in the following */
ptr = strchr(line, ':');
ptr2 = ptr ? strchr(ptr + 1, ':') : NULL;
/* ignore invalid lines */
if (!ptr || !ptr2) continue;
l = strlen(ptr2) - 1;
if (ptr2[l] == '\n')
ptr2[l] = '\0';
info->cgroups[i].subsystems = strndup(ptr + 1, ptr2 - (ptr + 1));
info->cgroups[i].cgroup = strdup(ptr2 + 1);
i++;
}
free(line);
fclose(proc_file);
return info; return info;
out_error: out_error:
lxc_proc_free_context_info(info); free(info);
free(line); free(line);
return NULL; return NULL;
} }
void lxc_proc_free_context_info(struct lxc_proc_context_info *info)
{
if (!info)
return;
if (info->cgroups) {
int i;
for (i = 0; i < info->cgroups_count; i++) {
free(info->cgroups[i].subsystems);
free(info->cgroups[i].cgroup);
}
}
free(info->cgroups);
free(info);
}
int lxc_attach_proc_to_cgroups(pid_t pid, struct lxc_proc_context_info *ctx)
{
int i, ret;
if (!ctx) {
ERROR("No valid context supplied when asked to attach "
"process to cgroups.");
return -1;
}
for (i = 0; i < ctx->cgroups_count; i++) {
char *path;
/* the kernel should return paths that start with '/' */
if (ctx->cgroups[i].cgroup[0] != '/') {
ERROR("For cgroup subsystem(s) %s the path '%s' does "
"not start with a '/'",
ctx->cgroups[i].subsystems,
ctx->cgroups[i].cgroup);
return -1;
}
/* lxc_cgroup_path_get can process multiple subsystems */
ret = lxc_cgroup_path_get(&path, ctx->cgroups[i].subsystems,
&ctx->cgroups[i].cgroup[1]);
if (ret)
return -1;
ret = lxc_cgroup_attach(path, pid);
if (ret)
return -1;
}
return 0;
}
int lxc_attach_to_ns(pid_t pid) int lxc_attach_to_ns(pid_t pid)
{ {
char path[MAXPATHLEN]; char path[MAXPATHLEN];
......
...@@ -26,22 +26,13 @@ ...@@ -26,22 +26,13 @@
#include <sys/types.h> #include <sys/types.h>
struct lxc_proc_cgroup_info {
char *subsystems;
char *cgroup;
};
struct lxc_proc_context_info { struct lxc_proc_context_info {
unsigned long personality; unsigned long personality;
unsigned long long capability_mask; unsigned long long capability_mask;
struct lxc_proc_cgroup_info* cgroups;
int cgroups_count;
}; };
extern struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid); extern struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid);
extern void lxc_proc_free_context_info(struct lxc_proc_context_info *info);
extern int lxc_attach_proc_to_cgroups(pid_t pid, struct lxc_proc_context_info *ctx);
extern int lxc_attach_to_ns(pid_t other_pid); extern int lxc_attach_to_ns(pid_t other_pid);
extern int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx); extern int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx);
......
...@@ -53,35 +53,6 @@ enum { ...@@ -53,35 +53,6 @@ enum {
CGROUP_CLONE_CHILDREN, CGROUP_CLONE_CHILDREN,
}; };
static char *hasmntopt_multiple(struct mntent *mntent, const char *options)
{
const char *ptr = options;
const char *ptr2 = strchr(options, ',');
char *result;
while (ptr2 != NULL) {
char *option = strndup(ptr, ptr2 - ptr);
if (!option) {
SYSERROR("Temporary memory allocation error");
return NULL;
}
result = hasmntopt(mntent, option);
free(option);
if (!result) {
return NULL;
}
ptr = ptr2 + 1;
ptr2 = strchr(ptr, ',');
}
/* for multiple mount options, the return value is basically NULL
* or non-NULL, so this should suffice for our purposes */
return hasmntopt(mntent, ptr);
}
/* /*
* get_init_cgroup: get the cgroup init is in. * get_init_cgroup: get the cgroup init is in.
* dsg: preallocated buffer to put the output in * dsg: preallocated buffer to put the output in
...@@ -168,7 +139,7 @@ static int get_cgroup_mount(const char *subsystem, char *mnt) ...@@ -168,7 +139,7 @@ static int get_cgroup_mount(const char *subsystem, char *mnt)
while ((mntent = getmntent(file))) { while ((mntent = getmntent(file))) {
if (strcmp(mntent->mnt_type, "cgroup")) if (strcmp(mntent->mnt_type, "cgroup"))
continue; continue;
if (subsystem && !hasmntopt_multiple(mntent, subsystem)) if (subsystem && !hasmntopt(mntent, subsystem))
continue; continue;
flags = get_cgroup_flags(mntent); flags = get_cgroup_flags(mntent);
...@@ -243,13 +214,20 @@ static int cgroup_enable_clone_children(const char *path) ...@@ -243,13 +214,20 @@ static int cgroup_enable_clone_children(const char *path)
return ret; return ret;
} }
int lxc_cgroup_attach(const char *path, pid_t pid) static int lxc_one_cgroup_attach(const char *name,
struct mntent *mntent, pid_t pid)
{ {
FILE *f; FILE *f;
char tasks[MAXPATHLEN]; char tasks[MAXPATHLEN], initcgroup[MAXPATHLEN];
int ret = 0; char *cgmnt = mntent->mnt_dir;
int flags, ret = 0;
flags = get_cgroup_flags(mntent);
snprintf(tasks, MAXPATHLEN, "%s/tasks", path); snprintf(tasks, MAXPATHLEN, "%s%s%s/%s/tasks", cgmnt,
get_init_cgroup(NULL, mntent, initcgroup),
(flags & CGROUP_NS_CGROUP) ? "" : "/lxc",
name);
f = fopen(tasks, "w"); f = fopen(tasks, "w");
if (!f) { if (!f) {
...@@ -268,6 +246,44 @@ int lxc_cgroup_attach(const char *path, pid_t pid) ...@@ -268,6 +246,44 @@ int lxc_cgroup_attach(const char *path, pid_t pid)
} }
/* /*
* for each mounted cgroup, attach a pid to the cgroup for the container
*/
int lxc_cgroup_attach(const char *name, pid_t pid)
{
struct mntent *mntent;
FILE *file = NULL;
int err = -1;
int found = 0;
file = setmntent(MTAB, "r");
if (!file) {
SYSERROR("failed to open %s", MTAB);
return -1;
}
while ((mntent = getmntent(file))) {
DEBUG("checking '%s' (%s)", mntent->mnt_dir, mntent->mnt_type);
if (strcmp(mntent->mnt_type, "cgroup"))
continue;
INFO("[%d] found cgroup mounted at '%s',opts='%s'",
++found, mntent->mnt_dir, mntent->mnt_opts);
err = lxc_one_cgroup_attach(name, mntent, pid);
if (err)
goto out;
};
if (!found)
ERROR("No cgroup mounted on the system");
out:
endmntent(file);
return err;
}
/*
* rename cgname, which is under cgparent, to a new name starting * rename cgname, which is under cgparent, to a new name starting
* with 'cgparent/dead'. That way cgname can be reused. Return * with 'cgparent/dead'. That way cgname can be reused. Return
* 0 on success, -1 on failure. * 0 on success, -1 on failure.
...@@ -378,20 +394,13 @@ static int lxc_one_cgroup_create(const char *name, ...@@ -378,20 +394,13 @@ static int lxc_one_cgroup_create(const char *name,
return -1; return -1;
} }
/* Let's add the pid to the 'tasks' file */
if (lxc_cgroup_attach(cgname, pid)) {
SYSERROR("failed to attach pid '%d' to '%s'", pid, cgname);
rmdir(cgname);
return -1;
}
INFO("created cgroup '%s'", cgname); INFO("created cgroup '%s'", cgname);
return 0; return 0;
} }
/* /*
* for each mounted cgroup, create a cgroup for the container * for each mounted cgroup, create a cgroup for the container and attach a pid
*/ */
int lxc_cgroup_create(const char *name, pid_t pid) int lxc_cgroup_create(const char *name, pid_t pid)
{ {
...@@ -418,6 +427,10 @@ int lxc_cgroup_create(const char *name, pid_t pid) ...@@ -418,6 +427,10 @@ int lxc_cgroup_create(const char *name, pid_t pid)
err = lxc_one_cgroup_create(name, mntent, pid); err = lxc_one_cgroup_create(name, mntent, pid);
if (err) if (err)
goto out; goto out;
err = lxc_one_cgroup_attach(name, mntent, pid);
if (err)
goto out;
}; };
if (!found) if (!found)
......
...@@ -30,6 +30,6 @@ extern int lxc_cgroup_create(const char *name, pid_t pid); ...@@ -30,6 +30,6 @@ extern int lxc_cgroup_create(const char *name, pid_t pid);
extern int lxc_cgroup_destroy(const char *name); extern int lxc_cgroup_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);
extern int lxc_cgroup_nrtasks(const char *name); extern int lxc_cgroup_nrtasks(const char *name);
extern int lxc_cgroup_attach(const char *path, pid_t pid); extern int lxc_cgroup_attach(const char *name, pid_t pid);
extern int lxc_ns_is_mounted(void); extern int lxc_ns_is_mounted(void);
#endif #endif
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
#include "commands.h" #include "commands.h"
#include "arguments.h" #include "arguments.h"
#include "caps.h" #include "caps.h"
#include "attach.h" #include "cgroup.h"
#include "confile.h" #include "confile.h"
#include "start.h" #include "start.h"
#include "sync.h" #include "sync.h"
...@@ -150,7 +150,7 @@ int main(int argc, char *argv[]) ...@@ -150,7 +150,7 @@ int main(int argc, char *argv[])
if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE)) if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE))
return -1; return -1;
if (!elevated_privileges && lxc_attach_proc_to_cgroups(pid, init_ctx)) if (!elevated_privileges && lxc_cgroup_attach(my_args.name, pid))
return -1; return -1;
/* tell the child we are done initializing */ /* tell the child we are done initializing */
......
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