tree-wide: remove cgmanager

parent e3f0e436
...@@ -18,7 +18,7 @@ before_install: ...@@ -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- - 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 add-apt-repository ppa:ubuntu-lxc/daily -y
- sudo apt-get update -qq - 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: script:
- ./autogen.sh - ./autogen.sh
- rm -Rf build - rm -Rf build
......
...@@ -305,45 +305,6 @@ AM_COND_IF([ENABLE_SECCOMP], ...@@ -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) AC_MSG_CHECKING(for static libcap)
# Check for static libcap, make sure the function checked for differs from the # 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 # 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 ...@@ -664,7 +625,6 @@ AC_CHECK_FUNCS([setns pivot_root sethostname unshare rand_r confstr faccessat ge
# Check for some functions # Check for some functions
AC_CHECK_LIB(pthread, main) AC_CHECK_LIB(pthread, main)
AC_CHECK_FUNCS(pthread_atfork)
AC_CHECK_FUNCS(statvfs) AC_CHECK_FUNCS(statvfs)
AC_CHECK_LIB(util, openpty) AC_CHECK_LIB(util, openpty)
AC_CHECK_FUNCS([openpty hasmntopt setmntent endmntent utmpxname]) AC_CHECK_FUNCS([openpty hasmntopt setmntent endmntent utmpxname])
...@@ -965,7 +925,6 @@ Security features: ...@@ -965,7 +925,6 @@ Security features:
- Linux capabilities: $enable_capabilities - Linux capabilities: $enable_capabilities
- seccomp: $enable_seccomp - seccomp: $enable_seccomp
- SELinux: $enable_selinux - SELinux: $enable_selinux
- cgmanager: $enable_cgmanager
Bindings: Bindings:
- lua: $enable_lua - lua: $enable_lua
...@@ -983,13 +942,3 @@ Debugging: ...@@ -983,13 +942,3 @@ Debugging:
Paths: Paths:
- Logs in configpath: $enable_configpath_log - Logs in configpath: $enable_configpath_log
EOF 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
...@@ -136,10 +136,6 @@ liblxc_la_SOURCES = \ ...@@ -136,10 +136,6 @@ liblxc_la_SOURCES = \
\ \
$(LSM_SOURCES) $(LSM_SOURCES)
if ENABLE_CGMANAGER
liblxc_la_SOURCES += cgroups/cgmanager.c
endif
if IS_BIONIC if IS_BIONIC
liblxc_la_SOURCES += \ liblxc_la_SOURCES += \
../include/ifaddrs.c ../include/ifaddrs.h \ ../include/ifaddrs.c ../include/ifaddrs.h \
...@@ -182,10 +178,6 @@ if ENABLE_APPARMOR ...@@ -182,10 +178,6 @@ if ENABLE_APPARMOR
AM_CFLAGS += -DHAVE_APPARMOR AM_CFLAGS += -DHAVE_APPARMOR
endif endif
if ENABLE_CGMANAGER
AM_CFLAGS += -DHAVE_CGMANAGER
endif
if ENABLE_SELINUX if ENABLE_SELINUX
AM_CFLAGS += -DHAVE_SELINUX AM_CFLAGS += -DHAVE_SELINUX
endif endif
...@@ -209,11 +201,6 @@ liblxc_la_LDFLAGS = \ ...@@ -209,11 +201,6 @@ liblxc_la_LDFLAGS = \
liblxc_la_LIBADD = $(CAP_LIBS) $(SELINUX_LIBS) $(SECCOMP_LIBS) 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 \ bin_SCRIPTS = cmd/lxc-checkconfig \
cmd/lxc-update-config cmd/lxc-update-config
......
...@@ -103,7 +103,6 @@ struct cgroup_mount_point { ...@@ -103,7 +103,6 @@ struct cgroup_mount_point {
* hierarchies * hierarchies
* *
* Note this is the per-process info tracked by the cgfs_ops. * 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 {
struct cgroup_process_info *next; struct cgroup_process_info *next;
......
...@@ -26,8 +26,7 @@ ...@@ -26,8 +26,7 @@
* cgroup backend. The original cgfs.c was designed to be as flexible * cgroup backend. The original cgfs.c was designed to be as flexible
* as possible. It would try to find cgroup filesystems no matter where * 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 * or how you had them mounted, and deduce the most usable mount for
* each controller. It also was not designed for unprivileged use, as * each controller.
* that was reserved for cgmanager.
* *
* This new implementation assumes that cgroup filesystems are mounted * This new implementation assumes that cgroup filesystems are mounted
* under /sys/fs/cgroup/clist where clist is either the controller, or * under /sys/fs/cgroup/clist where clist is either the controller, or
......
/*
* 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment