])
])
+# 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]])],
- Linux capabilities: $enable_capabilities
- seccomp: $enable_seccomp
- SELinux: $enable_selinux
+ - cgmanager: $enable_cgmanager
Bindings:
- lua: $enable_lua
\
$(LSM_SOURCES)
+if ENABLE_CGMANAGER
+liblxc_so_SOURCES += cgmanager.c
+endif
+
if IS_BIONIC
liblxc_so_SOURCES += \
../include/ifaddrs.c ../include/ifaddrs.h \
AM_CFLAGS += -DHAVE_APPARMOR
endif
+if ENABLE_CGMANAGER
+AM_CFLAGS += -DHAVE_CGMANAGER
+endif
+
if ENABLE_SELINUX
AM_CFLAGS += -DHAVE_SELINUX
endif
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 \
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
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);
--- /dev/null
+/*
+ * 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
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#undef _GNU_SOURCE
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#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 <nih-dbus/dbus_connection.h>
+#include <cgmanager-client/cgmanager-client.h>
+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; i<d->nr_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; i<d->nr_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
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;
}
/* 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;
}
/* 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;
}
/* 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;
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;
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) {
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;
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;
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;
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;
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;
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;
+ 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);
}
+}
- if (freeze) {
- f = "FROZEN";
- ret = write(fd, f, strlen(f) + 1);
- } else {
- f = "THAWED";
- ret = write(fd, f, strlen(f) + 1);
+int freeze_unfreeze(const char *name, int freeze, const char *lxcpath)
+{
+ return do_unfreeze(freeze, name, lxcpath);
+}
- /* compatibility code with old freezer interface */
- if (ret < 0) {
- f = "RUNNING";
- ret = write(fd, f, strlen(f) + 1) < 0;
- }
+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) {
+ ERROR("Failed to get freezer state for %s:%s", lxcpath, name);
+ return -1;
}
- if (ret < 0) {
- SYSERROR("failed to write '%s' to '%s'", f, freezer);
- goto out;
- }
+ if (v[strlen(v)-1] == '\n')
+ v[strlen(v)-1] = '\0';
+ return lxc_str2state(v);
+}
- while (1) {
- ret = lseek(fd, 0L, SEEK_SET);
- if (ret < 0) {
- SYSERROR("failed to lseek on file '%s'", freezer);
- goto out;
- }
+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;
+}
- ret = read(fd, tmpf, sizeof(tmpf));
- if (ret < 0) {
- SYSERROR("failed to read to '%s'", freezer);
- goto out;
- }
+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();
- ret = strncmp(f, tmpf, strlen(f));
- if (!ret)
- {
- if (name)
- lxc_monitor_send_state(name, freeze ? FROZEN : THAWED, lxcpath);
- break; /* Success */
- }
+ if (!d->meta) {
+ ERROR("cgroupfs failed to detect cgroup metadata");
+ return false;
+ }
+ handler->cgroup_info->data = d;
+ return true;
+}
- sleep(1);
+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;
+}
- ret = lseek(fd, 0L, SEEK_SET);
- if (ret < 0) {
- SYSERROR("failed to lseek on file '%s'", freezer);
- goto out;
- }
+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);
- ret = write(fd, f, strlen(f) + 1);
- if (ret < 0) {
- SYSERROR("failed to write '%s' to '%s'", f, freezer);
- goto out;
- }
+ 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;
+}
-out:
- close(fd);
- return ret;
+static char *cgfs_get_cgroup(struct lxc_handler *handler, const char *subsystem)
+{
+ return lxc_cgroup_get_hierarchy_path_handler(subsystem, handler);
}
-int freeze_unfreeze(const char *name, int freeze, const char *lxcpath)
+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)
{
- char *cgabspath;
- int ret;
+ 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;
+ }
+}
- cgabspath = lxc_cgroup_get_hierarchy_abs_path("freezer", name, lxcpath);
- if (!cgabspath)
- return -1;
+/*
+ * These are the backend-independent cgroup handlers for container
+ * start and stop
+ */
- ret = do_unfreeze(cgabspath, freeze, name, lxcpath);
- free(cgabspath);
- return ret;
+/* 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);
}
-lxc_state_t freezer_state(const char *name, const char *lxcpath)
+/*
+ * Allocate a lxc_cgroup_info for the active cgroup
+ * backend, and assign it to the handler
+ */
+bool cgroup_init(struct lxc_handler *handler)
{
- char *cgabspath = NULL;
- char freezer[MAXPATHLEN];
- char status[MAXPATHLEN];
- FILE *file;
- int ret;
+ 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";
- cgabspath = lxc_cgroup_get_hierarchy_abs_path("freezer", name, lxcpath);
- if (!cgabspath)
- return -1;
+ return active_cg_ops->init(handler);
+}
- ret = snprintf(freezer, MAXPATHLEN, "%s/freezer.state", cgabspath);
- if (ret < 0 || ret >= MAXPATHLEN)
- goto out;
+/* Create the container cgroups for all requested controllers */
+bool cgroup_create(struct lxc_handler *handler)
+{
+ return active_cg_ops->create(handler);
+}
- file = fopen(freezer, "r");
- if (!file) {
- ret = -1;
- goto out;
- }
+/*
+ * 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;
+}
- ret = fscanf(file, "%s", status);
- fclose(file);
+/* 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;
+}
- if (ret == EOF) {
- SYSERROR("failed to read %s", freezer);
- ret = -1;
- goto out;
- }
+/*
+ * 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);
+}
- ret = lxc_str2state(status);
+bool cgroup_create_legacy(struct lxc_handler *handler)
+{
+ if (active_cg_ops->create_legacy)
+ return active_cg_ops->create_legacy(handler);
+ return true;
+}
-out:
- free(cgabspath);
- return ret;
+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);
+}
* 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;
/* 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
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
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,
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;
}
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;
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);
*/
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);
{
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;
-}
*/
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
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);
}
#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;
}
{
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;
}
- 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;
}
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)
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;
}
goto out_abort;
}
- lxc_cgroup_put_meta(cgroup_meta);
lxc_sync_fini(handler);
return 0;
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) {
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 *);