From 0e7ff52c929ca8a93e1d090c20b5d78336a53f43 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 13 Feb 2018 05:37:48 +0100 Subject: [PATCH] tree-wide: remove cgmanager Signed-off-by: Christian Brauner --- .travis.yml | 2 +- configure.ac | 51 -- src/lxc/Makefile.am | 13 - src/lxc/cgroups/cgfs.c | 1 - src/lxc/cgroups/cgfsng.c | 3 +- src/lxc/cgroups/cgmanager.c | 1683 ----------------------------------- 6 files changed, 2 insertions(+), 1751 deletions(-) delete mode 100644 src/lxc/cgroups/cgmanager.c diff --git a/.travis.yml b/.travis.yml index 9595ae510..d078b0222 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ before_install: - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- - sudo add-apt-repository ppa:ubuntu-lxc/daily -y - sudo apt-get update -qq - - sudo apt-get install -qq libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls-dev liblua5.2-dev libselinux1-dev libcgmanager-dev + - sudo apt-get install -qq libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls-dev liblua5.2-dev libselinux1-dev script: - ./autogen.sh - rm -Rf build diff --git a/configure.ac b/configure.ac index b56b35b1a..224e1f15e 100644 --- a/configure.ac +++ b/configure.ac @@ -305,45 +305,6 @@ 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],[-lnih -lnih-dbus -ldbus-1]) -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]) - ]) - -AC_MSG_CHECKING(for get_pid_cgroup_abs_sync) -save_LIBS=$LIBS -AC_SEARCH_LIBS([cgmanager_get_pid_cgroup_abs_sync], [cgmanager], [have_abs_cgroups=yes], [have_abs_cgroups=no], [-lnih -lnih-dbus -ldbus-1]) -LIBS=$save_LIBS -if test "x$have_abs_cgroups" = "xyes"; then - AC_DEFINE([HAVE_CGMANAGER_GET_PID_CGROUP_ABS_SYNC], 1, [Have cgmanager_get_pid_cgroup_abs_sync]) - AC_MSG_RESULT([yes]) -else - AC_MSG_RESULT([no]) -fi - -AC_MSG_CHECKING(for cgmanager_list_controllers) -save_LIBS=$LIBS -AC_SEARCH_LIBS([cgmanager_list_controllers_sync], [cgmanager], [have_list_controllers=yes], [have_list_controllers=no], [-lnih -lnih-dbus -ldbus-1]) -LIBS=$save_LIBS -if test "x$have_list_controllers" = "xyes"; then - AC_DEFINE([HAVE_CGMANAGER_LIST_CONTROLLERS], 1, [Have cgmanager_list_controllers]) - AC_MSG_RESULT([yes]) -else - AC_MSG_RESULT([no]) -fi - AC_MSG_CHECKING(for static libcap) # Check for static libcap, make sure the function checked for differs from the # the one checked below so the cache doesn't give a wrong answer @@ -664,7 +625,6 @@ AC_CHECK_FUNCS([setns pivot_root sethostname unshare rand_r confstr faccessat ge # Check for some functions AC_CHECK_LIB(pthread, main) -AC_CHECK_FUNCS(pthread_atfork) AC_CHECK_FUNCS(statvfs) AC_CHECK_LIB(util, openpty) AC_CHECK_FUNCS([openpty hasmntopt setmntent endmntent utmpxname]) @@ -965,7 +925,6 @@ Security features: - Linux capabilities: $enable_capabilities - seccomp: $enable_seccomp - SELinux: $enable_selinux - - cgmanager: $enable_cgmanager Bindings: - lua: $enable_lua @@ -983,13 +942,3 @@ Debugging: Paths: - Logs in configpath: $enable_configpath_log EOF - -if test "x$ac_cv_func_pthread_atfork" = "xno" ; then -cat << EOF - -WARNING: Threading not supported on your platform - - You are compiling LXC for bionic target which lacks certain threading related functionality used by LXC API (like pthread_atfork). - Please note that, because of the missing functionality, multithreaded usage of LXC API cause some problems. -EOF -fi diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index f56436bbc..1cc986275 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -136,10 +136,6 @@ liblxc_la_SOURCES = \ \ $(LSM_SOURCES) -if ENABLE_CGMANAGER -liblxc_la_SOURCES += cgroups/cgmanager.c -endif - if IS_BIONIC liblxc_la_SOURCES += \ ../include/ifaddrs.c ../include/ifaddrs.h \ @@ -182,10 +178,6 @@ if ENABLE_APPARMOR AM_CFLAGS += -DHAVE_APPARMOR endif -if ENABLE_CGMANAGER -AM_CFLAGS += -DHAVE_CGMANAGER -endif - if ENABLE_SELINUX AM_CFLAGS += -DHAVE_SELINUX endif @@ -209,11 +201,6 @@ liblxc_la_LDFLAGS = \ liblxc_la_LIBADD = $(CAP_LIBS) $(SELINUX_LIBS) $(SECCOMP_LIBS) -if ENABLE_CGMANAGER -liblxc_la_LIBADD += $(CGMANAGER_LIBS) $(DBUS_LIBS) $(NIH_LIBS) $(NIH_DBUS_LIBS) -liblxc_la_CFLAGS += $(CGMANAGER_CFLAGS) $(DBUS_CFLAGS) $(NIH_CFLAGS) $(NIH_DBUS_CFLAGS) -endif - bin_SCRIPTS = cmd/lxc-checkconfig \ cmd/lxc-update-config diff --git a/src/lxc/cgroups/cgfs.c b/src/lxc/cgroups/cgfs.c index a2630efa4..338097f4d 100644 --- a/src/lxc/cgroups/cgfs.c +++ b/src/lxc/cgroups/cgfs.c @@ -103,7 +103,6 @@ struct cgroup_mount_point { * 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; diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c index 4c4dda61c..050b397df 100644 --- a/src/lxc/cgroups/cgfsng.c +++ b/src/lxc/cgroups/cgfsng.c @@ -26,8 +26,7 @@ * cgroup backend. The original cgfs.c was designed to be as flexible * as possible. It would try to find cgroup filesystems no matter where * or how you had them mounted, and deduce the most usable mount for - * each controller. It also was not designed for unprivileged use, as - * that was reserved for cgmanager. + * each controller. * * This new implementation assumes that cgroup filesystems are mounted * under /sys/fs/cgroup/clist where clist is either the controller, or diff --git a/src/lxc/cgroups/cgmanager.c b/src/lxc/cgroups/cgmanager.c deleted file mode 100644 index c23443c9f..000000000 --- a/src/lxc/cgroups/cgmanager.c +++ /dev/null @@ -1,1683 +0,0 @@ -/* - * 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 - */ -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "error.h" -#include "commands.h" -#include "list.h" -#include "namespace.h" -#include "conf.h" -#include "utils.h" -#include "log.h" -#include "cgroup.h" -#include "start.h" -#include "state.h" -#include "storage.h" - -#define CGM_SUPPORTS_GET_ABS 3 -#define CGM_SUPPORTS_NAMED 4 -#define CGM_SUPPORTS_MULT_CONTROLLERS 10 - -#ifdef HAVE_CGMANAGER -lxc_log_define(lxc_cgmanager, lxc); - -#include -#include -#include -#include -#include - -struct cgm_data { - char *name; - char *cgroup_path; - const char *cgroup_pattern; -}; - -static pthread_mutex_t cgm_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void lock_mutex(pthread_mutex_t *l) -{ - int ret; - - if ((ret = pthread_mutex_lock(l)) != 0) { - fprintf(stderr, "pthread_mutex_lock returned:%d %s\n", ret, strerror(ret)); - exit(1); - } -} - -static void unlock_mutex(pthread_mutex_t *l) -{ - int ret; - - if ((ret = pthread_mutex_unlock(l)) != 0) { - fprintf(stderr, "%s: pthread_mutex_unlock returned:%d %s\n", - __FILE__, ret, strerror(ret)); - exit(1); - } -} - -void cgm_lock(void) -{ - lock_mutex(&cgm_mutex); -} - -void cgm_unlock(void) -{ - unlock_mutex(&cgm_mutex); -} - -#ifdef HAVE_PTHREAD_ATFORK -__attribute__((constructor)) -static void process_lock_setup_atfork(void) -{ - pthread_atfork(cgm_lock, cgm_unlock, cgm_unlock); -} -#endif - -static NihDBusProxy *cgroup_manager = NULL; -static int32_t api_version; - -static struct cgroup_ops cgmanager_ops; -static int nr_subsystems; -static char **subsystems, **subsystems_inone; -static bool dbus_threads_initialized = false; -static void cull_user_controllers(void); - -static void cgm_dbus_disconnect(void) -{ - if (cgroup_manager) { - dbus_connection_flush(cgroup_manager->connection); - dbus_connection_close(cgroup_manager->connection); - nih_free(cgroup_manager); - } - cgroup_manager = NULL; - cgm_unlock(); -} - -#define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock" -static bool cgm_dbus_connect(void) -{ - DBusError dbus_error; - static DBusConnection *connection; - - cgm_lock(); - if (!dbus_threads_initialized) { - /* tell dbus to do struct locking for thread safety */ - dbus_threads_init_default(); - dbus_threads_initialized = true; - } - - dbus_error_init(&dbus_error); - - connection = dbus_connection_open_private(CGMANAGER_DBUS_SOCK, &dbus_error); - if (!connection) { - DEBUG("Failed opening dbus connection: %s: %s", - dbus_error.name, dbus_error.message); - dbus_error_free(&dbus_error); - cgm_unlock(); - 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); - cgm_dbus_disconnect(); - return false; - } - - /* get the api version */ - if (cgmanager_get_api_version_sync(NULL, cgroup_manager, &api_version) != 0) { - NihError *nerr; - nerr = nih_error_get(); - ERROR("Error cgroup manager api version: %s", nerr->message); - nih_free(nerr); - cgm_dbus_disconnect(); - return false; - } - if (api_version < CGM_SUPPORTS_NAMED) - cull_user_controllers(); - return true; -} - -static bool cgm_all_controllers_same; - -/* - * Check whether we can use "all" when talking to cgmanager. - * We check two things: - * 1. whether cgmanager is new enough to support this. - * 2. whether the task we are interested in is in the same - * cgroup for all controllers. - * In cgm_init (before an lxc-start) we care about our own - * cgroup. In cgm_attach, we care about the target task's - * cgroup. - */ -static void check_supports_multiple_controllers(pid_t pid) -{ - FILE *f; - char *line = NULL, *prevpath = NULL; - size_t sz = 0; - char path[100]; - - cgm_all_controllers_same = false; - - if (pid == -1) - sprintf(path, "/proc/self/cgroup"); - else - sprintf(path, "/proc/%d/cgroup", pid); - f = fopen(path, "r"); - if (!f) - return; - - cgm_all_controllers_same = true; - - while (getline(&line, &sz, f) != -1) { - /* file format: hierarchy:subsystems:group */ - char *colon; - if (!line[0]) - continue; - - colon = strchr(line, ':'); - if (!colon) - continue; - colon = strchr(colon+1, ':'); - if (!colon) - continue; - colon++; - if (!prevpath) { - prevpath = alloca(strlen(colon)+1); - strcpy(prevpath, colon); - continue; - } - if (strcmp(prevpath, colon) != 0) { - cgm_all_controllers_same = false; - break; - } - } - - fclose(f); - free(line); -} - -static int send_creds(int sock, int rpid, int ruid, int rgid) -{ - struct msghdr msg = { 0 }; - struct iovec iov; - struct cmsghdr *cmsg; - struct ucred cred = { - .pid = rpid, - .uid = ruid, - .gid = rgid, - }; - char cmsgbuf[CMSG_SPACE(sizeof(cred))]; - char buf[1]; - buf[0] = 'p'; - - msg.msg_control = cmsgbuf; - msg.msg_controllen = sizeof(cmsgbuf); - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_CREDENTIALS; - memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred)); - - msg.msg_name = NULL; - msg.msg_namelen = 0; - - iov.iov_base = buf; - iov.iov_len = sizeof(buf); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - if (sendmsg(sock, &msg, 0) < 0) - return -1; - return 0; -} - -static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path, int32_t *existed) -{ - bool ret = true; - if ( cgmanager_create_sync(NULL, cgroup_manager, controller, - cgroup_path, existed) != 0) { - NihError *nerr; - nerr = nih_error_get(); - ERROR("call to cgmanager_create_sync failed: %s", nerr->message); - nih_free(nerr); - ERROR("Failed to create %s:%s", controller, cgroup_path); - ret = false; - } - - return ret; -} - -/* - * Escape to the root cgroup if we are root, so that the container will - * be in "/lxc/c1" rather than "/user/..../c1" - * called internally with connection already open - */ -static bool cgm_escape(void *hdata) -{ - bool ret = true, cgm_needs_disconnect = false; - pid_t me = lxc_raw_getpid(); - char **slist = subsystems; - int i; - - if (!cgroup_manager) { - if (!cgm_dbus_connect()) { - ERROR("Error connecting to cgroup manager"); - return false; - } - cgm_needs_disconnect = true; - } - - - if (cgm_all_controllers_same) - slist = subsystems_inone; - - for (i = 0; slist[i]; i++) { - if (cgmanager_move_pid_abs_sync(NULL, cgroup_manager, - slist[i], "/", me) != 0) { - NihError *nerr; - nerr = nih_error_get(); - ERROR("call to cgmanager_move_pid_abs_sync(%s) failed: %s", - slist[i], nerr->message); - nih_free(nerr); - ret = false; - break; - } - } - - if (cgm_needs_disconnect) - cgm_dbus_disconnect(); - - return ret; -} - -static int cgm_num_hierarchies(void) -{ - /* not implemented */ - return -1; -} - -static bool cgm_get_hierarchies(int i, char ***out) -{ - /* not implemented */ - return false; -} - -struct chown_data { - const char *cgroup_path; - uid_t origuid; -}; - -static int do_chown_cgroup(const char *controller, const char *cgroup_path, - uid_t newuid) -{ - int sv[2] = {-1, -1}, optval = 1, ret = -1; - pid_t pid_self; - char buf[1]; - struct pollfd fds; - - if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) { - SYSERROR("Error creating socketpair"); - goto out; - } - if (setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) { - SYSERROR("setsockopt failed"); - goto out; - } - if (setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) { - SYSERROR("setsockopt failed"); - goto out; - } - if ( cgmanager_chown_scm_sync(NULL, cgroup_manager, controller, - cgroup_path, sv[1]) != 0) { - NihError *nerr; - nerr = nih_error_get(); - ERROR("call to cgmanager_chown_scm_sync failed: %s", nerr->message); - nih_free(nerr); - goto out; - } - /* now send credentials */ - - fds.fd = sv[0]; - fds.events = POLLIN; - fds.revents = 0; - if (poll(&fds, 1, -1) <= 0) { - ERROR("Error getting go-ahead from server: %s", strerror(errno)); - goto out; - } - if (read(sv[0], &buf, 1) != 1) { - ERROR("Error getting reply from server over socketpair"); - goto out; - } - - pid_self = lxc_raw_getpid(); - if (send_creds(sv[0], pid_self, getuid(), getgid())) { - SYSERROR("Error sending pid over SCM_CREDENTIAL"); - goto out; - } - fds.fd = sv[0]; - fds.events = POLLIN; - fds.revents = 0; - if (poll(&fds, 1, -1) <= 0) { - ERROR("Error getting go-ahead from server: %s", strerror(errno)); - goto out; - } - if (read(sv[0], &buf, 1) != 1) { - ERROR("Error getting reply from server over socketpair"); - goto out; - } - if (send_creds(sv[0], pid_self, newuid, 0)) { - SYSERROR("Error sending pid over SCM_CREDENTIAL"); - goto out; - } - fds.fd = sv[0]; - fds.events = POLLIN; - fds.revents = 0; - if (poll(&fds, 1, -1) <= 0) { - ERROR("Error getting go-ahead from server: %s", strerror(errno)); - goto out; - } - ret = read(sv[0], buf, 1); -out: - close(sv[0]); - close(sv[1]); - if (ret == 1 && *buf == '1') - return 0; - return -1; -} - -static int chown_cgroup_wrapper(void *data) -{ - struct chown_data *arg = data; - char **slist = subsystems; - int i, ret = -1; - uid_t destuid; - - if (setresgid(0,0,0) < 0) - SYSERROR("Failed to setgid to 0"); - if (setresuid(0,0,0) < 0) - SYSERROR("Failed to setuid to 0"); - if (setgroups(0, NULL) < 0) - SYSERROR("Failed to clear groups"); - cgm_dbus_disconnect(); - if (!cgm_dbus_connect()) { - ERROR("Error connecting to cgroup manager"); - return -1; - } - destuid = get_ns_uid(arg->origuid); - - if (cgm_all_controllers_same) - slist = subsystems_inone; - - for (i = 0; slist[i]; i++) { - if (do_chown_cgroup(slist[i], arg->cgroup_path, destuid) < 0) { - ERROR("Failed to chown %s:%s to container root", - slist[i], arg->cgroup_path); - goto fail; - } - } - ret = 0; -fail: - cgm_dbus_disconnect(); - return ret; -} - -/* Internal helper. Must be called with the cgmanager dbus socket open */ -static bool lxc_cgmanager_chmod(const char *controller, - const char *cgroup_path, const char *file, int mode) -{ - if (cgmanager_chmod_sync(NULL, cgroup_manager, controller, - cgroup_path, file, mode) != 0) { - NihError *nerr; - nerr = nih_error_get(); - ERROR("call to cgmanager_chmod_sync failed: %s", nerr->message); - nih_free(nerr); - return false; - } - return true; -} - -/* Internal helper. Must be called with the cgmanager dbus socket open */ -static bool chown_cgroup(const char *cgroup_path, struct lxc_conf *conf) -{ - struct chown_data data; - char **slist = subsystems; - int i; - - if (lxc_list_empty(&conf->id_map)) - /* If there's no mapping then we don't need to chown */ - return true; - - data.cgroup_path = cgroup_path; - data.origuid = geteuid(); - - /* Unpriv users can't chown it themselves, so chown from - * a child namespace mapping both our own and the target uid - */ - if (userns_exec_1(conf, chown_cgroup_wrapper, &data, - "chown_cgroup_wrapper") < 0) { - ERROR("Error requesting cgroup chown in new namespace"); - return false; - } - - /* - * Now chmod 775 the directory else the container cannot create cgroups. - * This can't be done in the child namespace because it only group-owns - * the cgroup - */ - if (cgm_all_controllers_same) - slist = subsystems_inone; - - for (i = 0; slist[i]; i++) { - if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "", 0775)) - return false; - if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "tasks", 0664)) - return false; - if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "cgroup.procs", 0664)) - return false; - } - - return true; -} - -#define CG_REMOVE_RECURSIVE 1 -/* Internal helper. Must be called with the cgmanager dbus socket open */ -static void cgm_remove_cgroup(const char *controller, const char *path) -{ - int existed; - if ( cgmanager_remove_sync(NULL, cgroup_manager, controller, - path, CG_REMOVE_RECURSIVE, &existed) != 0) { - NihError *nerr; - nerr = nih_error_get(); - ERROR("call to cgmanager_remove_sync failed: %s", nerr->message); - nih_free(nerr); - ERROR("Error removing %s:%s", controller, path); - } - if (existed == -1) - INFO("cgroup removal attempt: %s:%s did not exist", controller, path); -} - -static void *cgm_init(struct lxc_handler *handler) -{ - struct cgm_data *d; - - d = malloc(sizeof(*d)); - if (!d) - return NULL; - - if (!cgm_dbus_connect()) { - ERROR("Error connecting to cgroup manager"); - goto err1; - } - - memset(d, 0, sizeof(*d)); - d->name = strdup(handler->name); - if (!d->name) { - cgm_dbus_disconnect(); - goto err1; - } - - d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern"); - - /* cgm_create immediately gets called so keep the connection open */ - return d; - -err1: - free(d); - return NULL; -} - -/* Called after a failed container startup */ -static void cgm_destroy(void *hdata, struct lxc_conf *conf) -{ - struct cgm_data *d = hdata; - char **slist = subsystems; - int i; - - if (!d || !d->cgroup_path) - return; - if (!cgm_dbus_connect()) { - ERROR("Error connecting to cgroup manager"); - return; - } - - if (cgm_all_controllers_same) - slist = subsystems_inone; - for (i = 0; slist[i]; i++) - cgm_remove_cgroup(slist[i], d->cgroup_path); - - free(d->name); - free(d->cgroup_path); - free(d); - cgm_dbus_disconnect(); -} - -/* - * remove all the cgroups created - * called internally with dbus connection open - */ -static inline void cleanup_cgroups(char *path) -{ - int i; - char **slist = subsystems; - - if (cgm_all_controllers_same) - slist = subsystems_inone; - for (i = 0; slist[i]; i++) - cgm_remove_cgroup(slist[i], path); -} - -static inline bool cgm_create(void *hdata) -{ - struct cgm_data *d = hdata; - char **slist = subsystems; - int i, index=0, baselen, ret; - int32_t existed; - char result[MAXPATHLEN], *tmp, *cgroup_path; - - if (!d) - return false; - - /* 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", d->name, d->cgroup_pattern); - if (!tmp) - goto bad; - if (strlen(tmp) >= MAXPATHLEN) { - free(tmp); - goto bad; - } - 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"); - goto bad; - } - if (index) { - ret = snprintf(result+baselen, MAXPATHLEN-baselen, "-%d", index); - if (ret < 0 || ret >= MAXPATHLEN-baselen) - goto bad; - } - existed = 0; - - if (cgm_all_controllers_same) - slist = subsystems_inone; - - for (i = 0; slist[i]; i++) { - if (!lxc_cgmanager_create(slist[i], tmp, &existed)) { - ERROR("Error creating cgroup %s:%s", slist[i], result); - cleanup_cgroups(tmp); - goto bad; - } - if (existed == 1) - goto next; - } - /* success */ - cgroup_path = strdup(tmp); - if (!cgroup_path) { - cleanup_cgroups(tmp); - goto bad; - } - d->cgroup_path = cgroup_path; - cgm_dbus_disconnect(); - return true; - -next: - index++; - goto again; -bad: - cgm_dbus_disconnect(); - return false; -} - -/* - * 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 - * - * Internal helper, must be called with cgmanager dbus socket open - */ -static bool lxc_cgmanager_enter(pid_t pid, const char *controller, - const char *cgroup_path, bool abs) -{ - int ret; - - if (abs) - ret = cgmanager_move_pid_abs_sync(NULL, cgroup_manager, - controller, cgroup_path, pid); - else - ret = cgmanager_move_pid_sync(NULL, cgroup_manager, - controller, cgroup_path, pid); - if (ret != 0) { - NihError *nerr; - nerr = nih_error_get(); - WARN("call to cgmanager_move_pid_%ssync failed: %s", - abs ? "abs_" : "", nerr->message); - nih_free(nerr); - return false; - } - return true; -} - -static inline bool cgm_enter(void *hdata, pid_t pid) -{ - struct cgm_data *d = hdata; - char **slist = subsystems; - bool ret = false; - int i; - - if (!d || !d->cgroup_path) - return false; - - if (!cgm_dbus_connect()) { - ERROR("Error connecting to cgroup manager"); - return false; - } - - if (cgm_all_controllers_same) - slist = subsystems_inone; - - for (i = 0; slist[i]; i++) { - if (!lxc_cgmanager_enter(pid, slist[i], d->cgroup_path, false)) - goto out; - } - ret = true; -out: - cgm_dbus_disconnect(); - return ret; -} - -static const char *cgm_get_cgroup(void *hdata, const char *subsystem) -{ - struct cgm_data *d = hdata; - - if (!d || !d->cgroup_path) - return NULL; - return d->cgroup_path; -} - -#if HAVE_CGMANAGER_GET_PID_CGROUP_ABS_SYNC -static inline bool abs_cgroup_supported(void) { - return api_version >= CGM_SUPPORTS_GET_ABS; -} -#else -static inline bool abs_cgroup_supported(void) { - return false; -} -#define cgmanager_get_pid_cgroup_abs_sync(...) -1 -#endif - -static char *try_get_abs_cgroup(const char *name, const char *lxcpath, - const char *controller) -{ - char *cgroup = NULL; - - if (abs_cgroup_supported()) { - /* get the container init pid and ask for its abs cgroup */ - pid_t pid = lxc_cmd_get_init_pid(name, lxcpath); - if (pid < 0) - return NULL; - if (cgmanager_get_pid_cgroup_abs_sync(NULL, cgroup_manager, - controller, pid, &cgroup) != 0) { - cgroup = NULL; - NihError *nerr; - nerr = nih_error_get(); - nih_free(nerr); - } else - prune_init_scope(cgroup); - return cgroup; - } - - /* use the command interface to look for the cgroup */ - return lxc_cmd_get_cgroup_path(name, lxcpath, controller); -} - -/* - * nrtasks is called by the utmp helper by the container monitor. - * cgmanager socket was closed after cgroup setup was complete, so we need - * to reopen here. - * - * Return -1 on error. - */ -static int cgm_get_nrtasks(void *hdata) -{ - struct cgm_data *d = hdata; - int32_t *pids; - size_t pids_len; - - if (!d || !d->cgroup_path) - return -1; - - if (!cgm_dbus_connect()) { - ERROR("Error connecting to cgroup manager"); - return -1; - } - if (cgmanager_get_tasks_sync(NULL, cgroup_manager, subsystems[0], - d->cgroup_path, &pids, &pids_len) != 0) { - NihError *nerr; - nerr = nih_error_get(); - ERROR("call to cgmanager_get_tasks_sync failed: %s", nerr->message); - nih_free(nerr); - pids_len = -1; - goto out; - } - nih_free(pids); -out: - cgm_dbus_disconnect(); - return pids_len; -} - -#if HAVE_CGMANAGER_LIST_CONTROLLERS -static bool lxc_list_controllers(char ***list) -{ - if (!cgm_dbus_connect()) { - ERROR("Error connecting to cgroup manager"); - return false; - } - if (cgmanager_list_controllers_sync(NULL, cgroup_manager, list) != 0) { - NihError *nerr; - nerr = nih_error_get(); - ERROR("call to cgmanager_list_controllers_sync failed: %s", nerr->message); - nih_free(nerr); - cgm_dbus_disconnect(); - return false; - } - - cgm_dbus_disconnect(); - return true; -} -#else -static bool lxc_list_controllers(char ***list) -{ - return false; -} -#endif - -static inline void free_abs_cgroup(char *cgroup) -{ - if (!cgroup) - return; - if (abs_cgroup_supported()) - nih_free(cgroup); - else - free(cgroup); -} - -static void do_cgm_get(const char *name, const char *lxcpath, const char *filename, int outp, bool sendvalue) -{ - char *controller, *key, *cgroup = NULL, *cglast; - int len = -1; - int ret; - nih_local char *result = NULL; - - controller = alloca(strlen(filename)+1); - strcpy(controller, filename); - key = strchr(controller, '.'); - if (!key) { - ret = write(outp, &len, sizeof(len)); - if (ret != sizeof(len)) - WARN("Failed to warn cgm_get of error; parent may hang"); - exit(1); - } - *key = '\0'; - - if (!cgm_dbus_connect()) { - ERROR("Error connecting to cgroup manager"); - ret = write(outp, &len, sizeof(len)); - if (ret != sizeof(len)) - WARN("Failed to warn cgm_get of error; parent may hang"); - exit(1); - } - cgroup = try_get_abs_cgroup(name, lxcpath, controller); - if (!cgroup) { - cgm_dbus_disconnect(); - ret = write(outp, &len, sizeof(len)); - if (ret != sizeof(len)) - WARN("Failed to warn cgm_get of error; parent may hang"); - exit(1); - } - cglast = strrchr(cgroup, '/'); - if (!cglast) { - cgm_dbus_disconnect(); - free_abs_cgroup(cgroup); - ret = write(outp, &len, sizeof(len)); - if (ret != sizeof(len)) - WARN("Failed to warn cgm_get of error; parent may hang"); - exit(1); - } - *cglast = '\0'; - if (!lxc_cgmanager_enter(lxc_raw_getpid(), controller, cgroup, abs_cgroup_supported())) { - WARN("Failed to enter container cgroup %s:%s", controller, cgroup); - ret = write(outp, &len, sizeof(len)); - if (ret != sizeof(len)) - WARN("Failed to warn cgm_get of error; parent may hang"); - cgm_dbus_disconnect(); - free_abs_cgroup(cgroup); - exit(1); - } - if (cgmanager_get_value_sync(NULL, cgroup_manager, controller, cglast+1, filename, &result) != 0) { - NihError *nerr; - nerr = nih_error_get(); - nih_free(nerr); - free_abs_cgroup(cgroup); - cgm_dbus_disconnect(); - ret = write(outp, &len, sizeof(len)); - if (ret != sizeof(len)) - WARN("Failed to warn cgm_get of error; parent may hang"); - exit(1); - } - free_abs_cgroup(cgroup); - cgm_dbus_disconnect(); - len = strlen(result); - ret = write(outp, &len, sizeof(len)); - if (ret != sizeof(len)) { - WARN("Failed to send length to parent"); - exit(1); - } - if (!len || !sendvalue) { - exit(0); - } - ret = write(outp, result, len); - if (ret < 0) - exit(1); - exit(0); -} - -/* cgm_get is called to get container cgroup settings, not during startup */ -static int cgm_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) -{ - pid_t pid; - int p[2], ret, newlen, readlen; - - if (pipe(p) < 0) - return -1; - if ((pid = fork()) < 0) { - close(p[0]); - close(p[1]); - return -1; - } - if (!pid) /* do_cgm_get exits */ - do_cgm_get(name, lxcpath, filename, p[1], len && value); - close(p[1]); - ret = read(p[0], &newlen, sizeof(newlen)); - if (ret != sizeof(newlen)) { - close(p[0]); - ret = -1; - goto out; - } - if (!len || !value) { - close(p[0]); - ret = newlen; - goto out; - } - memset(value, 0, len); - if (newlen < 0) { /* child is reporting an error */ - close(p[0]); - ret = -1; - goto out; - } - if (newlen == 0) { /* empty read */ - close(p[0]); - ret = 0; - goto out; - } - readlen = newlen > len ? len : newlen; - ret = read(p[0], value, readlen); - close(p[0]); - if (ret != readlen) { - ret = -1; - goto out; - } - if (newlen >= len) { - value[len-1] = '\0'; - newlen = len-1; - } else if (newlen+1 < len) { - /* cgmanager doesn't add eol to last entry */ - value[newlen++] = '\n'; - value[newlen] = '\0'; - } - ret = newlen; -out: - if (wait_for_pid(pid)) - WARN("do_cgm_get exited with error"); - return ret; -} - -static void do_cgm_set(const char *name, const char *lxcpath, const char *filename, const char *value, int outp) -{ - char *controller, *key, *cgroup = NULL; - int retval = 0; /* value we are sending to the parent over outp */ - int ret; - char *cglast; - - controller = alloca(strlen(filename)+1); - strcpy(controller, filename); - key = strchr(controller, '.'); - if (!key) { - ret = write(outp, &retval, sizeof(retval)); - if (ret != sizeof(retval)) - WARN("Failed to warn cgm_set of error; parent may hang"); - exit(1); - } - *key = '\0'; - - if (!cgm_dbus_connect()) { - ERROR("Error connecting to cgroup manager"); - ret = write(outp, &retval, sizeof(retval)); - if (ret != sizeof(retval)) - WARN("Failed to warn cgm_set of error; parent may hang"); - exit(1); - } - cgroup = try_get_abs_cgroup(name, lxcpath, controller); - if (!cgroup) { - cgm_dbus_disconnect(); - ret = write(outp, &retval, sizeof(retval)); - if (ret != sizeof(retval)) - WARN("Failed to warn cgm_set of error; parent may hang"); - exit(1); - } - cglast = strrchr(cgroup, '/'); - if (!cglast) { - cgm_dbus_disconnect(); - free_abs_cgroup(cgroup); - ret = write(outp, &retval, sizeof(retval)); - if (ret != sizeof(retval)) - WARN("Failed to warn cgm_set of error; parent may hang"); - exit(1); - } - *cglast = '\0'; - if (!lxc_cgmanager_enter(lxc_raw_getpid(), controller, cgroup, abs_cgroup_supported())) { - ERROR("Failed to enter container cgroup %s:%s", controller, cgroup); - ret = write(outp, &retval, sizeof(retval)); - if (ret != sizeof(retval)) - WARN("Failed to warn cgm_set of error; parent may hang"); - cgm_dbus_disconnect(); - free_abs_cgroup(cgroup); - exit(1); - } - if (cgmanager_set_value_sync(NULL, cgroup_manager, controller, cglast+1, filename, value) != 0) { - NihError *nerr; - nerr = nih_error_get(); - ERROR("Error setting cgroup value %s for %s:%s", filename, controller, cgroup); - ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message); - nih_free(nerr); - free_abs_cgroup(cgroup); - cgm_dbus_disconnect(); - ret = write(outp, &retval, sizeof(retval)); - if (ret != sizeof(retval)) - WARN("Failed to warn cgm_set of error; parent may hang"); - exit(1); - } - free_abs_cgroup(cgroup); - cgm_dbus_disconnect(); - /* tell parent that we are done */ - retval = 1; - ret = write(outp, &retval, sizeof(retval)); - if (ret != sizeof(retval)) { - exit(1); - } - exit(0); -} - -/* cgm_set is called to change cgroup settings, not during startup */ -static int cgm_set(const char *filename, const char *value, const char *name, const char *lxcpath) -{ - pid_t pid; - int p[2], ret, v; - - if (pipe(p) < 0) - return -1; - if ((pid = fork()) < 0) { - close(p[1]); - close(p[0]); - return -1; - } - if (!pid) /* do_cgm_set exits */ - do_cgm_set(name, lxcpath, filename, value, p[1]); - close(p[1]); - ret = read(p[0], &v, sizeof(v)); - close(p[0]); - if (wait_for_pid(pid)) - WARN("do_cgm_set exited with error"); - if (ret != sizeof(v) || !v) - return -1; - return 0; -} - -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 void cull_user_controllers(void) -{ - int i, j; - - for (i = 0; i < nr_subsystems; i++) { - if (strncmp(subsystems[i], "name=", 5) != 0) - continue; - for (j = i; j < nr_subsystems-1; j++) - subsystems[j] = subsystems[j+1]; - nr_subsystems--; - } -} - -/* - * return true if inword is in the comma-delimited list cgroup_use - */ -static bool in_comma_list(const char *inword, const char *cgroup_use) -{ - char *e; - size_t inlen = strlen(inword), len; - - do { - e = strchr(cgroup_use, ','); - len = e ? e - cgroup_use : strlen(cgroup_use); - if (len == inlen && strncmp(inword, cgroup_use, len) == 0) - return true; - cgroup_use = e + 1; - } while (e); - - return false; -} - -/* - * inlist is a comma-delimited list of cgroups; so is checklist. Return - * true if any member of inlist is in checklist. - */ -static bool any_in_comma_list(const char *inlist, const char *checklist) -{ - char *tmp = alloca(strlen(inlist) + 1), *tok, *saveptr = NULL; - - strcpy(tmp, inlist); - for (tok = strtok_r(tmp, ",", &saveptr); tok; tok = strtok_r(NULL, ",", &saveptr)) { - if (in_comma_list(tok, checklist)) - return true; - } - - return false; -} - -static bool in_subsystem_list(const char *c) -{ - int i; - - for (i = 0; i < nr_subsystems; i++) { - if (strcmp(c, subsystems[i]) == 0) - return true; - } - - return false; -} - -/* - * If /etc/lxc/lxc.conf specifies lxc.cgroup.use = "freezer,memory", - * then clear out any other subsystems, and make sure that freezer - * and memory are both enabled - */ -static bool verify_and_prune(const char *cgroup_use) -{ - const char *p; - char *e; - int i, j; - - for (p = cgroup_use; p && *p; p = e + 1) { - e = strchr(p, ','); - if (e) - *e = '\0'; - - if (!in_subsystem_list(p)) { - ERROR("Controller %s required by lxc.cgroup.use but not available\n", p); - return false; - } - - if (e) - *e = ','; - if (!e) - break; - } - - for (i = 0; i < nr_subsystems;) { - if (in_comma_list(subsystems[i], cgroup_use)) { - i++; - continue; - } - free(subsystems[i]); - for (j = i; j < nr_subsystems-1; j++) - subsystems[j] = subsystems[j+1]; - subsystems[nr_subsystems-1] = NULL; - nr_subsystems--; - } - - return true; -} - -static void drop_subsystem(int which) -{ - int i; - - if (which < 0 || which >= nr_subsystems) { - ERROR("code error: dropping invalid subsystem index\n"); - exit(1); - } - - free(subsystems[which]); - /* note - we have nr_subsystems+1 entries, last one a NULL */ - for (i = which; i < nr_subsystems; i++) - subsystems[i] = subsystems[i+1]; - nr_subsystems -= 1; -} - -/* - * Check whether we can create the cgroups we would want - */ -static bool subsys_is_writeable(const char *controller, const char *probe) -{ - int32_t existed; - bool ret = true; - - if ( cgmanager_create_sync(NULL, cgroup_manager, controller, - probe, &existed) != 0) { - NihError *nerr; - nerr = nih_error_get(); - ERROR("call to cgmanager_create_sync failed: %s", nerr->message); - nih_free(nerr); - ERROR("Failed to create %s:%s", controller, probe); - ret = false; - } - - return ret; -} - -static char *get_last_controller_in_list(char *list) -{ - char *p; - - while ((p = strchr(list, ',')) != NULL) - list = p + 1; - - return list; -} - -/* - * Make sure that all the controllers are writeable. - * If any are not, then - * - if they are listed in lxc.cgroup.use, refuse to start - * - else if they are crucial subsystems, refuse to start - * - else warn and do not use them - */ -static bool verify_final_subsystems(const char *cgroup_use) -{ - int i; - bool dropped_any = false; - bool bret = false; - const char *cgroup_pattern; - char tmpnam[50], *probe; - - if (!cgm_dbus_connect()) { - ERROR("Error connecting to cgroup manager"); - return false; - } - - cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern"); - i = snprintf(tmpnam, 50, "lxcprobe-%d", lxc_raw_getpid()); - if (i < 0 || i >= 50) { - ERROR("Attack - format string modified?"); - return false; - } - probe = lxc_string_replace("%n", tmpnam, cgroup_pattern); - if (!probe) - goto out; - - i = 0; - while (i < nr_subsystems) { - char *p = get_last_controller_in_list(subsystems[i]); - - if (!subsys_is_writeable(p, probe)) { - if (is_crucial_cgroup_subsystem(p)) { - ERROR("Cannot write to crucial subsystem %s\n", - subsystems[i]); - goto out; - } - if (cgroup_use && any_in_comma_list(subsystems[i], cgroup_use)) { - ERROR("Cannot write to subsystem %s which is requested in lxc.cgroup.use\n", - subsystems[i]); - goto out; - } - WARN("Cannot write to subsystem %s, continuing with out it\n", - subsystems[i]); - dropped_any = true; - drop_subsystem(i); - } else { - cgm_remove_cgroup(subsystems[i], probe); - i++; - } - } - - if (dropped_any) - cgm_all_controllers_same = false; - bret = true; - -out: - free(probe); - cgm_dbus_disconnect(); - return bret; -} - -static bool collect_subsystems(void) -{ - char *line = NULL; - nih_local char **cgm_subsys_list = NULL; - size_t sz = 0; - FILE *f = NULL; - - if (subsystems) /* already initialized */ - return true; - - subsystems_inone = malloc(2 * sizeof(char *)); - if (!subsystems_inone) - return false; - subsystems_inone[0] = "all"; - subsystems_inone[1] = NULL; - - if (lxc_list_controllers(&cgm_subsys_list)) { - while (cgm_subsys_list[nr_subsystems]) { - char **tmp = NIH_MUST( realloc(subsystems, - (nr_subsystems+2)*sizeof(char *)) ); - tmp[nr_subsystems] = NIH_MUST( - strdup(cgm_subsys_list[nr_subsystems++]) ); - subsystems = tmp; - } - if (nr_subsystems) - subsystems[nr_subsystems] = NULL; - goto collected; - } - - INFO("cgmanager_list_controllers failed, falling back to /proc/self/cgroups"); - f = fopen_cloexec("/proc/self/cgroup", "r"); - if (!f) { - f = fopen_cloexec("/proc/1/cgroup", "r"); - if (!f) - return false; - } - while (getline(&line, &sz, f) != -1) { - /* file format: hierarchy:subsystems:group, - * with multiple subsystems being ,-separated */ - char *slist, *end, *p, *saveptr = NULL, **tmp; - - if (!line[0]) - continue; - - slist = strchr(line, ':'); - if (!slist) - continue; - slist++; - end = strchr(slist, ':'); - if (!end) - continue; - *end = '\0'; - - for (p = strtok_r(slist, ",", &saveptr); - p; - p = strtok_r(NULL, ",", &saveptr)) { - tmp = realloc(subsystems, (nr_subsystems+2)*sizeof(char *)); - if (!tmp) - goto out_free; - - subsystems = tmp; - tmp[nr_subsystems] = strdup(p); - tmp[nr_subsystems+1] = NULL; - if (!tmp[nr_subsystems]) - goto out_free; - nr_subsystems++; - } - } - fclose(f); - f = NULL; - - free(line); - line = NULL; - -collected: - if (!nr_subsystems) { - ERROR("No cgroup subsystems found"); - return false; - } - - /* make sure that cgroup.use can be and is honored */ - const char *cgroup_use = lxc_global_config_value("lxc.cgroup.use"); - if (!cgroup_use && errno != 0) - goto final_verify; - if (cgroup_use) { - if (!verify_and_prune(cgroup_use)) { - free_subsystems(); - return false; - } - subsystems_inone[0] = NIH_MUST( strdup(cgroup_use) ); - cgm_all_controllers_same = false; - } - -final_verify: - return verify_final_subsystems(cgroup_use); - -out_free: - free(line); - if (f) - fclose(f); - free_subsystems(); - return false; -} - -/* - * called during cgroup.c:cgroup_ops_init(), at startup. No threads. - * We check whether we can talk to cgmanager, escape to root cgroup if - * we are root, then close the connection. - */ -struct cgroup_ops *cgm_ops_init(void) -{ - check_supports_multiple_controllers(-1); - if (!collect_subsystems()) - return NULL; - - if (api_version < CGM_SUPPORTS_MULT_CONTROLLERS) - cgm_all_controllers_same = false; - - /* if root, try to escape to root cgroup */ - if (geteuid() == 0 && !cgm_escape(NULL)) { - free_subsystems(); - return NULL; - } - - return &cgmanager_ops; -} - -/* unfreeze is called by the command api after killing a container. */ -static bool cgm_unfreeze(void *hdata) -{ - struct cgm_data *d = hdata; - bool ret = true; - - if (!d || !d->cgroup_path) - return false; - - if (!cgm_dbus_connect()) { - ERROR("Error connecting to cgroup manager"); - return false; - } - if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", d->cgroup_path, - "freezer.state", "THAWED") != 0) { - NihError *nerr; - nerr = nih_error_get(); - ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message); - nih_free(nerr); - ERROR("Error unfreezing %s", d->cgroup_path); - ret = false; - } - cgm_dbus_disconnect(); - return ret; -} - -static bool cgm_setup_limits(void *hdata, struct lxc_conf *conf, bool do_devices) -{ - struct cgm_data *d = hdata; - struct lxc_list *iterator, *sorted_cgroup_settings, *next; - struct lxc_cgroup *cg; - struct lxc_list *cgroup_settings = &conf->cgroup; - bool ret = false; - - if (lxc_list_empty(cgroup_settings)) - return true; - - if (!d || !d->cgroup_path) - return false; - - if (!cgm_dbus_connect()) { - ERROR("Error connecting to cgroup manager"); - return false; - } - - sorted_cgroup_settings = sort_cgroup_settings(cgroup_settings); - if (!sorted_cgroup_settings) { - return false; - } - - lxc_list_for_each(iterator, sorted_cgroup_settings) { - char controller[100], *p; - cg = iterator->elem; - if (do_devices != !strncmp("devices", cg->subsystem, 7)) - continue; - if (strlen(cg->subsystem) > 100) /* i smell a rat */ - goto out; - strcpy(controller, cg->subsystem); - p = strchr(controller, '.'); - if (p) - *p = '\0'; - if (cgmanager_set_value_sync(NULL, cgroup_manager, controller, - d->cgroup_path, cg->subsystem, cg->value) != 0) { - NihError *nerr; - nerr = nih_error_get(); - if (do_devices) { - WARN("call to cgmanager_set_value_sync failed: %s", nerr->message); - nih_free(nerr); - WARN("Error setting cgroup %s:%s limit type %s", controller, - d->cgroup_path, cg->subsystem); - continue; - } - - ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message); - nih_free(nerr); - ERROR("Error setting cgroup %s:%s limit type %s", controller, - d->cgroup_path, cg->subsystem); - goto out; - } - - DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value); - } - - ret = true; - INFO("cgroup limits have been setup"); -out: - lxc_list_for_each_safe(iterator, sorted_cgroup_settings, next) { - lxc_list_del(iterator); - free(iterator); - } - free(sorted_cgroup_settings); - cgm_dbus_disconnect(); - return ret; -} - -static bool cgm_chown(void *hdata, struct lxc_conf *conf) -{ - struct cgm_data *d = hdata; - - if (!d || !d->cgroup_path) - return false; - if (!cgm_dbus_connect()) { - ERROR("Error connecting to cgroup manager"); - return false; - } - if (!chown_cgroup(d->cgroup_path, conf)) - WARN("Failed to chown %s to container root", d->cgroup_path); - cgm_dbus_disconnect(); - return true; -} - -/* - * TODO: this should be re-written to use the get_config_item("lxc.idmap") - * cmd api instead of getting the idmap from c->lxc_conf. The reason is - * that the id_maps may be different if the container was started with a - * -f or -s argument. - * The reason I'm punting on that is because we'll need to parse the - * idmap results. - */ -static bool cgm_attach(const char *name, const char *lxcpath, pid_t pid) -{ - bool pass = true; - char *cgroup = NULL; - char **slist = subsystems; - int i; - - if (!cgm_dbus_connect()) { - ERROR("Error connecting to cgroup manager"); - return false; - } - - for (i = 0; slist[i]; i++) { - cgroup = try_get_abs_cgroup(name, lxcpath, slist[i]); - if (!cgroup) { - ERROR("Failed to get cgroup for controller %s", slist[i]); - cgm_dbus_disconnect(); - return false; - } - - if (!lxc_cgmanager_enter(pid, slist[i], cgroup, abs_cgroup_supported())) { - pass = false; - break; - } - - } - cgm_dbus_disconnect(); - if (!pass) - ERROR("Failed to enter group %s", cgroup); - - free_abs_cgroup(cgroup); - return pass; -} - -static bool cgm_bind_dir(const char *root, const char *dirname) -{ - nih_local char *cgpath = NULL; - - /* /sys should have been mounted by now */ - cgpath = NIH_MUST( nih_strdup(NULL, root) ); - NIH_MUST( nih_strcat(&cgpath, NULL, "/sys/fs/cgroup") ); - - if (!dir_exists(cgpath)) { - ERROR("%s does not exist", cgpath); - return false; - } - - /* mount a tmpfs there so we can create subdirs */ - if (safe_mount("cgroup", cgpath, "tmpfs", 0, "size=10000,mode=755", root)) { - SYSERROR("Failed to mount tmpfs at %s", cgpath); - return false; - } - NIH_MUST( nih_strcat(&cgpath, NULL, "/cgmanager") ); - - if (mkdir(cgpath, 0755) < 0) { - SYSERROR("Failed to create %s", cgpath); - return false; - } - - if (safe_mount(dirname, cgpath, "none", MS_BIND, 0, root)) { - SYSERROR("Failed to bind mount %s to %s", dirname, cgpath); - return false; - } - - return true; -} - -/* - * cgm_mount_cgroup: - * If /sys/fs/cgroup/cgmanager.lower/ exists, bind mount that to - * /sys/fs/cgroup/cgmanager/ in the container. - * Otherwise, if /sys/fs/cgroup/cgmanager exists, bind mount that. - * Else do nothing - */ -#define CGMANAGER_LOWER_SOCK "/sys/fs/cgroup/cgmanager.lower" -#define CGMANAGER_UPPER_SOCK "/sys/fs/cgroup/cgmanager" -static bool cgm_mount_cgroup(void *hdata, const char *root, int type) -{ - if (dir_exists(CGMANAGER_LOWER_SOCK)) - return cgm_bind_dir(root, CGMANAGER_LOWER_SOCK); - if (dir_exists(CGMANAGER_UPPER_SOCK)) - return cgm_bind_dir(root, CGMANAGER_UPPER_SOCK); - /* Host doesn't have cgmanager running? Then how did we get here? */ - return false; -} - -static struct cgroup_ops cgmanager_ops = { - .init = cgm_init, - .destroy = cgm_destroy, - .create = cgm_create, - .enter = cgm_enter, - .create_legacy = NULL, - .get_cgroup = cgm_get_cgroup, - .escape = cgm_escape, - .num_hierarchies = cgm_num_hierarchies, - .get_hierarchies = cgm_get_hierarchies, - .get = cgm_get, - .set = cgm_set, - .unfreeze = cgm_unfreeze, - .setup_limits = cgm_setup_limits, - .name = "cgmanager", - .chown = cgm_chown, - .attach = cgm_attach, - .mount_cgroup = cgm_mount_cgroup, - .nrtasks = cgm_get_nrtasks, - .disconnect = NULL, - .driver = CGMANAGER, -}; -#endif