[lxc-devel] [lxc/master] lxclock: remove atfork handlers

brauner on Github lxc-bot at linuxcontainers.org
Tue Feb 13 04:42:25 UTC 2018


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 704 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180213/f19bdfc3/attachment.bin>
-------------- next part --------------
From e3f0e4368fa0ef0c8b7d334c876bc1b029385ec8 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sat, 10 Feb 2018 23:25:18 +0100
Subject: [PATCH 1/2] lxclock: remove pthread_atfork_handlers

They shouldn't be needed anymore.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/attach.c  |  9 +++------
 src/lxc/lxclock.c | 17 -----------------
 2 files changed, 3 insertions(+), 23 deletions(-)

diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index c3bb8bfb0..28c2cadfd 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -1242,13 +1242,10 @@ int lxc_attach(const char *name, const char *lxcpath,
 		return -1;
 	}
 
-	/* Create intermediate subprocess, three reasons:
-	 *       1. Runs all pthread_atfork handlers and the child will no
-	 *          longer be threaded (we can't properly setns() in a threaded
-	 *          process).
-	 *       2. We can't setns() in the child itself, since we want to make
+	/* Create intermediate subprocess, two reasons:
+	 *       1. We can't setns() in the child itself, since we want to make
 	 *          sure we are properly attached to the pidns.
-	 *       3. Also, the initial thread has to put the attached process
+	 *       2. Also, the initial thread has to put the attached process
 	 *          into the cgroup, which we can only do if we didn't already
 	 *          setns() (otherwise, user namespaces will hate us).
 	 */
diff --git a/src/lxc/lxclock.c b/src/lxc/lxclock.c
index dee5aa5f0..87496f681 100644
--- a/src/lxc/lxclock.c
+++ b/src/lxc/lxclock.c
@@ -317,23 +317,6 @@ void process_unlock(void)
 	unlock_mutex(&thread_mutex);
 }
 
-/* One thread can do fork() while another one is holding a mutex.
- * There is only one thread in child just after the fork(), so no one will ever release that mutex.
- * We setup a "child" fork handler to unlock the mutex just after the fork().
- * For several mutex types, unlocking an unlocked mutex can lead to undefined behavior.
- * One way to deal with it is to setup "prepare" fork handler
- * to lock the mutex before fork() and both "parent" and "child" fork handlers
- * to unlock the mutex.
- * This forbids doing fork() while explicitly holding the lock.
- */
-#ifdef HAVE_PTHREAD_ATFORK
-__attribute__((constructor))
-static void process_lock_setup_atfork(void)
-{
-	pthread_atfork(process_lock, process_unlock, process_unlock);
-}
-#endif
-
 int container_mem_lock(struct lxc_container *c)
 {
 	return lxclock(c->privlock, 0);

From 0e7ff52c929ca8a93e1d090c20b5d78336a53f43 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Tue, 13 Feb 2018 05:37:48 +0100
Subject: [PATCH 2/2] tree-wide: remove cgmanager

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 .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 <daniel.lezcano at free.fr>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <string.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <pthread.h>
-#include <grp.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 <poll.h>
-
-#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 <nih-dbus/dbus_connection.h>
-#include <cgmanager/cgmanager-client.h>
-#include <nih/alloc.h>
-#include <nih/error.h>
-#include <nih/string.h>
-
-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


More information about the lxc-devel mailing list