Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
L
lxc
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Chen Yisong
lxc
Commits
0e7ff52c
Unverified
Commit
0e7ff52c
authored
Feb 13, 2018
by
Christian Brauner
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
tree-wide: remove cgmanager
Signed-off-by:
Christian Brauner
<
christian.brauner@ubuntu.com
>
parent
e3f0e436
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
2 additions
and
1751 deletions
+2
-1751
.travis.yml
.travis.yml
+1
-1
configure.ac
configure.ac
+0
-51
Makefile.am
src/lxc/Makefile.am
+0
-13
cgfs.c
src/lxc/cgroups/cgfs.c
+0
-1
cgfsng.c
src/lxc/cgroups/cgfsng.c
+1
-2
cgmanager.c
src/lxc/cgroups/cgmanager.c
+0
-1683
No files found.
.travis.yml
View file @
0e7ff52c
...
...
@@ -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
...
...
configure.ac
View file @
0e7ff52c
...
...
@@ -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
src/lxc/Makefile.am
View file @
0e7ff52c
...
...
@@ -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
...
...
src/lxc/cgroups/cgfs.c
View file @
0e7ff52c
...
...
@@ -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
;
...
...
src/lxc/cgroups/cgfsng.c
View file @
0e7ff52c
...
...
@@ -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
...
...
src/lxc/cgroups/cgmanager.c
deleted
100644 → 0
View file @
e3f0e436
/*
* 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
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment