split cgroup handling into discrete backends

- refactor cgroup into two backends, the classic cgfs driver and the new
  cgmanager. Instead of lxc_handler knowing about the internals of each,
  have it just store an opaque pointer to a struct that is private to
  each backend.

- rename a couple of cgroup functions for consistency: those that are
  considered an API (ie. exported by lxc.h) begin with lxc_ and those that
  are not are just cgroup_*

- made as many backend routines static as possible, only cg*_ops_init is
  exported

- made a nrtasks op which is needed by the utmp code for monitoring
  container shutdown, currently only implemented for the cgfs backend

Signed-off-by: Dwight Engen <dwight.engen@oracle.com>
Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
This commit is contained in:
Dwight Engen 2014-02-05 16:59:26 -05:00 committed by Serge Hallyn
parent 20e2ae994f
commit 4fb3cba5bc
14 changed files with 633 additions and 657 deletions

View File

@ -62,7 +62,8 @@ liblxc_so_SOURCES = \
freezer.c \ freezer.c \
error.h error.c \ error.h error.c \
parse.c parse.h \ parse.c parse.h \
cgfs.c cgroup.h \ cgfs.c \
cgroup.c cgroup.h \
lxc.h \ lxc.h \
utils.c utils.h \ utils.c utils.h \
sync.c sync.h \ sync.c sync.h \

View File

@ -699,7 +699,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
/* attach to cgroup, if requested */ /* attach to cgroup, if requested */
if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) { if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) {
if (!lxc_cgroup_attach(name, lxcpath, pid)) if (!cgroup_attach(name, lxcpath, pid))
goto cleanup_error; goto cleanup_error;
} }

View File

@ -55,7 +55,75 @@
#include <mntent.h> #include <mntent.h>
#endif #endif
lxc_log_define(lxc_cgroup, lxc); struct cgroup_hierarchy;
struct cgroup_meta_data;
struct cgroup_mount_point;
/*
* cgroup_meta_data: the metadata about the cgroup infrastructure on this
* host
*/
struct cgroup_meta_data {
ptrdiff_t ref; /* simple refcount */
struct cgroup_hierarchy **hierarchies;
struct cgroup_mount_point **mount_points;
int maximum_hierarchy;
};
/*
* cgroup_hierarchy: describes a single cgroup hierarchy
* (may have multiple mount points)
*/
struct cgroup_hierarchy {
int index;
bool used; /* false if the hierarchy should be ignored by lxc */
char **subsystems;
struct cgroup_mount_point *rw_absolute_mount_point;
struct cgroup_mount_point *ro_absolute_mount_point;
struct cgroup_mount_point **all_mount_points;
size_t all_mount_point_capacity;
};
/*
* cgroup_mount_point: a mount point to where a hierarchy
* is mounted to
*/
struct cgroup_mount_point {
struct cgroup_hierarchy *hierarchy;
char *mount_point;
char *mount_prefix;
bool read_only;
bool need_cpuset_init;
};
/*
* cgroup_process_info: describes the membership of a
* process to the different cgroup
* hierarchies
*
* Note this is the per-process info tracked by the cgfs_ops.
* This is not used with cgmanager.
*/
struct cgroup_process_info {
struct cgroup_process_info *next;
struct cgroup_meta_data *meta_ref;
struct cgroup_hierarchy *hierarchy;
char *cgroup_path;
char *cgroup_path_sub;
char **created_paths;
size_t created_paths_capacity;
size_t created_paths_count;
struct cgroup_mount_point *designated_mount_point;
};
struct cgfs_data {
char *name;
const char *cgroup_pattern;
struct cgroup_meta_data *meta;
struct cgroup_process_info *info;
};
lxc_log_define(lxc_cgfs, lxc);
static struct cgroup_process_info *lxc_cgroup_process_info_getx(const char *proc_pid_cgroup_str, struct cgroup_meta_data *meta); static struct cgroup_process_info *lxc_cgroup_process_info_getx(const char *proc_pid_cgroup_str, struct cgroup_meta_data *meta);
static char **subsystems_from_mount_options(const char *mount_options, char **kernel_list); static char **subsystems_from_mount_options(const char *mount_options, char **kernel_list);
@ -68,27 +136,22 @@ static char *cgroup_to_absolute_path(struct cgroup_mount_point *mp, const char *
static struct cgroup_process_info *find_info_for_subsystem(struct cgroup_process_info *info, const char *subsystem); static struct cgroup_process_info *find_info_for_subsystem(struct cgroup_process_info *info, const char *subsystem);
static int do_cgroup_get(const char *cgroup_path, const char *sub_filename, char *value, size_t len); static int do_cgroup_get(const char *cgroup_path, const char *sub_filename, char *value, size_t len);
static int do_cgroup_set(const char *cgroup_path, const char *sub_filename, const char *value); static int do_cgroup_set(const char *cgroup_path, const char *sub_filename, const char *value);
static bool cgroup_devices_has_allow_or_deny(struct lxc_handler *h, char *v, bool for_allow); static bool cgroup_devices_has_allow_or_deny(struct cgfs_data *d, char *v, bool for_allow);
static int do_setup_cgroup_limits(struct lxc_handler *h, struct lxc_list *cgroup_settings, bool do_devices); static int do_setup_cgroup_limits(struct cgfs_data *d, struct lxc_list *cgroup_settings, bool do_devices);
static int cgroup_recursive_task_count(const char *cgroup_path); static int cgroup_recursive_task_count(const char *cgroup_path);
static int count_lines(const char *fn); static int count_lines(const char *fn);
static int handle_cgroup_settings(struct cgroup_mount_point *mp, char *cgroup_path); static int handle_cgroup_settings(struct cgroup_mount_point *mp, char *cgroup_path);
static bool init_cpuset_if_needed(struct cgroup_mount_point *mp, const char *path); static bool init_cpuset_if_needed(struct cgroup_mount_point *mp, const char *path);
static struct cgroup_ops cgfs_ops; static struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist);
struct cgroup_ops *active_cg_ops = &cgfs_ops; static struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data);
static void init_cg_ops(void); static struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data);
#ifdef HAVE_CGMANAGER /* free process membership information */
/* this needs to be mutexed for api use */ static void lxc_cgroup_process_info_free(struct cgroup_process_info *info);
extern bool cgmanager_initialized; static void lxc_cgroup_process_info_free_and_remove(struct cgroup_process_info *info);
extern bool use_cgmanager;
extern bool lxc_init_cgmanager(void); static struct cgroup_ops cgfs_ops;
#else
static bool cgmanager_initialized = false;
static bool use_cgmanager = false;
static bool lxc_init_cgmanager(void) { return false; }
#endif
static int cgroup_rmdir(char *dirname) static int cgroup_rmdir(char *dirname)
{ {
@ -159,7 +222,7 @@ static int cgroup_rmdir(char *dirname)
return failed ? -1 : 0; return failed ? -1 : 0;
} }
struct cgroup_meta_data *lxc_cgroup_load_meta() static struct cgroup_meta_data *lxc_cgroup_load_meta()
{ {
const char *cgroup_use = NULL; const char *cgroup_use = NULL;
char **cgroup_use_list = NULL; char **cgroup_use_list = NULL;
@ -184,7 +247,7 @@ struct cgroup_meta_data *lxc_cgroup_load_meta()
} }
/* Step 1: determine all kernel subsystems */ /* Step 1: determine all kernel subsystems */
bool find_cgroup_subsystems(char ***kernel_subsystems) static bool find_cgroup_subsystems(char ***kernel_subsystems)
{ {
FILE *proc_cgroups; FILE *proc_cgroups;
bool bret = false; bool bret = false;
@ -470,7 +533,7 @@ out:
return bret; return bret;
} }
struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist) static struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist)
{ {
bool all_kernel_subsystems = true; bool all_kernel_subsystems = true;
bool all_named_subsystems = false; bool all_named_subsystems = false;
@ -526,13 +589,13 @@ out_error:
return NULL; return NULL;
} }
struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data) static struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data)
{ {
meta_data->ref++; meta_data->ref++;
return meta_data; return meta_data;
} }
struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data) static struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data)
{ {
size_t i; size_t i;
if (!meta_data) if (!meta_data)
@ -549,7 +612,7 @@ struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data)
return NULL; return NULL;
} }
struct cgroup_hierarchy *lxc_cgroup_find_hierarchy(struct cgroup_meta_data *meta_data, const char *subsystem) static struct cgroup_hierarchy *lxc_cgroup_find_hierarchy(struct cgroup_meta_data *meta_data, const char *subsystem)
{ {
size_t i; size_t i;
for (i = 0; i <= meta_data->maximum_hierarchy; i++) { for (i = 0; i <= meta_data->maximum_hierarchy; i++) {
@ -560,7 +623,7 @@ struct cgroup_hierarchy *lxc_cgroup_find_hierarchy(struct cgroup_meta_data *meta
return NULL; return NULL;
} }
struct cgroup_mount_point *lxc_cgroup_find_mount_point(struct cgroup_hierarchy *hierarchy, const char *group, bool should_be_writable) static struct cgroup_mount_point *lxc_cgroup_find_mount_point(struct cgroup_hierarchy *hierarchy, const char *group, bool should_be_writable)
{ {
struct cgroup_mount_point **mps; struct cgroup_mount_point **mps;
struct cgroup_mount_point *current_result = NULL; struct cgroup_mount_point *current_result = NULL;
@ -600,7 +663,7 @@ struct cgroup_mount_point *lxc_cgroup_find_mount_point(struct cgroup_hierarchy *
return current_result; return current_result;
} }
char *lxc_cgroup_find_abs_path(const char *subsystem, const char *group, bool should_be_writable, const char *suffix) static char *lxc_cgroup_find_abs_path(const char *subsystem, const char *group, bool should_be_writable, const char *suffix)
{ {
struct cgroup_meta_data *meta_data; struct cgroup_meta_data *meta_data;
struct cgroup_hierarchy *h; struct cgroup_hierarchy *h;
@ -634,19 +697,19 @@ out_error:
return NULL; return NULL;
} }
struct cgroup_process_info *lxc_cgroup_process_info_get(pid_t pid, struct cgroup_meta_data *meta) static struct cgroup_process_info *lxc_cgroup_process_info_get(pid_t pid, struct cgroup_meta_data *meta)
{ {
char pid_buf[32]; char pid_buf[32];
snprintf(pid_buf, 32, "/proc/%lu/cgroup", (unsigned long)pid); snprintf(pid_buf, 32, "/proc/%lu/cgroup", (unsigned long)pid);
return lxc_cgroup_process_info_getx(pid_buf, meta); return lxc_cgroup_process_info_getx(pid_buf, meta);
} }
struct cgroup_process_info *lxc_cgroup_process_info_get_init(struct cgroup_meta_data *meta) static struct cgroup_process_info *lxc_cgroup_process_info_get_init(struct cgroup_meta_data *meta)
{ {
return lxc_cgroup_process_info_get(1, meta); return lxc_cgroup_process_info_get(1, meta);
} }
struct cgroup_process_info *lxc_cgroup_process_info_get_self(struct cgroup_meta_data *meta) static struct cgroup_process_info *lxc_cgroup_process_info_get_self(struct cgroup_meta_data *meta)
{ {
struct cgroup_process_info *i; struct cgroup_process_info *i;
i = lxc_cgroup_process_info_getx("/proc/self/cgroup", meta); i = lxc_cgroup_process_info_getx("/proc/self/cgroup", meta);
@ -724,7 +787,7 @@ static char *cgroup_rename_nsgroup(const char *mountpath, const char *oldname, p
} }
/* create a new cgroup */ /* create a new cgroup */
struct cgroup_process_info *lxc_cgroupfs_create(const char *name, const char *path_pattern, struct cgroup_meta_data *meta_data, const char *sub_pattern) static struct cgroup_process_info *lxc_cgroupfs_create(const char *name, const char *path_pattern, struct cgroup_meta_data *meta_data, const char *sub_pattern)
{ {
char **cgroup_path_components = NULL; char **cgroup_path_components = NULL;
char **p = NULL; char **p = NULL;
@ -817,7 +880,7 @@ struct cgroup_process_info *lxc_cgroupfs_create(const char *name, const char *pa
} }
goto find_name_on_this_level; goto find_name_on_this_level;
cleanup_name_on_this_level: cleanup_name_on_this_level:
/* This is reached if we found a name clash. /* This is reached if we found a name clash.
* In that case, remove the cgroup from all previous hierarchies * In that case, remove the cgroup from all previous hierarchies
@ -836,7 +899,7 @@ struct cgroup_process_info *lxc_cgroupfs_create(const char *name, const char *pa
current_component = current_subpath = NULL; current_component = current_subpath = NULL;
/* try again with another suffix */ /* try again with another suffix */
++suffix; ++suffix;
find_name_on_this_level: find_name_on_this_level:
/* determine name of the path component we should create */ /* determine name of the path component we should create */
if (contains_name && suffix > 0) { if (contains_name && suffix > 0) {
@ -937,7 +1000,7 @@ struct cgroup_process_info *lxc_cgroupfs_create(const char *name, const char *pa
free(current_component); free(current_component);
current_component = current_subpath = NULL; current_component = current_subpath = NULL;
continue; continue;
cleanup_from_error: cleanup_from_error:
/* called if an error occured in the loop, so we /* called if an error occured in the loop, so we
* do some additional cleanup here * do some additional cleanup here
@ -985,7 +1048,7 @@ out_initial_error:
return NULL; return NULL;
} }
int lxc_cgroup_create_legacy(struct cgroup_process_info *base_info, const char *name, pid_t pid) static int lxc_cgroup_create_legacy(struct cgroup_process_info *base_info, const char *name, pid_t pid)
{ {
struct cgroup_process_info *info_ptr; struct cgroup_process_info *info_ptr;
int r; int r;
@ -1016,7 +1079,7 @@ int lxc_cgroup_create_legacy(struct cgroup_process_info *base_info, const char *
} }
/* get the cgroup membership of a given container */ /* get the cgroup membership of a given container */
struct cgroup_process_info *lxc_cgroup_get_container_info(const char *name, const char *lxcpath, struct cgroup_meta_data *meta_data) static struct cgroup_process_info *lxc_cgroup_get_container_info(const char *name, const char *lxcpath, struct cgroup_meta_data *meta_data)
{ {
struct cgroup_process_info *result = NULL; struct cgroup_process_info *result = NULL;
int saved_errno = 0; int saved_errno = 0;
@ -1064,7 +1127,7 @@ out_error:
} }
/* move a processs to the cgroups specified by the membership */ /* move a processs to the cgroups specified by the membership */
int lxc_cgroupfs_enter(struct cgroup_process_info *info, pid_t pid, bool enter_sub) static int lxc_cgroupfs_enter(struct cgroup_process_info *info, pid_t pid, bool enter_sub)
{ {
char pid_buf[32]; char pid_buf[32];
char *cgroup_tasks_fn; char *cgroup_tasks_fn;
@ -1148,9 +1211,8 @@ void lxc_cgroup_process_info_free_and_remove(struct cgroup_process_info *info)
lxc_cgroup_process_info_free_and_remove(next); lxc_cgroup_process_info_free_and_remove(next);
} }
static char *lxc_cgroup_get_hierarchy_path_handler(const char *subsystem, struct lxc_handler *handler) static char *lxc_cgroup_get_hierarchy_path_data(const char *subsystem, struct cgfs_data *d)
{ {
struct cgfs_data *d = handler->cgroup_info->data;
struct cgroup_process_info *info = d->info; struct cgroup_process_info *info = d->info;
info = find_info_for_subsystem(info, subsystem); info = find_info_for_subsystem(info, subsystem);
if (!info) if (!info)
@ -1158,14 +1220,8 @@ static char *lxc_cgroup_get_hierarchy_path_handler(const char *subsystem, struct
return info->cgroup_path; return info->cgroup_path;
} }
char *lxc_cgroup_get_hierarchy_path(const char *subsystem, const char *name, const char *lxcpath) static char *lxc_cgroup_get_hierarchy_abs_path_data(const char *subsystem, struct cgfs_data *d)
{ {
return lxc_cmd_get_cgroup_path(name, lxcpath, subsystem);
}
char *lxc_cgroup_get_hierarchy_abs_path_handler(const char *subsystem, struct lxc_handler *handler)
{
struct cgfs_data *d = handler->cgroup_info->data;
struct cgroup_process_info *info = d->info; struct cgroup_process_info *info = d->info;
struct cgroup_mount_point *mp = NULL; struct cgroup_mount_point *mp = NULL;
@ -1182,7 +1238,7 @@ char *lxc_cgroup_get_hierarchy_abs_path_handler(const char *subsystem, struct lx
return cgroup_to_absolute_path(mp, info->cgroup_path, NULL); return cgroup_to_absolute_path(mp, info->cgroup_path, NULL);
} }
char *lxc_cgroup_get_hierarchy_abs_path(const char *subsystem, const char *name, const char *lxcpath) static char *lxc_cgroup_get_hierarchy_abs_path(const char *subsystem, const char *name, const char *lxcpath)
{ {
struct cgroup_meta_data *meta; struct cgroup_meta_data *meta;
struct cgroup_process_info *base_info, *info; struct cgroup_process_info *base_info, *info;
@ -1214,7 +1270,7 @@ out1:
return result; return result;
} }
int lxc_cgroup_set_handler(const char *filename, const char *value, struct lxc_handler *handler) static int lxc_cgroup_set_data(const char *filename, const char *value, struct cgfs_data *d)
{ {
char *subsystem = NULL, *p, *path; char *subsystem = NULL, *p, *path;
int ret = -1; int ret = -1;
@ -1224,7 +1280,7 @@ int lxc_cgroup_set_handler(const char *filename, const char *value, struct lxc_h
if ((p = index(subsystem, '.')) != NULL) if ((p = index(subsystem, '.')) != NULL)
*p = '\0'; *p = '\0';
path = lxc_cgroup_get_hierarchy_abs_path_handler(subsystem, handler); path = lxc_cgroup_get_hierarchy_abs_path_data(subsystem, d);
if (path) { if (path) {
ret = do_cgroup_set(path, filename, value); ret = do_cgroup_set(path, filename, value);
free(path); free(path);
@ -1232,25 +1288,7 @@ int lxc_cgroup_set_handler(const char *filename, const char *value, struct lxc_h
return ret; return ret;
} }
int lxc_cgroup_get_handler(const char *filename, char *value, size_t len, struct lxc_handler *handler) static int lxc_cgroupfs_set(const char *filename, const char *value, const char *name, const char *lxcpath)
{
char *subsystem = NULL, *p, *path;
int ret = -1;
subsystem = alloca(strlen(filename) + 1);
strcpy(subsystem, filename);
if ((p = index(subsystem, '.')) != NULL)
*p = '\0';
path = lxc_cgroup_get_hierarchy_abs_path_handler(subsystem, handler);
if (path) {
ret = do_cgroup_get(path, filename, value, len);
free(path);
}
return ret;
}
int lxc_cgroupfs_set(const char *filename, const char *value, const char *name, const char *lxcpath)
{ {
char *subsystem = NULL, *p, *path; char *subsystem = NULL, *p, *path;
int ret = -1; int ret = -1;
@ -1268,7 +1306,7 @@ int lxc_cgroupfs_set(const char *filename, const char *value, const char *name,
return ret; return ret;
} }
int lxc_cgroupfs_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) static int lxc_cgroupfs_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
{ {
char *subsystem = NULL, *p, *path; char *subsystem = NULL, *p, *path;
int ret = -1; int ret = -1;
@ -1286,48 +1324,7 @@ int lxc_cgroupfs_get(const char *filename, char *value, size_t len, const char *
return ret; return ret;
} }
/* static bool cgroupfs_mount_cgroup(void *hdata, const char *root, int type)
* lxc_cgroup_path_get: Get the absolute pathname for a cgroup
* file for a running container.
*
* @filename : the file of interest (e.g. "freezer.state") or
* the subsystem name (e.g. "freezer") in which case
* the directory where the cgroup may be modified
* will be returned
* @name : name of container to connect to
* @lxcpath : the lxcpath in which the container is running
*
* This is the exported function, which determines cgpath from the
* lxc-start of the @name container running in @lxcpath.
*
* Returns path on success, NULL on error. The caller must free()
* the returned path.
*/
char *lxc_cgroup_path_get(const char *filename, const char *name,
const char *lxcpath)
{
char *subsystem = NULL, *longer_file = NULL, *p, *group, *path;
subsystem = alloca(strlen(filename) + 1);
strcpy(subsystem, filename);
if ((p = index(subsystem, '.')) != NULL) {
*p = '\0';
longer_file = alloca(strlen(filename) + 2);
longer_file[0] = '/';
strcpy(longer_file + 1, filename);
}
group = lxc_cgroup_get_hierarchy_path(subsystem, name, lxcpath);
if (!group)
return NULL;
path = lxc_cgroup_find_abs_path(subsystem, group, true, p ? longer_file : NULL);
free(group);
return path;
}
static bool cgroupfs_mount_cgroup(const char *root,
struct lxc_cgroup_info *cgroup_info, int type)
{ {
size_t bufsz = strlen(root) + sizeof("/sys/fs/cgroup"); size_t bufsz = strlen(root) + sizeof("/sys/fs/cgroup");
char *path = NULL; char *path = NULL;
@ -1339,9 +1336,9 @@ static bool cgroupfs_mount_cgroup(const char *root,
struct cgroup_process_info *info, *base_info; struct cgroup_process_info *info, *base_info;
int r, saved_errno = 0; int r, saved_errno = 0;
init_cg_ops(); cgfs_d = hdata;
if (!cgfs_d)
cgfs_d = cgroup_info->data; return false;
base_info = cgfs_d->info; base_info = cgfs_d->info;
if (type < LXC_AUTO_CGROUP_RO || type > LXC_AUTO_CGROUP_FULL_MIXED) { if (type < LXC_AUTO_CGROUP_RO || type > LXC_AUTO_CGROUP_FULL_MIXED) {
@ -1510,14 +1507,20 @@ out_error:
return false; return false;
} }
int lxc_cgroup_nrtasks_handler(struct lxc_handler *handler) static int cgfs_nrtasks(void *hdata)
{ {
struct cgfs_data *d = handler->cgroup_info->data; struct cgfs_data *d = hdata;
struct cgroup_process_info *info = d->info; struct cgroup_process_info *info;
struct cgroup_mount_point *mp = NULL; struct cgroup_mount_point *mp = NULL;
char *abs_path = NULL; char *abs_path = NULL;
int ret; int ret;
if (!d) {
errno = ENOENT;
return -1;
}
info = d->info;
if (!info) { if (!info) {
errno = ENOENT; errno = ENOENT;
return -1; return -1;
@ -1842,7 +1845,7 @@ static int do_cgroup_set(const char *cgroup_path, const char *sub_filename,
return ret; return ret;
} }
static int do_setup_cgroup_limits(struct lxc_handler *h, static int do_setup_cgroup_limits(struct cgfs_data *d,
struct lxc_list *cgroup_settings, bool do_devices) struct lxc_list *cgroup_settings, bool do_devices)
{ {
struct lxc_list *iterator; struct lxc_list *iterator;
@ -1857,14 +1860,14 @@ static int do_setup_cgroup_limits(struct lxc_handler *h,
if (do_devices == !strncmp("devices", cg->subsystem, 7)) { if (do_devices == !strncmp("devices", cg->subsystem, 7)) {
if (strcmp(cg->subsystem, "devices.deny") == 0 && if (strcmp(cg->subsystem, "devices.deny") == 0 &&
cgroup_devices_has_allow_or_deny(h, cg->value, false)) cgroup_devices_has_allow_or_deny(d, cg->value, false))
continue; continue;
if (strcmp(cg->subsystem, "devices.allow") == 0 && if (strcmp(cg->subsystem, "devices.allow") == 0 &&
cgroup_devices_has_allow_or_deny(h, cg->value, true)) cgroup_devices_has_allow_or_deny(d, cg->value, true))
continue; continue;
if (lxc_cgroup_set_handler(cg->subsystem, cg->value, h)) { if (lxc_cgroup_set_data(cg->subsystem, cg->value, d)) {
ERROR("Error setting %s to %s for %s\n", ERROR("Error setting %s to %s for %s\n",
cg->subsystem, cg->value, h->name); cg->subsystem, cg->value, d->name);
goto out; goto out;
} }
} }
@ -1878,7 +1881,7 @@ out:
return ret; return ret;
} }
static bool cgroup_devices_has_allow_or_deny(struct lxc_handler *h, static bool cgroup_devices_has_allow_or_deny(struct cgfs_data *d,
char *v, bool for_allow) char *v, bool for_allow)
{ {
char *path; char *path;
@ -1898,7 +1901,7 @@ static bool cgroup_devices_has_allow_or_deny(struct lxc_handler *h,
if (!for_allow && strcmp(v, "a") != 0 && strcmp(v, "a *:* rwm") != 0) if (!for_allow && strcmp(v, "a") != 0 && strcmp(v, "a *:* rwm") != 0)
return false; return false;
parts[0] = (const char *)lxc_cgroup_get_hierarchy_abs_path_handler("devices", h); parts[0] = (const char *)lxc_cgroup_get_hierarchy_abs_path_data("devices", d);
if (!parts[0]) if (!parts[0])
return false; return false;
path = lxc_string_join("/", parts, false); path = lxc_string_join("/", parts, false);
@ -2167,124 +2170,127 @@ static bool init_cpuset_if_needed(struct cgroup_mount_point *mp,
do_init_cpuset_file(mp, path, "/cpuset.mems") ); do_init_cpuset_file(mp, path, "/cpuset.mems") );
} }
extern void lxc_monitor_send_state(const char *name, lxc_state_t state, struct cgroup_ops *cgfs_ops_init(void)
const char *lxcpath);
int do_unfreeze(int freeze, const char *name, const char *lxcpath)
{ {
char v[100]; return &cgfs_ops;
const char *state = freeze ? "FROZEN" : "THAWED"; }
if (lxc_cgroup_set("freezer.state", state, name, lxcpath) < 0) { static void *cgfs_init(const char *name)
ERROR("Failed to freeze %s:%s", lxcpath, name); {
return -1; struct cgfs_data *d;
}
while (1) { d = malloc(sizeof(*d));
if (lxc_cgroup_get("freezer.state", v, 100, name, lxcpath) < 0) { if (!d)
ERROR("Failed to get new freezer state for %s:%s", lxcpath, name); return NULL;
return -1;
} memset(d, 0, sizeof(*d));
if (v[strlen(v)-1] == '\n') d->name = strdup(name);
v[strlen(v)-1] = '\0'; if (!d->name)
if (strncmp(v, state, strlen(state)) == 0) { goto err1;
if (name)
lxc_monitor_send_state(name, freeze ? FROZEN : THAWED, lxcpath); /* if we are running as root, use system cgroup pattern, otherwise
return 0; * just create a cgroup under the current one. But also fall back to
} * that if for some reason reading the configuration fails and no
sleep(1); * default value is available
*/
if (geteuid() == 0)
d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
if (!d->cgroup_pattern)
d->cgroup_pattern = "%n";
d->meta = lxc_cgroup_load_meta();
if (!d->meta) {
ERROR("cgroupfs failed to detect cgroup metadata");
goto err2;
} }
return d;
err2:
free(d->name);
err1:
free(d);
return NULL;
} }
int freeze_unfreeze(const char *name, int freeze, const char *lxcpath) static void cgfs_destroy(void *hdata)
{ {
return do_unfreeze(freeze, name, lxcpath); struct cgfs_data *d = hdata;
}
lxc_state_t freezer_state(const char *name, const char *lxcpath)
{
char v[100];
if (lxc_cgroup_get("freezer.state", v, 100, name, lxcpath) < 0)
return -1;
if (v[strlen(v)-1] == '\n')
v[strlen(v)-1] = '\0';
return lxc_str2state(v);
}
static void cgfs_destroy(struct lxc_handler *handler)
{
struct cgfs_data *d = handler->cgroup_info->data;
if (!d) if (!d)
return; return;
if (d->name)
free(d->name);
if (d->info) if (d->info)
lxc_cgroup_process_info_free_and_remove(d->info); lxc_cgroup_process_info_free_and_remove(d->info);
if (d->meta) if (d->meta)
lxc_cgroup_put_meta(d->meta); lxc_cgroup_put_meta(d->meta);
free(d); free(d);
handler->cgroup_info->data = NULL;
} }
static inline bool cgfs_init(struct lxc_handler *handler) static inline bool cgfs_create(void *hdata)
{ {
struct cgfs_data *d = malloc(sizeof(*d)); struct cgfs_data *d = hdata;
struct cgroup_process_info *i;
struct cgroup_meta_data *md;
if (!d) if (!d)
return false; return false;
d->info = NULL; md = d->meta;
d->meta = lxc_cgroup_load_meta(); i = lxc_cgroupfs_create(d->name, d->cgroup_pattern, md, NULL);
if (!d->meta) {
ERROR("cgroupfs failed to detect cgroup metadata");
free(d);
return false;
}
handler->cgroup_info->data = d;
return true;
}
static inline bool cgfs_create(struct lxc_handler *handler)
{
struct cgfs_data *d = handler->cgroup_info->data;
struct cgroup_process_info *i;
struct cgroup_meta_data *md = d->meta;
i = lxc_cgroupfs_create(handler->name, handler->cgroup_info->cgroup_pattern, md, NULL);
if (!i) if (!i)
return false; return false;
d->info = i; d->info = i;
return true; return true;
} }
static inline bool cgfs_enter(struct lxc_handler *handler) static inline bool cgfs_enter(void *hdata, pid_t pid)
{ {
struct cgfs_data *d = handler->cgroup_info->data; struct cgfs_data *d = hdata;
struct cgroup_process_info *i = d->info; struct cgroup_process_info *i;
int ret; int ret;
ret = lxc_cgroupfs_enter(i, handler->pid, false); if (!d)
return false;
i = d->info;
ret = lxc_cgroupfs_enter(i, pid, false);
return ret == 0; return ret == 0;
} }
static inline bool cgfs_create_legacy(struct lxc_handler *handler) static inline bool cgfs_create_legacy(void *hdata, pid_t pid)
{ {
struct cgfs_data *d = handler->cgroup_info->data; struct cgfs_data *d = hdata;
struct cgroup_process_info *i = d->info; struct cgroup_process_info *i;
if (lxc_cgroup_create_legacy(i, handler->name, handler->pid) < 0) {
ERROR("failed to create legacy ns cgroups for '%s'", handler->name); if (!d)
return false;
i = d->info;
if (lxc_cgroup_create_legacy(i, d->name, pid) < 0) {
ERROR("failed to create legacy ns cgroups for '%s'", d->name);
return false; return false;
} }
return true; return true;
} }
static char *cgfs_get_cgroup(struct lxc_handler *handler, const char *subsystem) static const char *cgfs_get_cgroup(void *hdata, const char *subsystem)
{ {
return lxc_cgroup_get_hierarchy_path_handler(subsystem, handler); struct cgfs_data *d = hdata;
if (!d)
return NULL;
return lxc_cgroup_get_hierarchy_path_data(subsystem, d);
} }
static bool cgfs_unfreeze_fromhandler(struct lxc_handler *handler) static bool cgfs_unfreeze(void *hdata)
{ {
struct cgfs_data *d = hdata;
char *cgabspath, *cgrelpath; char *cgabspath, *cgrelpath;
int ret; int ret;
cgrelpath = lxc_cgroup_get_hierarchy_path_handler("freezer", handler); if (!d)
return false;
cgrelpath = lxc_cgroup_get_hierarchy_path_data("freezer", d);
cgabspath = lxc_cgroup_find_abs_path("freezer", cgrelpath, true, NULL); cgabspath = lxc_cgroup_find_abs_path("freezer", cgrelpath, true, NULL);
if (!cgabspath) if (!cgabspath)
return false; return false;
@ -2294,12 +2300,17 @@ static bool cgfs_unfreeze_fromhandler(struct lxc_handler *handler)
return ret == 0; return ret == 0;
} }
bool cgroupfs_setup_limits(struct lxc_handler *h, bool with_devices) static bool cgroupfs_setup_limits(void *hdata, struct lxc_list *cgroup_conf,
bool with_devices)
{ {
return do_setup_cgroup_limits(h, &h->conf->cgroup, with_devices) == 0; struct cgfs_data *d = hdata;
if (!d)
return false;
return do_setup_cgroup_limits(d, cgroup_conf, with_devices) == 0;
} }
bool lxc_cgroupfs_attach(const char *name, const char *lxcpath, pid_t pid) static bool lxc_cgroupfs_attach(const char *name, const char *lxcpath, pid_t pid)
{ {
struct cgroup_meta_data *meta_data; struct cgroup_meta_data *meta_data;
struct cgroup_process_info *container_info; struct cgroup_process_info *container_info;
@ -2328,134 +2339,19 @@ bool lxc_cgroupfs_attach(const char *name, const char *lxcpath, pid_t pid)
} }
static struct cgroup_ops cgfs_ops = { static struct cgroup_ops cgfs_ops = {
.destroy = cgfs_destroy,
.init = cgfs_init, .init = cgfs_init,
.destroy = cgfs_destroy,
.create = cgfs_create, .create = cgfs_create,
.enter = cgfs_enter, .enter = cgfs_enter,
.create_legacy = cgfs_create_legacy, .create_legacy = cgfs_create_legacy,
.get_cgroup = cgfs_get_cgroup, .get_cgroup = cgfs_get_cgroup,
.get = lxc_cgroupfs_get, .get = lxc_cgroupfs_get,
.set = lxc_cgroupfs_set, .set = lxc_cgroupfs_set,
.unfreeze_fromhandler = cgfs_unfreeze_fromhandler, .unfreeze = cgfs_unfreeze,
.setup_limits = cgroupfs_setup_limits, .setup_limits = cgroupfs_setup_limits,
.name = "cgroupfs", .name = "cgroupfs",
.attach = lxc_cgroupfs_attach, .attach = lxc_cgroupfs_attach,
.chown = NULL, .chown = NULL,
.mount_cgroup = cgroupfs_mount_cgroup, .mount_cgroup = cgroupfs_mount_cgroup,
.nrtasks = cgfs_nrtasks,
}; };
static void init_cg_ops(void)
{
if (!use_cgmanager)
return;
if (cgmanager_initialized)
return;
if (!lxc_init_cgmanager()) {
ERROR("Could not contact cgroup manager, falling back to cgroupfs");
active_cg_ops = &cgfs_ops;
}
}
/*
* These are the backend-independent cgroup handlers for container
* start and stop
*/
/* Free all cgroup info held by the handler */
void cgroup_destroy(struct lxc_handler *handler)
{
if (!handler->cgroup_info)
return;
if (active_cg_ops)
active_cg_ops->destroy(handler);
}
/*
* Allocate a lxc_cgroup_info for the active cgroup
* backend, and assign it to the handler
*/
bool cgroup_init(struct lxc_handler *handler)
{
init_cg_ops();
handler->cgroup_info = malloc(sizeof(struct lxc_cgroup_info));
if (!handler->cgroup_info)
return false;
memset(handler->cgroup_info, 0, sizeof(struct lxc_cgroup_info));
/* if we are running as root, use system cgroup pattern, otherwise
* just create a cgroup under the current one. But also fall back to
* that if for some reason reading the configuration fails and no
* default value is available
*/
if (geteuid() == 0)
handler->cgroup_info->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
if (!handler->cgroup_info->cgroup_pattern)
handler->cgroup_info->cgroup_pattern = "%n";
return active_cg_ops->init(handler);
}
/* Create the container cgroups for all requested controllers */
bool cgroup_create(struct lxc_handler *handler)
{
return active_cg_ops->create(handler);
}
/*
* Enter the container init into its new cgroups for all
* requested controllers */
bool cgroup_enter(struct lxc_handler *handler)
{
return active_cg_ops->enter(handler);
}
bool cgroup_create_legacy(struct lxc_handler *handler)
{
if (active_cg_ops->create_legacy)
return active_cg_ops->create_legacy(handler);
return true;
}
char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem)
{
return active_cg_ops->get_cgroup(handler, subsystem);
}
int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath)
{
init_cg_ops();
return active_cg_ops->set(filename, value, name, lxcpath);
}
int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
{
init_cg_ops();
return active_cg_ops->get(filename, value, len, name, lxcpath);
}
bool lxc_unfreeze_fromhandler(struct lxc_handler *handler)
{
return active_cg_ops->unfreeze_fromhandler(handler);
}
bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices)
{
return active_cg_ops->setup_limits(handler, with_devices);
}
bool cgroup_chown(struct lxc_handler *handler)
{
if (active_cg_ops->chown)
return active_cg_ops->chown(handler);
return true;
}
bool lxc_cgroup_attach(const char *name, const char *lxcpath, pid_t pid)
{
init_cg_ops();
return active_cg_ops->attach(name, lxcpath, pid);
}
bool lxc_setup_mount_cgroup(const char *root,
struct lxc_cgroup_info *cgroup_info, int type)
{
return active_cg_ops->mount_cgroup(root, cgroup_info, type);
}

View File

@ -58,24 +58,74 @@ lxc_log_define(lxc_cgmanager, lxc);
#include <nih/alloc.h> #include <nih/alloc.h>
#include <nih/error.h> #include <nih/error.h>
#include <nih/string.h> #include <nih/string.h>
NihDBusProxy *cgroup_manager = NULL;
extern struct cgroup_ops *active_cg_ops; struct cgm_data {
bool cgmanager_initialized = false; char *name;
bool use_cgmanager = true; char *cgroup_path;
const char *cgroup_pattern;
};
static NihDBusProxy *cgroup_manager = NULL;
static struct cgroup_ops cgmanager_ops; static struct cgroup_ops cgmanager_ops;
static int nr_subsystems; static int nr_subsystems;
static char **subsystems; static char **subsystems;
bool lxc_init_cgmanager(void); #define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock"
static void cgmanager_disconnected(DBusConnection *connection) static void cgm_dbus_disconnected(DBusConnection *connection);
static bool cgm_dbus_connect(void)
{
DBusError dbus_error;
DBusConnection *connection;
dbus_error_init(&dbus_error);
connection = nih_dbus_connect(CGMANAGER_DBUS_SOCK, cgm_dbus_disconnected);
if (!connection) {
NihError *nerr;
nerr = nih_error_get();
DEBUG("Unable to open cgmanager connection at %s: %s", CGMANAGER_DBUS_SOCK,
nerr->message);
nih_free(nerr);
dbus_error_free(&dbus_error);
return false;
}
dbus_connection_set_exit_on_disconnect(connection, FALSE);
dbus_error_free(&dbus_error);
cgroup_manager = nih_dbus_proxy_new(NULL, connection,
NULL /* p2p */,
"/org/linuxcontainers/cgmanager", NULL, NULL);
dbus_connection_unref(connection);
if (!cgroup_manager) {
NihError *nerr;
nerr = nih_error_get();
ERROR("Error opening cgmanager proxy: %s", nerr->message);
nih_free(nerr);
return false;
}
// force fd passing negotiation
if (cgmanager_ping_sync(NULL, cgroup_manager, 0) != 0) {
NihError *nerr;
nerr = nih_error_get();
ERROR("Error pinging cgroup manager: %s", nerr->message);
nih_free(nerr);
}
return true;
}
static void cgm_dbus_disconnect(void)
{
nih_free(cgroup_manager);
cgroup_manager = NULL;
}
static void cgm_dbus_disconnected(DBusConnection *connection)
{ {
WARN("Cgroup manager connection was terminated"); WARN("Cgroup manager connection was terminated");
cgroup_manager = NULL; cgroup_manager = NULL;
cgmanager_initialized = false; if (cgm_dbus_connect()) {
if (lxc_init_cgmanager()) {
cgmanager_initialized = true;
INFO("New cgroup manager connection was opened"); INFO("New cgroup manager connection was opened");
} else {
WARN("Cgroup manager unable to re-open connection");
} }
} }
@ -115,47 +165,6 @@ static int send_creds(int sock, int rpid, int ruid, int rgid)
return 0; return 0;
} }
#define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock"
bool lxc_init_cgmanager(void)
{
DBusError dbus_error;
DBusConnection *connection;
dbus_error_init(&dbus_error);
connection = nih_dbus_connect(CGMANAGER_DBUS_SOCK, cgmanager_disconnected);
if (!connection) {
NihError *nerr;
nerr = nih_error_get();
ERROR("Error opening cgmanager connection at %s: %s", CGMANAGER_DBUS_SOCK,
nerr->message);
nih_free(nerr);
dbus_error_free(&dbus_error);
return false;
}
dbus_connection_set_exit_on_disconnect(connection, FALSE);
dbus_error_free(&dbus_error);
cgroup_manager = nih_dbus_proxy_new(NULL, connection,
NULL /* p2p */,
"/org/linuxcontainers/cgmanager", NULL, NULL);
dbus_connection_unref(connection);
if (!cgroup_manager) {
NihError *nerr;
nerr = nih_error_get();
ERROR("Error opening cgmanager proxy: %s", nerr->message);
nih_free(nerr);
return false;
}
active_cg_ops = &cgmanager_ops;
// force fd passing negotiation
if (cgmanager_ping_sync(NULL, cgroup_manager, 0) != 0) {
NihError *nerr;
nerr = nih_error_get();
ERROR("Error pinging cgroup manager: %s", nerr->message);
nih_free(nerr);
}
return true;
}
static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path, int32_t *existed) static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path, int32_t *existed)
{ {
if ( cgmanager_create_sync(NULL, cgroup_manager, controller, if ( cgmanager_create_sync(NULL, cgroup_manager, controller,
@ -341,19 +350,49 @@ static void cgm_remove_cgroup(const char *controller, const char *path)
INFO("cgroup removal attempt: %s:%s did not exist", controller, path); INFO("cgroup removal attempt: %s:%s did not exist", controller, path);
} }
static void cgm_destroy(struct lxc_handler *handler) static void *cgm_init(const char *name)
{ {
char *cgroup_path = handler->cgroup_info->data; struct cgm_data *d;
d = malloc(sizeof(*d));
if (!d)
return NULL;
memset(d, 0, sizeof(*d));
d->name = strdup(name);
if (!d->name)
goto err1;
/* if we are running as root, use system cgroup pattern, otherwise
* just create a cgroup under the current one. But also fall back to
* that if for some reason reading the configuration fails and no
* default value is available
*/
if (geteuid() == 0)
d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
if (!d->cgroup_pattern)
d->cgroup_pattern = "%n";
return d;
err1:
free(d);
return NULL;
}
static void cgm_destroy(void *hdata)
{
struct cgm_data *d = hdata;
int i; int i;
if (!cgroup_path) if (!d)
return; return;
for (i = 0; i < nr_subsystems; i++) for (i = 0; i < nr_subsystems; i++)
cgm_remove_cgroup(subsystems[i], cgroup_path); cgm_remove_cgroup(subsystems[i], d->cgroup_path);
free(cgroup_path); free(d->name);
handler->cgroup_info->data = NULL; if (d->cgroup_path)
free(d->cgroup_path);
free(d);
} }
/* /*
@ -366,19 +405,21 @@ static inline void cleanup_cgroups(char *path)
cgm_remove_cgroup(subsystems[i], path); cgm_remove_cgroup(subsystems[i], path);
} }
static inline bool cgm_create(struct lxc_handler *handler) static inline bool cgm_create(void *hdata)
{ {
struct cgm_data *d = hdata;
int i, index=0, baselen, ret; int i, index=0, baselen, ret;
int32_t existed; int32_t existed;
char result[MAXPATHLEN], *tmp; char result[MAXPATHLEN], *tmp, *cgroup_path;
char *cgroup_path = handler->cgroup_info->data;
if (!d)
return false;
// XXX we should send a hint to the cgmanager that when these // XXX we should send a hint to the cgmanager that when these
// cgroups become empty they should be deleted. Requires a cgmanager // cgroups become empty they should be deleted. Requires a cgmanager
// extension // extension
memset(result, 0, MAXPATHLEN); memset(result, 0, MAXPATHLEN);
tmp = lxc_string_replace("%n", handler->name, handler->cgroup_info->cgroup_pattern); tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern);
if (!tmp) if (!tmp)
return false; return false;
if (strlen(tmp) > MAXPATHLEN) if (strlen(tmp) > MAXPATHLEN)
@ -415,7 +456,7 @@ again:
cleanup_cgroups(tmp); cleanup_cgroups(tmp);
return false; return false;
} }
handler->cgroup_info->data = cgroup_path; d->cgroup_path = cgroup_path;
return true; return true;
next: next:
cleanup_cgroups(tmp); cleanup_cgroups(tmp);
@ -454,19 +495,25 @@ static bool do_cgm_enter(pid_t pid, const char *cgroup_path)
return true; return true;
} }
static inline bool cgm_enter(struct lxc_handler *handler) static inline bool cgm_enter(void *hdata, pid_t pid)
{ {
char *cgroup_path = handler->cgroup_info->data; struct cgm_data *d = hdata;
return do_cgm_enter(handler->pid, cgroup_path);
if (!d || !d->cgroup_path)
return false;
return do_cgm_enter(pid, d->cgroup_path);
} }
static char *cgm_get_cgroup(struct lxc_handler *handler, const char *subsystem) static const char *cgm_get_cgroup(void *hdata, const char *subsystem)
{ {
char *cgroup_path = handler->cgroup_info->data; struct cgm_data *d = hdata;
return cgroup_path;
if (!d || !d->cgroup_path)
return NULL;
return d->cgroup_path;
} }
int cgm_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) static int cgm_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
{ {
char *result, *controller, *key, *cgroup; char *result, *controller, *key, *cgroup;
size_t newlen; size_t newlen;
@ -531,7 +578,7 @@ static int cgm_do_set(const char *controller, const char *file,
return ret; return ret;
} }
int cgm_set(const char *filename, const char *value, const char *name, const char *lxcpath) static int cgm_set(const char *filename, const char *value, const char *name, const char *lxcpath)
{ {
char *controller, *key, *cgroup; char *controller, *key, *cgroup;
int ret; int ret;
@ -555,10 +602,21 @@ int cgm_set(const char *filename, const char *value, const char *name, const cha
return ret; return ret;
} }
static void free_subsystems(void)
{
int i;
for (i = 0; i < nr_subsystems; i++)
free(subsystems[i]);
free(subsystems);
subsystems = NULL;
nr_subsystems = 0;
}
static bool collect_subsytems(void) static bool collect_subsytems(void)
{ {
char *line = NULL, *tab1; char *line = NULL, *tab1;
size_t sz = 0, i; size_t sz = 0;
FILE *f; FILE *f;
if (subsystems) // already initialized if (subsystems) // already initialized
@ -598,51 +656,62 @@ static bool collect_subsytems(void)
out_free: out_free:
fclose(f); fclose(f);
for (i = 0; i < nr_subsystems; i++) free_subsystems();
free(subsystems[i]);
free(subsystems);
subsystems = NULL;
nr_subsystems = 0;
return false; return false;
} }
static inline bool cgm_init(struct lxc_handler *handler) struct cgroup_ops *cgm_ops_init(void)
{ {
if (!collect_subsytems()) if (!collect_subsytems())
return false; return NULL;
if (geteuid()) if (!cgm_dbus_connect())
return true; goto err1;
// root; try to escape to root cgroup // root; try to escape to root cgroup
return lxc_cgmanager_escape(); if (geteuid() == 0 && !lxc_cgmanager_escape())
goto err2;
return &cgmanager_ops;
err2:
cgm_dbus_disconnect();
err1:
free_subsystems();
return NULL;
} }
static bool cgm_unfreeze_fromhandler(struct lxc_handler *handler) static bool cgm_unfreeze(void *hdata)
{ {
char *cgroup_path = handler->cgroup_info->data; struct cgm_data *d = hdata;
if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", cgroup_path, if (!d || !d->cgroup_path)
return false;
if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", d->cgroup_path,
"freezer.state", "THAWED") != 0) { "freezer.state", "THAWED") != 0) {
NihError *nerr; NihError *nerr;
nerr = nih_error_get(); nerr = nih_error_get();
ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message); ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
nih_free(nerr); nih_free(nerr);
ERROR("Error unfreezing %s", cgroup_path); ERROR("Error unfreezing %s", d->cgroup_path);
return false; return false;
} }
return true; return true;
} }
static bool setup_limits(struct lxc_handler *h, bool do_devices) static bool cgm_setup_limits(void *hdata, struct lxc_list *cgroup_settings, bool do_devices)
{ {
struct cgm_data *d = hdata;
struct lxc_list *iterator; struct lxc_list *iterator;
struct lxc_cgroup *cg; struct lxc_cgroup *cg;
bool ret = false; bool ret = false;
struct lxc_list *cgroup_settings = &h->conf->cgroup;
char *cgroup_path = h->cgroup_info->data;
if (lxc_list_empty(cgroup_settings)) if (lxc_list_empty(cgroup_settings))
return true; return true;
if (!d || !d->cgroup_path)
return false;
lxc_list_for_each(iterator, cgroup_settings) { lxc_list_for_each(iterator, cgroup_settings) {
char controller[100], *p; char controller[100], *p;
cg = iterator->elem; cg = iterator->elem;
@ -654,10 +723,10 @@ static bool setup_limits(struct lxc_handler *h, bool do_devices)
p = strchr(controller, '.'); p = strchr(controller, '.');
if (p) if (p)
*p = '\0'; *p = '\0';
if (cgm_do_set(controller, cg->subsystem, cgroup_path if (cgm_do_set(controller, cg->subsystem, d->cgroup_path
, cg->value) < 0) { , cg->value) < 0) {
ERROR("Error setting %s to %s for %s\n", ERROR("Error setting %s to %s for %s\n",
cg->subsystem, cg->value, h->name); cg->subsystem, cg->value, d->name);
goto out; goto out;
} }
@ -670,20 +739,17 @@ out:
return ret; return ret;
} }
static bool cgm_setup_limits(struct lxc_handler *handler, bool with_devices) static bool cgm_chown(void *hdata, struct lxc_conf *conf)
{ {
return setup_limits(handler, with_devices); struct cgm_data *d = hdata;
}
static bool cgm_chown(struct lxc_handler *handler)
{
char *cgroup_path = handler->cgroup_info->data;
int i; int i;
if (!d || !d->cgroup_path)
return false;
for (i = 0; i < nr_subsystems; i++) { for (i = 0; i < nr_subsystems; i++) {
if (!chown_cgroup(subsystems[i], cgroup_path, handler->conf)) if (!chown_cgroup(subsystems[i], d->cgroup_path, conf))
WARN("Failed to chown %s:%s to container root", WARN("Failed to chown %s:%s to container root",
subsystems[i], cgroup_path); subsystems[i], d->cgroup_path);
} }
return true; return true;
} }
@ -771,8 +837,7 @@ static bool cgm_bind_dir(const char *root, const char *dirname)
*/ */
#define CGMANAGER_LOWER_SOCK "/sys/fs/cgroup/cgmanager.lower" #define CGMANAGER_LOWER_SOCK "/sys/fs/cgroup/cgmanager.lower"
#define CGMANAGER_UPPER_SOCK "/sys/fs/cgroup/cgmanager" #define CGMANAGER_UPPER_SOCK "/sys/fs/cgroup/cgmanager"
static bool cgm_mount_cgroup(const char *root, static bool cgm_mount_cgroup(void *hdata, const char *root, int type)
struct lxc_cgroup_info *cgroup_info, int type)
{ {
if (dir_exists(CGMANAGER_LOWER_SOCK)) if (dir_exists(CGMANAGER_LOWER_SOCK))
return cgm_bind_dir(root, CGMANAGER_LOWER_SOCK); return cgm_bind_dir(root, CGMANAGER_LOWER_SOCK);
@ -783,19 +848,20 @@ static bool cgm_mount_cgroup(const char *root,
} }
static struct cgroup_ops cgmanager_ops = { static struct cgroup_ops cgmanager_ops = {
.destroy = cgm_destroy,
.init = cgm_init, .init = cgm_init,
.destroy = cgm_destroy,
.create = cgm_create, .create = cgm_create,
.enter = cgm_enter, .enter = cgm_enter,
.create_legacy = NULL, .create_legacy = NULL,
.get_cgroup = cgm_get_cgroup, .get_cgroup = cgm_get_cgroup,
.get = cgm_get, .get = cgm_get,
.set = cgm_set, .set = cgm_set,
.unfreeze_fromhandler = cgm_unfreeze_fromhandler, .unfreeze = cgm_unfreeze,
.setup_limits = cgm_setup_limits, .setup_limits = cgm_setup_limits,
.name = "cgmanager", .name = "cgmanager",
.chown = cgm_chown, .chown = cgm_chown,
.attach = cgm_attach, .attach = cgm_attach,
.mount_cgroup = cgm_mount_cgroup, .mount_cgroup = cgm_mount_cgroup,
.nrtasks = NULL,
}; };
#endif #endif

169
src/lxc/cgroup.c Normal file
View File

@ -0,0 +1,169 @@
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "cgroup.h"
#include "conf.h"
#include "log.h"
#include "start.h"
lxc_log_define(lxc_cgroup, lxc);
static struct cgroup_ops *ops = NULL;
extern struct cgroup_ops *cgfs_ops_init(void);
extern struct cgroup_ops *cgm_ops_init(void);
__attribute__((constructor))
void cgroup_ops_init(void)
{
if (ops) {
INFO("cgroup driver %s", ops->name);
return;
}
DEBUG("cgroup_init");
#if HAVE_CGMANAGER
ops = cgm_ops_init();
#endif
if (!ops)
ops = cgfs_ops_init();
if (ops)
INFO("Initialized cgroup driver %s", ops->name);
}
bool cgroup_init(struct lxc_handler *handler)
{
if (handler->cgroup_data) {
ERROR("cgroup_init called on already inited handler");
return true;
}
if (ops) {
INFO("cgroup driver %s initing for %s", ops->name, handler->name);
handler->cgroup_data = ops->init(handler->name);
}
return handler->cgroup_data != NULL;
}
void cgroup_destroy(struct lxc_handler *handler)
{
if (ops) {
ops->destroy(handler->cgroup_data);
handler->cgroup_data = NULL;
}
}
/* Create the container cgroups for all requested controllers */
bool cgroup_create(struct lxc_handler *handler)
{
if (ops)
return ops->create(handler->cgroup_data);
return false;
}
/*
* Enter the container init into its new cgroups for all
* requested controllers
*/
bool cgroup_enter(struct lxc_handler *handler)
{
if (ops)
return ops->enter(handler->cgroup_data, handler->pid);
return false;
}
bool cgroup_create_legacy(struct lxc_handler *handler)
{
if (ops && ops->create_legacy)
return ops->create_legacy(handler->cgroup_data, handler->pid);
return true;
}
const char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem)
{
if (ops)
return ops->get_cgroup(handler->cgroup_data, subsystem);
return NULL;
}
bool cgroup_unfreeze(struct lxc_handler *handler)
{
if (ops)
return ops->unfreeze(handler->cgroup_data);
return false;
}
bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices)
{
if (ops)
return ops->setup_limits(handler->cgroup_data,
&handler->conf->cgroup, with_devices);
return false;
}
bool cgroup_chown(struct lxc_handler *handler)
{
if (ops && ops->chown)
return ops->chown(handler->cgroup_data, handler->conf);
return true;
}
bool cgroup_mount(const char *root, struct lxc_handler *handler, int type)
{
if (ops) {
return ops->mount_cgroup(handler->cgroup_data, root, type);
}
return false;
}
int cgroup_nrtasks(struct lxc_handler *handler)
{
if (ops) {
if (ops->nrtasks)
return ops->nrtasks(handler->cgroup_data);
else
WARN("CGROUP driver %s doesn't implement nrtasks", ops->name);
}
return -1;
}
bool cgroup_attach(const char *name, const char *lxcpath, pid_t pid)
{
if (ops)
return ops->attach(name, lxcpath, pid);
return false;
}
int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath)
{
if (ops)
return ops->set(filename, value, name, lxcpath);
return -1;
}
int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
{
if (ops)
return ops->get(filename, value, len, name, lxcpath);
return -1;
}

View File

@ -20,195 +20,39 @@
* License along with this library; if not, write to the Free Software * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef _ncgroup_h
#define _ncgroup_h #ifndef __lxc_cgroup_h
#define __lxc_cgroup_h
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <sys/types.h>
#include "state.h"
struct cgroup_hierarchy;
struct cgroup_meta_data;
struct cgroup_mount_point;
/*
* cgroup_meta_data: the metadata about the cgroup infrastructure on this
* host
*/
struct cgroup_meta_data {
ptrdiff_t ref; /* simple refcount */
struct cgroup_hierarchy **hierarchies;
struct cgroup_mount_point **mount_points;
int maximum_hierarchy;
};
/*
* cgroup_hierarchy: describes a single cgroup hierarchy
* (may have multiple mount points)
*/
struct cgroup_hierarchy {
int index;
bool used; /* false if the hierarchy should be ignored by lxc */
char **subsystems;
struct cgroup_mount_point *rw_absolute_mount_point;
struct cgroup_mount_point *ro_absolute_mount_point;
struct cgroup_mount_point **all_mount_points;
size_t all_mount_point_capacity;
};
/*
* cgroup_mount_point: a mount point to where a hierarchy
* is mounted to
*/
struct cgroup_mount_point {
struct cgroup_hierarchy *hierarchy;
char *mount_point;
char *mount_prefix;
bool read_only;
bool need_cpuset_init;
};
/*
* cgroup_process_info: describes the membership of a
* process to the different cgroup
* hierarchies
*
* Note this is the per-process info tracked by the cgfs_ops.
* This is not used with cgmanager.
*/
struct cgroup_process_info {
struct cgroup_process_info *next;
struct cgroup_meta_data *meta_ref;
struct cgroup_hierarchy *hierarchy;
char *cgroup_path;
char *cgroup_path_sub;
char **created_paths;
size_t created_paths_capacity;
size_t created_paths_count;
struct cgroup_mount_point *designated_mount_point;
};
/* meta data management:
* lxc_cgroup_load_meta loads the meta data (using subsystem
* whitelist from main lxc configuration)
* lxc_cgroup_load_meta2 does the same, but allows one to specify
* a custom whitelist
* lxc_cgroup_get_meta increments the refcount of a meta data
* object
* lxc_cgroup_put_meta decrements the refcount of a meta data
* object, potentially destroying it
*/
extern struct cgroup_meta_data *lxc_cgroup_load_meta();
extern struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist);
extern struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data);
extern struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data);
/* find the hierarchy corresponding to a given subsystem */
extern struct cgroup_hierarchy *lxc_cgroup_find_hierarchy(struct cgroup_meta_data *meta_data, const char *subsystem);
/* find a mount point for a given hierarchy that has access to the cgroup in 'cgroup' and (if wanted) is writable */
extern struct cgroup_mount_point *lxc_cgroup_find_mount_point(struct cgroup_hierarchy *hierarchy, const char *group, bool should_be_writable);
/* all-in-one: find a mount point for a given hierarchy that has access to the cgroup and return the correct path within */
extern char *lxc_cgroup_find_abs_path(const char *subsystem, const char *group, bool should_be_writable, const char *suffix);
/* determine the cgroup membership of a given process */
extern struct cgroup_process_info *lxc_cgroup_process_info_get(pid_t pid, struct cgroup_meta_data *meta);
extern struct cgroup_process_info *lxc_cgroup_process_info_get_init(struct cgroup_meta_data *meta);
extern struct cgroup_process_info *lxc_cgroup_process_info_get_self(struct cgroup_meta_data *meta);
/* create a new cgroup */
extern struct cgroup_process_info *lxc_cgroup_create(const char *name, const char *path_pattern, struct cgroup_meta_data *meta_data, const char *sub_pattern);
extern int lxc_cgroup_create_legacy(struct cgroup_process_info *base_info, const char *name, pid_t pid);
/* get the cgroup membership of a given container */
extern struct cgroup_process_info *lxc_cgroup_get_container_info(const char *name, const char *lxcpath, struct cgroup_meta_data *meta_data);
/* move a processs to the cgroups specified by the membership TODO - deprecated, switch users to cgroup_enter() */
extern int lxc_cgroupfs_enter(struct cgroup_process_info *info, pid_t pid, bool enter_sub);
/* free process membership information */
extern void lxc_cgroup_process_info_free(struct cgroup_process_info *info);
extern void lxc_cgroup_process_info_free_and_remove(struct cgroup_process_info *info);
struct lxc_handler; struct lxc_handler;
extern char *lxc_cgroup_get_hierarchy_path(const char *subsystem, const char *name, const char *lxcpath); struct lxc_conf;
extern char *lxc_cgroup_get_hierarchy_abs_path_handler(const char *subsystem, struct lxc_handler *handler);
extern char *lxc_cgroup_get_hierarchy_abs_path(const char *subsystem, const char *name, const char *lxcpath);
extern int lxc_cgroup_set_handler(const char *filename, const char *value, struct lxc_handler *handler);
extern int lxc_cgroup_get_handler(const char *filename, char *value, size_t len, struct lxc_handler *handler);
/*
* lxc_cgroup_path_get: Get the absolute pathname for a cgroup
* file for a running container.
*
* @filename : the file of interest (e.g. "freezer.state") or
* the subsystem name (e.g. "freezer") in which case
* the directory where the cgroup may be modified
* will be returned
* @name : name of container to connect to
* @lxcpath : the lxcpath in which the container is running
*
* This is the exported function, which determines cgpath from the
* lxc-start of the @name container running in @lxcpath.
*
* Returns path on success, NULL on error. The caller must free()
* the returned path.
*/
extern char *lxc_cgroup_path_get(const char *filename, const char *name,
const char *lxcpath);
struct lxc_list; struct lxc_list;
extern int lxc_setup_cgroup_without_devices(struct lxc_handler *h, struct lxc_list *cgroup_settings);
extern int lxc_setup_cgroup_devices(struct lxc_handler *h, struct lxc_list *cgroup_settings);
extern int lxc_cgroup_nrtasks_handler(struct lxc_handler *handler);
extern int do_unfreeze(int freeze, const char *name, const char *lxcpath);
extern int freeze_unfreeze(const char *name, int freeze, const char *lxcpath);
extern const char *lxc_state2str(lxc_state_t state);
extern lxc_state_t freezer_state(const char *name, const char *lxcpath);
/*
* cgroup-related data for backend use in start/stop of a
* container. This is tacked to the lxc_handler.
*/
struct lxc_cgroup_info {
/* handlers to actually do the cgroup stuff */
struct cgroup_ops *ops;
/* extra data for the cgroup_ops, i.e. mountpoints for fs backend */
void *data;
const char *cgroup_pattern;
};
/* per-backend cgroup hooks */
struct cgroup_ops { struct cgroup_ops {
void (*destroy)(struct lxc_handler *handler); const char *name;
bool (*init)(struct lxc_handler *handler);
bool (*create)(struct lxc_handler *handler); void *(*init)(const char *name);
bool (*enter)(struct lxc_handler *handler); void (*destroy)(void *hdata);
bool (*create_legacy)(struct lxc_handler *handler); bool (*create)(void *hdata);
char *(*get_cgroup)(struct lxc_handler *handler, const char *subsystem); bool (*enter)(void *hdata, pid_t pid);
bool (*create_legacy)(void *hdata, pid_t pid);
const char *(*get_cgroup)(void *hdata, const char *subsystem);
int (*set)(const char *filename, const char *value, const char *name, const char *lxcpath); int (*set)(const char *filename, const char *value, const char *name, const char *lxcpath);
int (*get)(const char *filename, char *value, size_t len, const char *name, const char *lxcpath); int (*get)(const char *filename, char *value, size_t len, const char *name, const char *lxcpath);
bool (*unfreeze_fromhandler)(struct lxc_handler *handler); bool (*unfreeze)(void *hdata);
bool (*setup_limits)(struct lxc_handler *handler, bool with_devices); bool (*setup_limits)(void *hdata, struct lxc_list *cgroup_conf, bool with_devices);
bool (*chown)(struct lxc_handler *handler); bool (*chown)(void *hdata, struct lxc_conf *conf);
bool (*attach)(const char *name, const char *lxcpath, pid_t pid); bool (*attach)(const char *name, const char *lxcpath, pid_t pid);
bool (*mount_cgroup)(const char *root, struct lxc_cgroup_info *info, bool (*mount_cgroup)(void *hdata, const char *root, int type);
int type); int (*nrtasks)(void *hdata);
const char *name;
}; };
struct cgfs_data { extern bool cgroup_attach(const char *name, const char *lxcpath, pid_t pid);
struct cgroup_meta_data *meta; extern bool cgroup_mount(const char *root, struct lxc_handler *handler, int type);
struct cgroup_process_info *info;
};
/*
* backend-independent cgroup handlers
*/
extern void cgroup_destroy(struct lxc_handler *handler); extern void cgroup_destroy(struct lxc_handler *handler);
extern bool cgroup_init(struct lxc_handler *handler); extern bool cgroup_init(struct lxc_handler *handler);
extern bool cgroup_create(struct lxc_handler *handler); extern bool cgroup_create(struct lxc_handler *handler);
@ -217,11 +61,8 @@ extern bool cgroup_chown(struct lxc_handler *handler);
extern bool cgroup_enter(struct lxc_handler *handler); extern bool cgroup_enter(struct lxc_handler *handler);
extern void cgroup_cleanup(struct lxc_handler *handler); extern void cgroup_cleanup(struct lxc_handler *handler);
extern bool cgroup_create_legacy(struct lxc_handler *handler); extern bool cgroup_create_legacy(struct lxc_handler *handler);
extern char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem); extern int cgroup_nrtasks(struct lxc_handler *handler);
extern bool lxc_cgroup_attach(const char *name, const char *lxcpath, pid_t pid); extern const char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem);
extern bool lxc_setup_mount_cgroup(const char *root, struct lxc_cgroup_info *cgroup_info, int type); extern bool cgroup_unfreeze(struct lxc_handler *handler);
extern bool lxc_unfreeze_fromhandler(struct lxc_handler *handler);
extern int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath);
extern int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath);
#endif #endif

View File

@ -430,7 +430,7 @@ static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req,
struct lxc_handler *handler) struct lxc_handler *handler)
{ {
struct lxc_cmd_rsp rsp; struct lxc_cmd_rsp rsp;
char *path; const char *path;
if (req->datalen < 1) if (req->datalen < 1)
return -1; return -1;
@ -439,7 +439,7 @@ static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req,
if (!path) if (!path)
return -1; return -1;
rsp.datalen = strlen(path) + 1, rsp.datalen = strlen(path) + 1,
rsp.data = path; rsp.data = (char *)path;
rsp.ret = 0; rsp.ret = 0;
return lxc_cmd_rsp_send(fd, &rsp); return lxc_cmd_rsp_send(fd, &rsp);
@ -590,7 +590,12 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req,
memset(&rsp, 0, sizeof(rsp)); memset(&rsp, 0, sizeof(rsp));
rsp.ret = kill(handler->pid, stopsignal); rsp.ret = kill(handler->pid, stopsignal);
if (!rsp.ret) { if (!rsp.ret) {
if (lxc_unfreeze_fromhandler(handler)) /* we can't just use lxc_unfreeze() since we are already in the
* context of handling the STOP cmd in lxc-start, and calling
* lxc_unfreeze() would do another cmd (GET_CGROUP) which would
* deadlock us
*/
if (cgroup_unfreeze(handler))
return 0; return 0;
ERROR("Failed to unfreeze %s:%s", handler->lxcpath, handler->name); ERROR("Failed to unfreeze %s:%s", handler->lxcpath, handler->name);
rsp.ret = -1; rsp.ret = -1;

View File

@ -63,7 +63,6 @@
#include "utils.h" #include "utils.h"
#include "conf.h" #include "conf.h"
#include "log.h" #include "log.h"
#include "lxc.h" /* for lxc_cgroup_set() */
#include "caps.h" /* for lxc_caps_last_cap() */ #include "caps.h" /* for lxc_caps_last_cap() */
#include "bdev.h" #include "bdev.h"
#include "cgroup.h" #include "cgroup.h"
@ -670,7 +669,7 @@ int pin_rootfs(const char *rootfs)
return fd; return fd;
} }
static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_cgroup_info *cgroup_info) static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_handler *handler)
{ {
int r; int r;
size_t i; size_t i;
@ -744,8 +743,8 @@ static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_cg
} }
if (flags & LXC_AUTO_CGROUP_MASK) { if (flags & LXC_AUTO_CGROUP_MASK) {
if (!lxc_setup_mount_cgroup(conf->rootfs.mount, cgroup_info, if (!cgroup_mount(conf->rootfs.mount, handler,
flags & LXC_AUTO_CGROUP_MASK)) { flags & LXC_AUTO_CGROUP_MASK)) {
SYSERROR("error mounting /sys/fs/cgroup"); SYSERROR("error mounting /sys/fs/cgroup");
return -1; return -1;
} }
@ -3500,7 +3499,6 @@ int lxc_setup(struct lxc_handler *handler)
struct lxc_conf *lxc_conf = handler->conf; struct lxc_conf *lxc_conf = handler->conf;
const char *lxcpath = handler->lxcpath; const char *lxcpath = handler->lxcpath;
void *data = handler->data; void *data = handler->data;
struct lxc_cgroup_info *cgroup_info = handler->cgroup_info;
if (lxc_conf->inherit_ns_fd[LXC_NS_UTS] == -1) { if (lxc_conf->inherit_ns_fd[LXC_NS_UTS] == -1) {
if (setup_utsname(lxc_conf->utsname)) { if (setup_utsname(lxc_conf->utsname)) {
@ -3538,7 +3536,7 @@ int lxc_setup(struct lxc_handler *handler)
/* do automatic mounts (mainly /proc and /sys), but exclude /* do automatic mounts (mainly /proc and /sys), but exclude
* those that need to wait until other stuff has finished * those that need to wait until other stuff has finished
*/ */
if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & ~LXC_AUTO_CGROUP_MASK, cgroup_info) < 0) { if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & ~LXC_AUTO_CGROUP_MASK, handler) < 0) {
ERROR("failed to setup the automatic mounts for '%s'", name); ERROR("failed to setup the automatic mounts for '%s'", name);
return -1; return -1;
} }
@ -3557,7 +3555,7 @@ int lxc_setup(struct lxc_handler *handler)
* before, /sys could not have been mounted * before, /sys could not have been mounted
* (is either mounted automatically or via fstab entries) * (is either mounted automatically or via fstab entries)
*/ */
if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & LXC_AUTO_CGROUP_MASK, cgroup_info) < 0) { if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & LXC_AUTO_CGROUP_MASK, handler) < 0) {
ERROR("failed to setup the automatic mounts for '%s'", name); ERROR("failed to setup the automatic mounts for '%s'", name);
return -1; return -1;
} }

View File

@ -35,18 +35,53 @@
#include "state.h" #include "state.h"
#include "monitor.h" #include "monitor.h"
#include "log.h" #include "log.h"
#include "cgroup.h" #include "lxc.h"
lxc_log_define(lxc_freezer, lxc); lxc_log_define(lxc_freezer, lxc);
lxc_state_t freezer_state(const char *name, const char *lxcpath)
{
char v[100];
if (lxc_cgroup_get("freezer.state", v, 100, name, lxcpath) < 0)
return -1;
if (v[strlen(v)-1] == '\n')
v[strlen(v)-1] = '\0';
return lxc_str2state(v);
}
static int do_freeze_thaw(int freeze, const char *name, const char *lxcpath)
{
char v[100];
const char *state = freeze ? "FROZEN" : "THAWED";
if (lxc_cgroup_set("freezer.state", state, name, lxcpath) < 0) {
ERROR("Failed to freeze %s:%s", lxcpath, name);
return -1;
}
while (1) {
if (lxc_cgroup_get("freezer.state", v, 100, name, lxcpath) < 0) {
ERROR("Failed to get new freezer state for %s:%s", lxcpath, name);
return -1;
}
if (v[strlen(v)-1] == '\n')
v[strlen(v)-1] = '\0';
if (strncmp(v, state, strlen(state)) == 0) {
if (name)
lxc_monitor_send_state(name, freeze ? FROZEN : THAWED, lxcpath);
return 0;
}
sleep(1);
}
}
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);
return freeze_unfreeze(name, 1, lxcpath); return do_freeze_thaw(1, name, lxcpath);
} }
int lxc_unfreeze(const char *name, const char *lxcpath) int lxc_unfreeze(const char *name, const char *lxcpath)
{ {
return freeze_unfreeze(name, 0, lxcpath); return do_freeze_thaw(0, name, lxcpath);
} }

View File

@ -129,17 +129,6 @@ extern int lxc_unfreeze(const char *name, const char *lxcpath);
*/ */
extern lxc_state_t lxc_state(const char *name, const char *lxcpath); extern lxc_state_t lxc_state(const char *name, const char *lxcpath);
struct lxc_handler;
/*
* Set a specified value for a specified subsystem. The specified
* subsystem must be fully specified, eg. "cpu.shares"
* @filename : the cgroup attribute filename
* @value : the value to be set
* @handler : the lxc_handler structure of the container
* Returns 0 on success, < 0 otherwise
*/
extern int lxc_cgroup_set_handler(const char *filename, const char *value, struct lxc_handler *handler);
/* /*
* 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"

View File

@ -291,7 +291,7 @@ static int utmp_get_ntasks(struct lxc_handler *handler)
{ {
int ntasks; int ntasks;
ntasks = lxc_cgroup_nrtasks_handler(handler); ntasks = cgroup_nrtasks(handler);
if (ntasks < 0) { if (ntasks < 0) {
ERROR("failed to get the number of tasks"); ERROR("failed to get the number of tasks");

View File

@ -70,7 +70,7 @@ struct lxc_handler {
int sv[2]; int sv[2];
int pinfd; int pinfd;
const char *lxcpath; const char *lxcpath;
struct lxc_cgroup_info *cgroup_info; void *cgroup_data;
}; };
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 *);

View File

@ -69,6 +69,8 @@ lxc_state_t lxc_str2state(const char *state)
lxc_state_t lxc_getstate(const char *name, const char *lxcpath) lxc_state_t lxc_getstate(const char *name, const char *lxcpath)
{ {
extern lxc_state_t freezer_state(const char *name, const char *lxcpath);
lxc_state_t state = freezer_state(name, lxcpath); lxc_state_t state = freezer_state(name, lxcpath);
if (state != FROZEN && state != FREEZING) if (state != FROZEN && state != FREEZING)
state = lxc_cmd_get_state(name, lxcpath); state = lxc_cmd_get_state(name, lxcpath);

View File

@ -50,9 +50,7 @@ static int test_running_container(const char *lxcpath,
int ret = -1; int ret = -1;
struct lxc_container *c = NULL; struct lxc_container *c = NULL;
char *cgrelpath; char *cgrelpath;
char *cgabspath;
char relpath[PATH_MAX+1]; char relpath[PATH_MAX+1];
char abspath[PATH_MAX+1];
char value[NAME_MAX], value_save[NAME_MAX]; char value[NAME_MAX], value_save[NAME_MAX];
sprintf(relpath, "%s/%s", group ? group : "lxc", name); sprintf(relpath, "%s/%s", group ? group : "lxc", name);
@ -109,32 +107,8 @@ static int test_running_container(const char *lxcpath,
goto err3; goto err3;
} }
cgabspath = lxc_cgroup_path_get("freezer", c->name, c->config_path);
if (!cgabspath) {
TSTERR("lxc_cgroup_path_get returned NULL");
goto err3;
}
sprintf(abspath, "%s/%s/%s", "freezer", group ? group : "lxc", c->name);
if (!strstr(cgabspath, abspath)) {
TSTERR("lxc_cgroup_path_get %s not in %s", abspath, cgabspath);
goto err4;
}
free(cgabspath);
cgabspath = lxc_cgroup_path_get("freezer.state", c->name, c->config_path);
if (!cgabspath) {
TSTERR("lxc_cgroup_path_get returned NULL");
goto err3;
}
sprintf(abspath, "%s/%s/%s", "freezer", group ? group : "lxc", c->name);
if (!strstr(cgabspath, abspath)) {
TSTERR("lxc_cgroup_path_get %s not in %s", abspath, cgabspath);
goto err4;
}
ret = 0; ret = 0;
err4:
free(cgabspath);
err3: err3:
free(cgrelpath); free(cgrelpath);
err2: err2: