diff --git a/configure.ac b/configure.ac index 8880b7e92..d8be165d0 100644 --- a/configure.ac +++ b/configure.ac @@ -240,6 +240,23 @@ AM_COND_IF([ENABLE_SECCOMP], ]) ]) +# cgmanager +AC_ARG_ENABLE([cgmanager], + [AC_HELP_STRING([--enable-cgmanager], [enable cgmanager support [default=auto]])], + [], [enable_cgmanager=auto]) + +if test "x$enable_cgmanager" = "xauto" ; then + AC_CHECK_LIB([cgmanager],[cgmanager_create],[enable_cgmanager=yes],[enable_cgmanager=no]) +fi +AM_CONDITIONAL([ENABLE_CGMANAGER], [test "x$enable_cgmanager" = "xyes"]) + +AM_COND_IF([ENABLE_CGMANAGER], + [PKG_CHECK_MODULES([CGMANAGER], [libcgmanager]) + PKG_CHECK_MODULES([NIH], [libnih >= 1.0.2]) + PKG_CHECK_MODULES([NIH_DBUS], [libnih-dbus >= 1.0.0]) + PKG_CHECK_MODULES([DBUS], [dbus-1 >= 1.2.16]) + ]) + # Linux capabilities AC_ARG_ENABLE([capabilities], [AC_HELP_STRING([--enable-capabilities], [enable kernel capabilities support [default=auto]])], @@ -689,6 +706,7 @@ Security features: - Linux capabilities: $enable_capabilities - seccomp: $enable_seccomp - SELinux: $enable_selinux + - cgmanager: $enable_cgmanager Bindings: - lua: $enable_lua diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 1e0232b13..34d69a609 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -93,6 +93,10 @@ liblxc_so_SOURCES = \ \ $(LSM_SOURCES) +if ENABLE_CGMANAGER +liblxc_so_SOURCES += cgmanager.c +endif + if IS_BIONIC liblxc_so_SOURCES += \ ../include/ifaddrs.c ../include/ifaddrs.h \ @@ -122,6 +126,10 @@ if ENABLE_APPARMOR AM_CFLAGS += -DHAVE_APPARMOR endif +if ENABLE_CGMANAGER +AM_CFLAGS += -DHAVE_CGMANAGER +endif + if ENABLE_SELINUX AM_CFLAGS += -DHAVE_SELINUX endif @@ -144,6 +152,11 @@ liblxc_so_LDFLAGS = \ liblxc_so_LDADD = $(CAP_LIBS) $(APPARMOR_LIBS) $(SECCOMP_LIBS) +#if ENABLE_CGMANAGER +liblxc_so_LDADD += $(CGMANAGER_LIBS) $(DBUS_LIBS) $(NIH_LIBS) $(NIH_DBUS_LIBS) +liblxc_so_CFLAGS += $(CGMANAGER_CFLAGS) $(DBUS_CFLAGS) $(NIH_CFLAGS) $(NIH_DBUS_CFLAGS) +#endif + bin_SCRIPTS = \ lxc-ps \ lxc-netstat \ @@ -245,6 +258,11 @@ LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@ @SECCOMP_LIBS@ lxc_attach_SOURCES = lxc_attach.c lxc_autostart_SOURCES = lxc_autostart.c lxc_cgroup_SOURCES = lxc_cgroup.c +#if ENABLE_CGMANAGER +lxc_cgroup_SOURCES += cgmanager.c +lxc_cgroup_LDADD = $(CGMANAGER_LIBS) $(DBUS_LIBS) $(NIH_LIBS) $(NIH_DBUS_LIBS) $(LDADD) +lxc_cgroup_CFLAGS = $(CGMANAGER_CFLAGS) $(DBUS_CFLAGS) $(NIH_CFLAGS) $(NIH_DBUS_CFLAGS) +#endif lxc_checkpoint_SOURCES = lxc_checkpoint.c lxc_config_SOURCES = lxc_config.c lxc_console_SOURCES = lxc_console.c diff --git a/src/lxc/attach.c b/src/lxc/attach.c index 422f24cac..de325497b 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -748,7 +748,11 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun goto cleanup_error; } - ret = lxc_cgroup_enter(container_info, attached_pid, false); + /* + * TODO - switch over to using a cgroup_operation. We can't use + * cgroup_enter() as that takes a handler. + */ + ret = lxc_cgroupfs_enter(container_info, attached_pid, false); lxc_cgroup_process_info_free(container_info); if (ret < 0) { ERROR("could not move attached process %ld to cgroup of container", (long)attached_pid); diff --git a/src/lxc/cgmanager.c b/src/lxc/cgmanager.c new file mode 100644 index 000000000..92745dcaf --- /dev/null +++ b/src/lxc/cgmanager.c @@ -0,0 +1,367 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * 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 + */ +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "config.h" +#include "commands.h" +#include "list.h" +#include "conf.h" +#include "utils.h" +#include "bdev.h" +#include "log.h" +#include "cgroup.h" +#include "start.h" +#include "state.h" + +#ifdef HAVE_CGMANAGER +lxc_log_define(lxc_cgmanager, lxc); + +#include +#include +NihDBusProxy *cgroup_manager = NULL; + +extern struct cgroup_ops *active_cg_ops; +bool cgmanager_initialized = false; +bool use_cgmanager = true; +static struct cgroup_ops cgmanager_ops; + +bool lxc_init_cgmanager(void); +static void cgmanager_disconnected(DBusConnection *connection) +{ + WARN("Cgroup manager connection was terminated"); + cgroup_manager = NULL; + cgmanager_initialized = false; + if (lxc_init_cgmanager()) { + cgmanager_initialized = true; + INFO("New cgroup manager connection was opened"); + } +} + +#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) { + ERROR("Error opening cgmanager connection at %s", CGMANAGER_DBUS_SOCK); + 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) { + return false; + } + active_cg_ops = &cgmanager_ops; + return true; +} + +/* + * Use the cgmanager to move a task into a cgroup for a particular + * hierarchy. + * All the subsystems in this hierarchy are co-mounted, so we only + * need to transition the task into one of the cgroups + */ +static bool lxc_cgmanager_enter(pid_t pid, char *controller, char *cgroup_path) +{ + return cgmanager_move_pid_sync(NULL, cgroup_manager, controller, + cgroup_path, pid) == 0; +} + +static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path, int32_t *existed) +{ + if ( cgmanager_create_sync(NULL, cgroup_manager, controller, + cgroup_path, existed) != 0) { + ERROR("Failed to create %s:%s", controller, cgroup_path); + return false; + } + + // TODO - try to chown the cgroup to the container root + return true; +} + + +struct cgm_data { + int nr_subsystems; + char **subsystems; + char *cgroup_path; +}; + +void cgmanager_remove_cgroup(const char *subsystem, const char *path) +{ + // TODO implement + WARN("%s: not yet implemented", __func__); +} + +static void cgm_destroy(struct lxc_handler *handler) +{ + struct cgm_data *d = handler->cgroup_info->data; + int i; + + if (!d) + return; + for (i=0; inr_subsystems; i++) { + if (d->cgroup_path) + cgmanager_remove_cgroup(d->subsystems[i], d->cgroup_path); + free(d->subsystems[i]); + } + free(d->subsystems); + free(d->cgroup_path); + free(d); + handler->cgroup_info->data = NULL; +} + +/* + * remove all the cgroups created + */ +static inline void cleanup_cgroups(struct cgm_data *d, char *path) +{ + int i; + for (i = 0; i < d->nr_subsystems; i++) { + cgmanager_remove_cgroup(d->subsystems[i], path); + } +} + +static inline bool cgm_create(struct lxc_handler *handler) +{ + int i, index=0, baselen, ret; + int32_t existed; + char result[MAXPATHLEN], *tmp; + struct cgm_data *d = handler->cgroup_info->data; + +// XXX we should send a hint to the cgmanager that when these +// cgroups become empty they should be deleted. Requires a cgmanager +// extension + + memset(result, 0, MAXPATHLEN); + tmp = lxc_string_replace("%n", handler->name, handler->cgroup_info->cgroup_pattern); + if (!tmp) + return false; + if (strlen(tmp) > MAXPATHLEN) + return false; + strcpy(result, tmp); + baselen = strlen(result); + free(tmp); + tmp = result; + while (*tmp == '/') + tmp++; +again: + if (index == 100) { // turn this into a warn later + ERROR("cgroup error? 100 cgroups with this name already running"); + return false; + } + if (index) { + ret = snprintf(result+baselen, MAXPATHLEN-baselen, "-%d", index); + if (ret < 0 || ret >= MAXPATHLEN-baselen) + return false; + } + existed = 0; + for (i = 0; i < d->nr_subsystems; i++) { + if (!lxc_cgmanager_create(d->subsystems[i], tmp, &existed)) { + ERROR("Error creating cgroup %s:%s", d->subsystems[i], result); + cleanup_cgroups(d, tmp); + return false; + } + if (existed) + goto next; + } + // success + d->cgroup_path = strdup(tmp); + if (!d->cgroup_path) { + cleanup_cgroups(d, tmp); + return false; + } + return true; +next: + cleanup_cgroups(d, tmp); + index++; + goto again; +} + +static inline bool cgm_enter(struct lxc_handler *handler) +{ + struct cgm_data *d = handler->cgroup_info->data; + int i; + + for (i = 0; i < d->nr_subsystems; i++) { + if (!lxc_cgmanager_enter(handler->pid, d->subsystems[i], d->cgroup_path)) + return false; + } + return true; +} + +static char *cgm_get_cgroup(struct lxc_handler *handler, const char *subsystem) +{ + struct cgm_data *d = handler->cgroup_info->data; + return d->cgroup_path; +} + +int cgm_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) +{ + char *result, *controller, *key, *cgroup; + + controller = alloca(strlen(filename)+1); + key = strchr(controller, '.'); + if (!key) + return false; + *key = '\0'; + key++; + + /* use the command interface to look for the cgroup */ + cgroup = lxc_cmd_get_cgroup_path(name, lxcpath, controller); + if (!cgroup) { + ERROR("Failed to get cgroup for controller %s for %s:%s", + controller, lxcpath, name); + return false; + } + if (cgmanager_get_value_sync(NULL, cgroup_manager, controller, cgroup, key, &result) != 0) { + ERROR("Error getting value for %s from cgmanager for cgroup %s (%s:%s)", + filename, cgroup, lxcpath, name); + free(cgroup); + return false; + } + free(cgroup); + strncpy(value, result, len); + if (strlen(result) >= len) + value[len-1] = '\0'; + free(result); + return true; +} + +int cgm_set(const char *filename, const char *value, const char *name, const char *lxcpath) +{ + char *controller, *key, *cgroup; + + controller = alloca(strlen(filename)+1); + key = strchr(controller, '.'); + if (!key) + return false; + *key = '\0'; + key++; + + /* use the command interface to look for the cgroup */ + cgroup = lxc_cmd_get_cgroup_path(name, lxcpath, controller); + if (!cgroup) { + ERROR("Failed to get cgroup for controller %s for %s:%s", + controller, lxcpath, name); + return false; + } + if (cgmanager_set_value_sync(NULL, cgroup_manager, controller, cgroup, key, value) != 0) { + ERROR("Error setting value for %s from cgmanager for cgroup %s (%s:%s)", + filename, cgroup, lxcpath, name); + free(cgroup); + return false; + } + free(cgroup); + return true; +} + +/* + * TODO really this should be done once for global data, not once + * per container + */ +static inline bool cgm_init(struct lxc_handler *handler) +{ + struct cgm_data *d = malloc(sizeof(*d)); + char *line = NULL, *tab1; + size_t sz = 0, i; + FILE *f; + + if (!d) + return false; + d->nr_subsystems = 0; + d->subsystems = NULL; + f = fopen_cloexec("/proc/cgroups", "r"); + if (!f) { + free(d); + return false; + } + while (getline(&line, &sz, f) != -1) { + char **tmp; + if (line[0] == '#') + continue; + if (!line[0]) + continue; + tab1 = strchr(line, '\t'); + if (!tab1) + continue; + *tab1 = '\0'; + tmp = realloc(d->subsystems, (d->nr_subsystems+1)*sizeof(char *)); + if (!tmp) { + goto out_free; + } + d->subsystems = tmp; + d->subsystems[d->nr_subsystems] = strdup(line); + if (!d->subsystems[d->nr_subsystems]) + goto out_free; + d->nr_subsystems++; + } + fclose(f); + + d->cgroup_path = NULL; + handler->cgroup_info->data = d; + return true; + +out_free: + for (i=0; inr_subsystems; i++) + free(d->subsystems[i]); + free(d->subsystems); + free(d); + return false; +} + +static struct cgroup_ops cgmanager_ops = { + .destroy = cgm_destroy, + .init = cgm_init, + .create = cgm_create, + .enter = cgm_enter, + .create_legacy = NULL, + .get_cgroup = cgm_get_cgroup, + .get = cgm_get, + .set = cgm_set, + .name = "cgmanager" +}; +#endif diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index b2dfd2678..9d38fb0d6 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -75,6 +75,21 @@ static int cgroup_recursive_task_count(const char *cgroup_path); static int count_lines(const char *fn); static int handle_cgroup_settings(struct cgroup_mount_point *mp, char *cgroup_path); +static struct cgroup_ops cgfs_ops; +struct cgroup_ops *active_cg_ops = &cgfs_ops; +static void init_cg_ops(void); + +#ifdef HAVE_CGMANAGER +/* this needs to be mutexed for api use */ +extern bool cgmanager_initialized; +extern bool use_cgmanager; +extern bool lxc_init_cgmanager(void); +#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) { struct dirent dirent, *direntp; @@ -169,7 +184,7 @@ struct cgroup_meta_data *lxc_cgroup_load_meta() } /* Step 1: determine all kernel subsystems */ -static bool find_cgroup_subsystems(char ***kernel_subsystems) +bool find_cgroup_subsystems(char ***kernel_subsystems) { FILE *proc_cgroups; bool bret = false; @@ -709,7 +724,7 @@ static char *cgroup_rename_nsgroup(const char *mountpath, const char *oldname, p } /* 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) +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 **p = NULL; @@ -1041,7 +1056,7 @@ out_error: } /* move a processs to the cgroups specified by the membership */ -int lxc_cgroup_enter(struct cgroup_process_info *info, pid_t pid, bool enter_sub) +int lxc_cgroupfs_enter(struct cgroup_process_info *info, pid_t pid, bool enter_sub) { char pid_buf[32]; char *cgroup_tasks_fn; @@ -1125,9 +1140,11 @@ void lxc_cgroup_process_info_free_and_remove(struct cgroup_process_info *info) lxc_cgroup_process_info_free_and_remove(next); } -char *lxc_cgroup_get_hierarchy_path_handler(const char *subsystem, struct lxc_handler *handler) +static char *lxc_cgroup_get_hierarchy_path_handler(const char *subsystem, struct lxc_handler *handler) { - struct cgroup_process_info *info = find_info_for_subsystem(handler->cgroup, subsystem); + struct cgfs_data *d = handler->cgroup_info->data; + struct cgroup_process_info *info = d->info; + info = find_info_for_subsystem(info, subsystem); if (!info) return NULL; return info->cgroup_path; @@ -1140,8 +1157,11 @@ char *lxc_cgroup_get_hierarchy_path(const char *subsystem, const char *name, con 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_mount_point *mp = NULL; - struct cgroup_process_info *info = find_info_for_subsystem(handler->cgroup, subsystem); + + info = find_info_for_subsystem(info, subsystem); if (!info) return NULL; if (info->designated_mount_point) { @@ -1222,7 +1242,7 @@ int lxc_cgroup_get_handler(const char *filename, char *value, size_t len, struct return ret; } -int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath) +int lxc_cgroupfs_set(const char *filename, const char *value, const char *name, const char *lxcpath) { char *subsystem = NULL, *p, *path; int ret = -1; @@ -1240,7 +1260,7 @@ int lxc_cgroup_set(const char *filename, const char *value, const char *name, co return ret; } -int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) +int lxc_cgroupfs_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) { char *subsystem = NULL, *p, *path; int ret = -1; @@ -1298,17 +1318,7 @@ char *lxc_cgroup_path_get(const char *filename, const char *name, return path; } -int lxc_setup_cgroup_without_devices(struct lxc_handler *h, struct lxc_list *cgroup_settings) -{ - return do_setup_cgroup(h, cgroup_settings, false); -} - -int lxc_setup_cgroup_devices(struct lxc_handler *h, struct lxc_list *cgroup_settings) -{ - return do_setup_cgroup(h, cgroup_settings, true); -} - -int lxc_setup_mount_cgroup(const char *root, struct cgroup_process_info *base_info, int type) +int lxc_setup_mount_cgroup(const char *root, struct lxc_cgroup_info *cgroup_info, int type) { size_t bufsz = strlen(root) + sizeof("/sys/fs/cgroup"); char *path = NULL; @@ -1316,9 +1326,20 @@ int lxc_setup_mount_cgroup(const char *root, struct cgroup_process_info *base_in char *dirname = NULL; char *abs_path = NULL; char *abs_path2 = NULL; - struct cgroup_process_info *info; + struct cgfs_data *cgfs_d; + struct cgroup_process_info *info, *base_info; int r, saved_errno = 0; + init_cg_ops(); + + if (strcmp(active_cg_ops->name, "cgmanager") == 0) { + // todo - offer to bind-mount /sys/fs/cgroup/cgmanager/ + return 0; + } + + cgfs_d = cgroup_info->data; + base_info = cgfs_d->info; + if (type < LXC_AUTO_CGROUP_RO || type > LXC_AUTO_CGROUP_FULL_MIXED) { ERROR("could not mount cgroups into container: invalid type specified internally"); errno = EINVAL; @@ -1487,7 +1508,8 @@ out_error: int lxc_cgroup_nrtasks_handler(struct lxc_handler *handler) { - struct cgroup_process_info *info = handler->cgroup; + struct cgfs_data *d = handler->cgroup_info->data; + struct cgroup_process_info *info = d->info; struct cgroup_mount_point *mp = NULL; char *abs_path = NULL; int ret; @@ -2035,133 +2057,227 @@ static int handle_cgroup_settings(struct cgroup_mount_point *mp, extern void lxc_monitor_send_state(const char *name, lxc_state_t state, const char *lxcpath); -int do_unfreeze(const char *nsgroup, int freeze, const char *name, const char *lxcpath) +int do_unfreeze(int freeze, const char *name, const char *lxcpath) { - char freezer[MAXPATHLEN], *f; - char tmpf[32]; - int fd, ret; + char v[100]; + const char *state = freeze ? "FROZEN" : "THAWED"; - ret = snprintf(freezer, MAXPATHLEN, "%s/freezer.state", nsgroup); - if (ret >= MAXPATHLEN) { - ERROR("freezer.state name too long"); + if (lxc_cgroup_set("freezer.state", state, name, lxcpath) < 0) { + ERROR("Failed to freeze %s:%s", lxcpath, name); return -1; } - - fd = open(freezer, O_RDWR); - if (fd < 0) { - SYSERROR("failed to open freezer at '%s'", nsgroup); - return -1; - } - - if (freeze) { - f = "FROZEN"; - ret = write(fd, f, strlen(f) + 1); - } else { - f = "THAWED"; - ret = write(fd, f, strlen(f) + 1); - - /* compatibility code with old freezer interface */ - if (ret < 0) { - f = "RUNNING"; - ret = write(fd, f, strlen(f) + 1) < 0; - } - } - - if (ret < 0) { - SYSERROR("failed to write '%s' to '%s'", f, freezer); - goto out; - } - while (1) { - ret = lseek(fd, 0L, SEEK_SET); - if (ret < 0) { - SYSERROR("failed to lseek on file '%s'", freezer); - goto out; + 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; } - - ret = read(fd, tmpf, sizeof(tmpf)); - if (ret < 0) { - SYSERROR("failed to read to '%s'", freezer); - goto out; - } - - ret = strncmp(f, tmpf, strlen(f)); - if (!ret) - { + 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); - break; /* Success */ + return 0; } - sleep(1); - - ret = lseek(fd, 0L, SEEK_SET); - if (ret < 0) { - SYSERROR("failed to lseek on file '%s'", freezer); - goto out; - } - - ret = write(fd, f, strlen(f) + 1); - if (ret < 0) { - SYSERROR("failed to write '%s' to '%s'", f, freezer); - goto out; - } } - -out: - close(fd); - return ret; } int freeze_unfreeze(const char *name, int freeze, const char *lxcpath) { - char *cgabspath; - int ret; - - cgabspath = lxc_cgroup_get_hierarchy_abs_path("freezer", name, lxcpath); - if (!cgabspath) - return -1; - - ret = do_unfreeze(cgabspath, freeze, name, lxcpath); - free(cgabspath); - return ret; + return do_unfreeze(freeze, name, lxcpath); } lxc_state_t freezer_state(const char *name, const char *lxcpath) { - char *cgabspath = NULL; - char freezer[MAXPATHLEN]; - char status[MAXPATHLEN]; - FILE *file; - int ret; - - cgabspath = lxc_cgroup_get_hierarchy_abs_path("freezer", name, lxcpath); - if (!cgabspath) + char v[100]; + if (lxc_cgroup_get("freezer.state", v, 100, name, lxcpath) < 0) { + ERROR("Failed to get freezer state for %s:%s", lxcpath, name); return -1; - - ret = snprintf(freezer, MAXPATHLEN, "%s/freezer.state", cgabspath); - if (ret < 0 || ret >= MAXPATHLEN) - goto out; - - file = fopen(freezer, "r"); - if (!file) { - ret = -1; - goto out; } - ret = fscanf(file, "%s", status); - fclose(file); - - if (ret == EOF) { - SYSERROR("failed to read %s", freezer); - ret = -1; - goto out; - } - - ret = lxc_str2state(status); - -out: - free(cgabspath); - return ret; + 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) + return; + if (d->info) + lxc_cgroup_process_info_free_and_remove(d->info); + if (d->meta) + lxc_cgroup_put_meta(d->meta); + free(d); + handler->cgroup_info->data = NULL; +} + +static inline bool cgfs_init(struct lxc_handler *handler) +{ + struct cgfs_data *d = malloc(sizeof(*d)); + if (!d) + return false; + d->info = NULL; + d->meta = lxc_cgroup_load_meta(); + + if (!d->meta) { + ERROR("cgroupfs failed to detect cgroup metadata"); + 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) + return false; + d->info = i; + return true; +} + +static inline bool cgfs_enter(struct lxc_handler *handler) +{ + struct cgfs_data *d = handler->cgroup_info->data; + struct cgroup_process_info *i = d->info; + int ret; + + ret = lxc_cgroupfs_enter(i, handler->pid, false); + + return ret == 0; +} + +static inline bool cgfs_create_legacy(struct lxc_handler *handler) +{ + struct cgfs_data *d = handler->cgroup_info->data; + struct cgroup_process_info *i = d->info; + if (lxc_cgroup_create_legacy(i, handler->name, handler->pid) < 0) { + ERROR("failed to create legacy ns cgroups for '%s'", handler->name); + return false; + } + return true; +} + +static char *cgfs_get_cgroup(struct lxc_handler *handler, const char *subsystem) +{ + return lxc_cgroup_get_hierarchy_path_handler(subsystem, handler); +} + +static struct cgroup_ops cgfs_ops = { + .destroy = cgfs_destroy, + .init = cgfs_init, + .create = cgfs_create, + .enter = cgfs_enter, + .create_legacy = cgfs_create_legacy, + .get_cgroup = cgfs_get_cgroup, + .get = lxc_cgroupfs_get, + .set = lxc_cgroupfs_set, + .name = "cgroupfs", +}; +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); +} + +/* + * Set up per-controller configuration excluding the devices + * cgroup + */ +bool cgroup_setup_without_devices(struct lxc_handler *handler) +{ + return do_setup_cgroup(handler, &handler->conf->cgroup, false) == 0; +} + +/* Set up the devices cgroup configuration for the container */ +bool cgroup_setup_devices(struct lxc_handler *handler) +{ + return do_setup_cgroup(handler, &handler->conf->cgroup, true) == 0; +} + +/* + * 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); +} diff --git a/src/lxc/cgroup.h b/src/lxc/cgroup.h index a2521233f..1dc459bde 100644 --- a/src/lxc/cgroup.h +++ b/src/lxc/cgroup.h @@ -72,6 +72,9 @@ struct cgroup_mount_point { * 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; @@ -121,22 +124,19 @@ extern int lxc_cgroup_create_legacy(struct cgroup_process_info *base_info, const /* 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 */ -extern int lxc_cgroup_enter(struct cgroup_process_info *info, pid_t pid, bool enter_sub); +/* 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; -extern char *lxc_cgroup_get_hierarchy_path_handler(const char *subsystem, struct lxc_handler *handler); extern char *lxc_cgroup_get_hierarchy_path(const char *subsystem, const char *name, const char *lxcpath); 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); -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); /* * lxc_cgroup_path_get: Get the absolute pathname for a cgroup @@ -162,12 +162,57 @@ 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_setup_mount_cgroup(const char *root, struct cgroup_process_info *base_info, int type); - extern int lxc_cgroup_nrtasks_handler(struct lxc_handler *handler); -extern int do_unfreeze(const char *nsgroup, int freeze, const char *name, const char *lxcpath); +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); +/* per-backend cgroup hooks */ +struct cgroup_ops { + void (*destroy)(struct lxc_handler *handler); + bool (*init)(struct lxc_handler *handler); + bool (*create)(struct lxc_handler *handler); + bool (*enter)(struct lxc_handler *handler); + bool (*create_legacy)(struct lxc_handler *handler); + char *(*get_cgroup)(struct lxc_handler *handler, const char *subsystem); + 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); + const char *name; +}; + +/* + * 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; +}; + +extern int lxc_setup_mount_cgroup(const char *root, struct lxc_cgroup_info *base_info, int type); + +struct cgfs_data { + struct cgroup_meta_data *meta; + struct cgroup_process_info *info; +}; + +/* + * backend-independent cgroup handlers + */ +extern void cgroup_destroy(struct lxc_handler *handler); +extern bool cgroup_init(struct lxc_handler *handler); +extern bool cgroup_create(struct lxc_handler *handler); +extern bool cgroup_setup_without_devices(struct lxc_handler *handler); +extern bool cgroup_setup_devices(struct lxc_handler *handler); +extern bool cgroup_enter(struct lxc_handler *handler); +extern void cgroup_cleanup(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 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 diff --git a/src/lxc/commands.c b/src/lxc/commands.c index e993f2e25..29aa905eb 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -435,7 +435,7 @@ static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req, if (req->datalen < 1) return -1; - path = lxc_cgroup_get_hierarchy_path_handler(req->data, handler); + path = cgroup_get_cgroup(handler, req->data); if (!path) return -1; rsp.datalen = strlen(path) + 1, @@ -591,17 +591,10 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req, memset(&rsp, 0, sizeof(rsp)); rsp.ret = kill(handler->pid, stopsignal); if (!rsp.ret) { - char *path = lxc_cgroup_get_hierarchy_path_handler("freezer", handler); - if (!path) { - ERROR("container %s:%s is not in a freezer cgroup", - handler->lxcpath, handler->name); - return 0; - } - ret = lxc_unfreeze_bypath(path); + ret = lxc_unfreeze(handler->name, handler->lxcpath); if (!ret) return 0; - - ERROR("failed to unfreeze container"); + ERROR("Failed to unfreeze %s:%s", handler->lxcpath, handler->name); rsp.ret = ret; } diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 159684cdb..8ce783bef 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -646,7 +646,7 @@ int pin_rootfs(const char *rootfs) return fd; } -static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct cgroup_process_info *cgroup_info) +static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_cgroup_info *cgroup_info) { int r; size_t i; @@ -3458,8 +3458,14 @@ static int check_autodev( const char *rootfs, void *data ) return 0; } -int lxc_setup(const char *name, struct lxc_conf *lxc_conf, const char *lxcpath, struct cgroup_process_info *cgroup_info, void *data) +int lxc_setup(struct lxc_handler *handler) { + const char *name = handler->name; + struct lxc_conf *lxc_conf = handler->conf; + const char *lxcpath = handler->lxcpath; + void *data = handler->data; + struct lxc_cgroup_info *cgroup_info = handler->cgroup_info; + if (lxc_conf->inherit_ns_fd[LXC_NS_UTS] == -1) { if (setup_utsname(lxc_conf->utsname)) { ERROR("failed to setup the utsname for '%s'", name); diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 8efd0f3c8..a0ce3f7b5 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -366,10 +366,7 @@ extern int lxc_clear_groups(struct lxc_conf *c); */ struct cgroup_process_info; -extern int lxc_setup(const char *name, struct lxc_conf *lxc_conf, - const char *lxcpath, - struct cgroup_process_info *cgroup_info, - void *data); +extern int lxc_setup(struct lxc_handler *handler); extern void lxc_rename_phys_nics_on_shutdown(struct lxc_conf *conf); diff --git a/src/lxc/freezer.c b/src/lxc/freezer.c index 53e9f644e..c33a727d5 100644 --- a/src/lxc/freezer.c +++ b/src/lxc/freezer.c @@ -50,17 +50,3 @@ int lxc_unfreeze(const char *name, const char *lxcpath) { return freeze_unfreeze(name, 0, lxcpath); } - -int lxc_unfreeze_bypath(const char *cgrelpath) -{ - char *cgabspath; - int ret; - - cgabspath = lxc_cgroup_find_abs_path("freezer", cgrelpath, true, NULL); - if (!cgabspath) - return -1; - - ret = do_unfreeze(cgabspath, 0, NULL, NULL); - free(cgabspath); - return ret; -} diff --git a/src/lxc/lxc.h b/src/lxc/lxc.h index d048cf211..0682f8680 100644 --- a/src/lxc/lxc.h +++ b/src/lxc/lxc.h @@ -122,14 +122,6 @@ extern int lxc_freeze(const char *name, const char *lxcpath); */ extern int lxc_unfreeze(const char *name, const char *lxcpath); -/* - * Unfreeze all previously frozen tasks. - * This is the function to use from inside the monitor - * @name : the name of the container - * Return 0 on sucess, < 0 otherwise - */ -extern int lxc_unfreeze_bypath(const char *cgpath); - /* * Retrieve the container state * @name : the name of the container diff --git a/src/lxc/start.c b/src/lxc/start.c index 6c07e433f..441d0f45b 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -457,10 +457,7 @@ static void lxc_fini(const char *name, struct lxc_handler *handler) close(handler->conf->maincmd_fd); handler->conf->maincmd_fd = -1; free(handler->name); - if (handler->cgroup) { - lxc_cgroup_process_info_free_and_remove(handler->cgroup); - handler->cgroup = NULL; - } + cgroup_destroy(handler); free(handler); } @@ -602,7 +599,7 @@ static int do_start(void *data) #endif /* Setup the container, ip, names, utsname, ... */ - if (lxc_setup(handler->name, handler->conf, handler->lxcpath, handler->cgroup, handler->data) ){ + if (lxc_setup(handler)) { ERROR("failed to setup the container"); goto out_warn_father; } @@ -690,8 +687,6 @@ static int lxc_spawn(struct lxc_handler *handler) { int failed_before_rename = 0; const char *name = handler->name; - struct cgroup_meta_data *cgroup_meta = NULL; - const char *cgroup_pattern = NULL; int saved_ns_fd[LXC_NS_MAX]; int preserve_mask = 0, i; @@ -755,27 +750,13 @@ static int lxc_spawn(struct lxc_handler *handler) } - cgroup_meta = lxc_cgroup_load_meta(); - if (!cgroup_meta) { - ERROR("failed to detect cgroup metadata"); + if (!cgroup_init(handler)) { + ERROR("failed initializing cgroup support"); goto out_delete_net; } - /* 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 (getuid() == 0) - cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern"); - if (!cgroup_pattern) - cgroup_pattern = "%n"; - - /* Create cgroup before doing clone(), so the child will know from - * handler which cgroup it is going to be put in later. - */ - if ((handler->cgroup = lxc_cgroup_create(name, cgroup_pattern, cgroup_meta, NULL)) == NULL) { - ERROR("failed to create cgroups for '%s'", name); + if (!cgroup_create(handler)) { + ERROR("failed creating cgroups"); goto out_delete_net; } @@ -808,20 +789,16 @@ static int lxc_spawn(struct lxc_handler *handler) if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE)) failed_before_rename = 1; - /* In case there is still legacy ns cgroup support in the kernel. - * Should be removed at some later point in time. - */ - if (lxc_cgroup_create_legacy(handler->cgroup, name, handler->pid) < 0) { - ERROR("failed to create legacy ns cgroups for '%s'", name); + if (!cgroup_create_legacy(handler)) { + ERROR("failed to setup the legacy cgroups for %s", name); goto out_delete_net; } - - if (lxc_setup_cgroup_without_devices(handler, &handler->conf->cgroup)) { + if (!cgroup_setup_without_devices(handler)) { ERROR("failed to setup the cgroups for '%s'", name); goto out_delete_net; } - if (lxc_cgroup_enter(handler->cgroup, handler->pid, false) < 0) + if (!cgroup_enter(handler)) goto out_delete_net; if (failed_before_rename) @@ -851,7 +828,7 @@ static int lxc_spawn(struct lxc_handler *handler) if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CONFIGURE)) goto out_delete_net; - if (lxc_setup_cgroup_devices(handler, &handler->conf->cgroup)) { + if (!cgroup_setup_devices(handler)) { ERROR("failed to setup the devices cgroup for '%s'", name); goto out_delete_net; } @@ -878,7 +855,6 @@ static int lxc_spawn(struct lxc_handler *handler) goto out_abort; } - lxc_cgroup_put_meta(cgroup_meta); lxc_sync_fini(handler); return 0; @@ -887,7 +863,6 @@ out_delete_net: if (handler->clone_flags & CLONE_NEWNET) lxc_delete_network(handler); out_abort: - lxc_cgroup_put_meta(cgroup_meta); lxc_abort(name, handler); lxc_sync_fini(handler); if (handler->pinfd >= 0) { diff --git a/src/lxc/start.h b/src/lxc/start.h index 7d4ae5906..c30c66170 100644 --- a/src/lxc/start.h +++ b/src/lxc/start.h @@ -70,7 +70,7 @@ struct lxc_handler { int sv[2]; int pinfd; const char *lxcpath; - struct cgroup_process_info *cgroup; + struct lxc_cgroup_info *cgroup_info; }; extern struct lxc_handler *lxc_init(const char *name, struct lxc_conf *, const char *);