mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-06 19:30:08 +00:00
Merge git://github.com/lxc/lxc
Signed-off-by: Daniel Lezcano <dlezcano@fr.ibm.com>
This commit is contained in:
commit
2e3ae157d5
46
.gitignore
vendored
46
.gitignore
vendored
@ -24,45 +24,63 @@ libtool
|
|||||||
lxc.spec
|
lxc.spec
|
||||||
lxc.pc
|
lxc.pc
|
||||||
|
|
||||||
templates/lxc-debian
|
|
||||||
templates/lxc-lucid
|
|
||||||
templates/lxc-maverick
|
|
||||||
templates/lxc-natty
|
|
||||||
templates/lxc-oneiric
|
|
||||||
templates/lxc-fedora
|
|
||||||
templates/lxc-altlinux
|
templates/lxc-altlinux
|
||||||
templates/lxc-sshd
|
|
||||||
templates/lxc-busybox
|
|
||||||
templates/lxc-archlinux
|
templates/lxc-archlinux
|
||||||
|
templates/lxc-busybox
|
||||||
|
templates/lxc-debian
|
||||||
|
templates/lxc-fedora
|
||||||
|
templates/lxc-lenny
|
||||||
|
templates/lxc-opensuse
|
||||||
|
templates/lxc-oracle
|
||||||
|
templates/lxc-sshd
|
||||||
|
templates/lxc-ubuntu
|
||||||
|
templates/lxc-ubuntu-cloud
|
||||||
|
|
||||||
|
|
||||||
src/lxc/lxc-attach
|
src/lxc/lxc-attach
|
||||||
src/lxc/lxc-cgroup
|
src/lxc/lxc-cgroup
|
||||||
src/lxc/lxc-checkconfig
|
src/lxc/lxc-checkconfig
|
||||||
src/lxc/lxc-checkpoint
|
src/lxc/lxc-checkpoint
|
||||||
src/lxc/lxc-cinit
|
src/lxc/lxc-clone
|
||||||
src/lxc/lxc-cmd
|
|
||||||
src/lxc/lxc-console
|
src/lxc/lxc-console
|
||||||
src/lxc/lxc-create
|
src/lxc/lxc-create
|
||||||
src/lxc/lxc-destroy
|
src/lxc/lxc-destroy
|
||||||
src/lxc/lxc-enter
|
|
||||||
src/lxc/lxc-exec
|
|
||||||
src/lxc/lxc-execute
|
src/lxc/lxc-execute
|
||||||
src/lxc/lxc-freeze
|
src/lxc/lxc-freeze
|
||||||
src/lxc/lxc-info
|
src/lxc/lxc-info
|
||||||
src/lxc/lxc-init
|
src/lxc/lxc-init
|
||||||
src/lxc/lxc-kill
|
src/lxc/lxc-kill
|
||||||
src/lxc/lxc-ls
|
|
||||||
src/lxc/lxc-monitor
|
src/lxc/lxc-monitor
|
||||||
src/lxc/lxc-netstat
|
src/lxc/lxc-netstat
|
||||||
src/lxc/lxc-setcap
|
|
||||||
src/lxc/lxc-ps
|
src/lxc/lxc-ps
|
||||||
src/lxc/lxc-restart
|
src/lxc/lxc-restart
|
||||||
|
src/lxc/lxc-setcap
|
||||||
|
src/lxc/lxc-setuid
|
||||||
|
src/lxc/lxc-shutdown
|
||||||
src/lxc/lxc-start
|
src/lxc/lxc-start
|
||||||
|
src/lxc/lxc-start-ephemeral
|
||||||
src/lxc/lxc-stop
|
src/lxc/lxc-stop
|
||||||
src/lxc/lxc-unfreeze
|
src/lxc/lxc-unfreeze
|
||||||
src/lxc/lxc-unshare
|
src/lxc/lxc-unshare
|
||||||
src/lxc/lxc-version
|
src/lxc/lxc-version
|
||||||
src/lxc/lxc-wait
|
src/lxc/lxc-wait
|
||||||
|
src/lxc/legacy/lxc-ls
|
||||||
|
|
||||||
|
src/python-lxc/build/
|
||||||
|
src/python-lxc/examples/api_test.py
|
||||||
|
src/python-lxc/lxc/__init__.py
|
||||||
|
src/python-lxc/lxc/__pycache__/
|
||||||
|
|
||||||
|
src/tests/lxc-test-containertests
|
||||||
|
src/tests/lxc-test-createtest
|
||||||
|
src/tests/lxc-test-destroytest
|
||||||
|
src/tests/lxc-test-get_item
|
||||||
|
src/tests/lxc-test-getkeys
|
||||||
|
src/tests/lxc-test-locktests
|
||||||
|
src/tests/lxc-test-saveconfig
|
||||||
|
src/tests/lxc-test-shutdowntest
|
||||||
|
src/tests/lxc-test-startone
|
||||||
|
|
||||||
|
|
||||||
config/compile
|
config/compile
|
||||||
config/config.guess
|
config/config.guess
|
||||||
|
@ -83,5 +83,4 @@ then you just add a line saying
|
|||||||
|
|
||||||
Signed-off-by: Random J Developer <random@developer.org>
|
Signed-off-by: Random J Developer <random@developer.org>
|
||||||
|
|
||||||
using your real name (sorry, no pseudonyms or anonymous
|
using your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||||
contributions.)
|
|
||||||
|
2
COPYING
2
COPYING
@ -500,5 +500,3 @@ necessary. Here is a sample; alter the names:
|
|||||||
Ty Coon, President of Vice
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
That's all there is to it!
|
That's all there is to it!
|
||||||
|
|
||||||
|
|
||||||
|
4
INSTALL
4
INSTALL
@ -10,8 +10,8 @@ unlimited permission to copy, distribute and modify it.
|
|||||||
Basic Installation
|
Basic Installation
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Briefly, the shell commands `./configure; make; make install' should
|
Briefly, the shell commands `./autogen.sh; ./configure; make; make install'
|
||||||
configure, build, and install this package. The following
|
should configure, build, and install this package. The following
|
||||||
more-detailed instructions are generic; see the `README' file for
|
more-detailed instructions are generic; see the `README' file for
|
||||||
instructions specific to this package.
|
instructions specific to this package.
|
||||||
|
|
||||||
|
@ -2,13 +2,17 @@
|
|||||||
|
|
||||||
ACLOCAL_AMFLAGS = -I config
|
ACLOCAL_AMFLAGS = -I config
|
||||||
|
|
||||||
SUBDIRS = src templates doc
|
SUBDIRS = config src templates doc
|
||||||
DIST_SUBDIRS = config src templates doc
|
DIST_SUBDIRS = config src templates doc
|
||||||
EXTRA_DIST = autogen.sh lxc.spec CONTRIBUTING MAINTAINERS ChangeLog
|
EXTRA_DIST = autogen.sh lxc.spec CONTRIBUTING MAINTAINERS ChangeLog
|
||||||
|
|
||||||
pcdatadir = $(libdir)/pkgconfig
|
pcdatadir = $(libdir)/pkgconfig
|
||||||
pcdata_DATA = lxc.pc
|
pcdata_DATA = lxc.pc
|
||||||
|
|
||||||
|
install-data-local:
|
||||||
|
$(MKDIR_P) $(DESTDIR)$(LXCPATH)
|
||||||
|
$(MKDIR_P) $(DESTDIR)$(localstatedir)/cache/lxc
|
||||||
|
|
||||||
ChangeLog::
|
ChangeLog::
|
||||||
@touch ChangeLog
|
@touch ChangeLog
|
||||||
|
|
||||||
|
37
README
37
README
@ -7,7 +7,7 @@ What is lxc:
|
|||||||
kernel. It provides the resource management through the control groups aka
|
kernel. It provides the resource management through the control groups aka
|
||||||
process containers and resource isolation through the namespaces.
|
process containers and resource isolation through the namespaces.
|
||||||
|
|
||||||
The linux containers, lxc, aims to use these new functionnalities to pro-
|
The linux containers, lxc, aims to use these new functionalities to pro-
|
||||||
vide an userspace container object which provides full resource isolation
|
vide an userspace container object which provides full resource isolation
|
||||||
and resource control for an applications or a system.
|
and resource control for an applications or a system.
|
||||||
|
|
||||||
@ -29,9 +29,14 @@ Downloading the current source code:
|
|||||||
You can browse the up to the minute source code and change history online.
|
You can browse the up to the minute source code and change history online.
|
||||||
http://lxc.git.sourceforge.net
|
http://lxc.git.sourceforge.net
|
||||||
|
|
||||||
|
For an even more bleeding edge experience, you may want to look at the
|
||||||
|
staging branch where all changes aimed at the next release land before
|
||||||
|
getting pulled into the master branch.
|
||||||
|
http://github.com/lxc/lxc
|
||||||
|
|
||||||
For detailed build instruction refer to INSTALL and man lxc man page
|
For detailed build instruction refer to INSTALL and man lxc man page
|
||||||
but a short command line should work:
|
but a short command line should work:
|
||||||
./configure && make && sudo make install && sudo lxc-setcap
|
./autogen.sh && ./configure && make && sudo make install && sudo lxc-setcap
|
||||||
preceded by ./autogen.sh if configure do not exist yet.
|
preceded by ./autogen.sh if configure do not exist yet.
|
||||||
|
|
||||||
Getting help:
|
Getting help:
|
||||||
@ -48,7 +53,33 @@ Portability:
|
|||||||
|
|
||||||
lxc is developed and tested on Linux since kernel mainline version 2.6.27
|
lxc is developed and tested on Linux since kernel mainline version 2.6.27
|
||||||
(without network) and 2.6.29 with network isolation.
|
(without network) and 2.6.29 with network isolation.
|
||||||
is compiled with gcc, and supports i686, x86_64, ppc, ppc64, S390 archi.
|
It's compiled with gcc, and should work on most architectures as long as the
|
||||||
|
required kernel features are available. This includes (but isn't limited to):
|
||||||
|
i686, x86_64, ppc, ppc64, S390, armel and armhf.
|
||||||
|
|
||||||
AUTHOR
|
AUTHOR
|
||||||
Daniel Lezcano <daniel.lezcano@free.fr>
|
Daniel Lezcano <daniel.lezcano@free.fr>
|
||||||
|
|
||||||
|
Seccomp with LXC
|
||||||
|
----------------
|
||||||
|
|
||||||
|
To restrict a container with seccomp, you must specify a profile which is
|
||||||
|
basically a whitelist of system calls it may execute. In the container
|
||||||
|
config file, add a line like
|
||||||
|
|
||||||
|
lxc.seccomp = /var/lib/lxc/q1/seccomp.full
|
||||||
|
|
||||||
|
I created a usable (but basically worthless) seccomp.full file using
|
||||||
|
|
||||||
|
cat > seccomp.full << EOF
|
||||||
|
1
|
||||||
|
whitelist
|
||||||
|
EOF
|
||||||
|
for i in `seq 0 300`; do
|
||||||
|
echo $i >> seccomp.full
|
||||||
|
done
|
||||||
|
for i in `seq 1024 1079`; do
|
||||||
|
echo $i >> seccomp.full
|
||||||
|
done
|
||||||
|
|
||||||
|
-- Serge Hallyn <serge.hallyn@ubuntu.com> Fri, 27 Jul 2012 15:47:02 +0600
|
||||||
|
@ -1,2 +1,15 @@
|
|||||||
distclean:
|
configdir = $(sysconfdir)/lxc
|
||||||
|
config_DATA = lxc.conf
|
||||||
|
conffile = @LXC_CONFFILE@
|
||||||
|
|
||||||
|
EXTRA_DIST = lxc.conf.ubuntu lxc.conf.libvirt lxc.conf.unknown
|
||||||
|
|
||||||
|
lxc.conf:
|
||||||
|
cp $(conffile) $@
|
||||||
|
|
||||||
|
clean-local:
|
||||||
|
@$(RM) -f lxc.conf
|
||||||
|
|
||||||
|
distclean-local:
|
||||||
|
@$(RM) -f lxc.conf
|
||||||
@$(RM) -f compile config.guess config.sub depcomp install-sh ltmain.sh missing Makefile.in Makefile
|
@$(RM) -f compile config.guess config.sub depcomp install-sh ltmain.sh missing Makefile.in Makefile
|
||||||
|
3
config/lxc.conf.libvirt
Normal file
3
config/lxc.conf.libvirt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
lxc.network.type = veth
|
||||||
|
lxc.network.link = virbr0
|
||||||
|
lxc.network.flags = up
|
3
config/lxc.conf.ubuntu
Normal file
3
config/lxc.conf.ubuntu
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
lxc.network.type = veth
|
||||||
|
lxc.network.link = lxcbr0
|
||||||
|
lxc.network.flags = up
|
1
config/lxc.conf.unknown
Normal file
1
config/lxc.conf.unknown
Normal file
@ -0,0 +1 @@
|
|||||||
|
lxc.network.type = empty
|
135
configure.ac
135
configure.ac
@ -6,40 +6,119 @@ AC_INIT([lxc], [0.8.0])
|
|||||||
AC_CONFIG_SRCDIR([configure.ac])
|
AC_CONFIG_SRCDIR([configure.ac])
|
||||||
AC_CONFIG_AUX_DIR([config])
|
AC_CONFIG_AUX_DIR([config])
|
||||||
AM_CONFIG_HEADER([src/config.h])
|
AM_CONFIG_HEADER([src/config.h])
|
||||||
AM_INIT_AUTOMAKE([-Wno-portability])
|
AM_INIT_AUTOMAKE([-Wall -Werror -Wno-portability])
|
||||||
AC_CANONICAL_HOST
|
AC_CANONICAL_HOST
|
||||||
AM_PROG_CC_C_O
|
AM_PROG_CC_C_O
|
||||||
AC_GNU_SOURCE
|
AC_GNU_SOURCE
|
||||||
AC_CHECK_PROG(SETCAP, setcap, yes, no, $PATH$PATH_SEPARATOR/sbin)
|
AC_CHECK_PROG(SETCAP, setcap, yes, no, $PATH$PATH_SEPARATOR/sbin)
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([host distribution])
|
||||||
|
AC_ARG_WITH(distro, AS_HELP_STRING([--with-distro=DISTRO], [Specify the Linux distribution to target: One of redhat, oracle, fedora, suse, gentoo, debian, arch, slackware, paldo, mandriva or pardus]))
|
||||||
|
if test "z$with_distro" = "z"; then
|
||||||
|
with_distro=`lsb_release -is`
|
||||||
|
fi
|
||||||
|
if test "z$with_distro" = "z"; then
|
||||||
|
AC_CHECK_FILE(/etc/redhat-release,with_distro="redhat")
|
||||||
|
AC_CHECK_FILE(/etc/oracle-release,with_distro="oracle")
|
||||||
|
AC_CHECK_FILE(/etc/fedora-release,with_distro="fedora")
|
||||||
|
AC_CHECK_FILE(/etc/SuSE-release,with_distro="suse")
|
||||||
|
AC_CHECK_FILE(/etc/gentoo-release,with_distro="gentoo")
|
||||||
|
AC_CHECK_FILE(/etc/debian_version,with_distro="debian")
|
||||||
|
AC_CHECK_FILE(/etc/arch-release,with_distro="arch")
|
||||||
|
AC_CHECK_FILE(/etc/slackware-version,with_distro="slackware")
|
||||||
|
AC_CHECK_FILE(/etc/frugalware-release,with_distro="frugalware")
|
||||||
|
AC_CHECK_FILE(/etc/mandrakelinux-release, with_distro="mandriva")
|
||||||
|
AC_CHECK_FILE(/etc/mandriva-release,with_distro="mandriva")
|
||||||
|
AC_CHECK_FILE(/etc/pardus-release,with_distro="pardus")
|
||||||
|
fi
|
||||||
|
with_distro=`echo ${with_distro} | tr '[[:upper:]]' '[[:lower:]]'`
|
||||||
|
|
||||||
|
if test "z$with_distro" = "z"; then
|
||||||
|
with_distro="unknown"
|
||||||
|
fi
|
||||||
|
case $with_distro in
|
||||||
|
ubuntu)
|
||||||
|
conffile=lxc.conf.ubuntu
|
||||||
|
;;
|
||||||
|
redhat|fedora|oracle|oracleserver)
|
||||||
|
conffile=lxc.conf.libvirt
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -n "Linux distribution network config unknown, defaulting to lxc.network.type = empty"
|
||||||
|
conffile=lxc.conf.unknown
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
AC_MSG_RESULT([$with_distro])
|
||||||
|
|
||||||
|
AM_CONDITIONAL([HAVE_DEBIAN], [test x"$with_distro" = "xdebian" -o x"$with_distro" = "xubuntu"])
|
||||||
|
|
||||||
AC_ARG_ENABLE([rpath],
|
AC_ARG_ENABLE([rpath],
|
||||||
[AC_HELP_STRING([--disable-rpath], [do not set rpath in executables])],
|
[AC_HELP_STRING([--disable-rpath], [do not set rpath in executables])],
|
||||||
[], [enable_rpath=yes])
|
[], [enable_rpath=yes])
|
||||||
|
|
||||||
AM_CONDITIONAL([ENABLE_RPATH], [test "x$enable_rpath" = "xyes"])
|
AM_CONDITIONAL([ENABLE_RPATH], [test "x$enable_rpath" = "xyes"])
|
||||||
|
|
||||||
AC_ARG_ENABLE([apparmor],
|
|
||||||
[AC_HELP_STRING([--enable-apparmor], [enable apparmor])],
|
|
||||||
[], [enable_apparmor=yes])
|
|
||||||
AM_CONDITIONAL([ENABLE_APPARMOR], [test "x$enable_apparmor" = "xyes"])
|
|
||||||
|
|
||||||
AC_ARG_ENABLE([doc],
|
AC_ARG_ENABLE([doc],
|
||||||
[AC_HELP_STRING([--enable-doc], [make mans (require docbook2man installed) [default=auto]])],
|
[AC_HELP_STRING([--enable-doc], [make mans (require docbook2x-man installed) [default=auto]])],
|
||||||
[], [enable_doc=auto])
|
[], [enable_doc=auto])
|
||||||
|
|
||||||
if test "x$enable_doc" = "xyes" -o "x$enable_doc" = "xauto"; then
|
if test "x$enable_doc" = "xyes" -o "x$enable_doc" = "xauto"; then
|
||||||
AC_CHECK_PROG(have_docbook, [docbook2man], [yes], [no])
|
db2xman=""
|
||||||
|
|
||||||
test "x$have_docbook" = "xno" -a "x$enable_doc" = "xyes" && \
|
AC_MSG_CHECKING(for docbook2x-man)
|
||||||
AC_MSG_ERROR([docbook2man required by man request, but not found])
|
for name in docbook2x-man db2x_docbook2man; do
|
||||||
|
if "$name" --help >/dev/null 2>&1; then
|
||||||
|
db2xman="$name"
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if test -n "${db2xman}"; then
|
||||||
|
AC_MSG_RESULT(${db2xman})
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
if test "x$enable_doc" = "xyes"; then
|
||||||
|
AC_MSG_ERROR([docbook2x-man required by man request, but not found])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_SUBST(db2xman)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([apparmor],
|
||||||
|
[AC_HELP_STRING([--enable-apparmor], [enable apparmor])],
|
||||||
|
[], [enable_apparmor=check])
|
||||||
|
|
||||||
|
if test "$enable_apparmor" = "check" ; then
|
||||||
|
AC_CHECK_LIB([apparmor],[aa_change_profile],[enable_apparmor=yes], [enable_apparmor=no])
|
||||||
|
fi
|
||||||
|
|
||||||
|
AM_CONDITIONAL([ENABLE_APPARMOR], [test "x$enable_apparmor" = "xyes"])
|
||||||
|
|
||||||
AM_COND_IF([ENABLE_APPARMOR],
|
AM_COND_IF([ENABLE_APPARMOR],
|
||||||
[AC_CHECK_HEADER([sys/apparmor.h],[],[AC_MSG_ERROR([You must install the AppArmor development package in order to compile lxc])])
|
[AC_CHECK_HEADER([sys/apparmor.h],[],[AC_MSG_ERROR([You must install the AppArmor development package in order to compile lxc])])
|
||||||
AC_CHECK_LIB([apparmor], [aa_change_profile],[],[AC_MSG_ERROR([You must install the AppArmor development package in order to compile lxc])])
|
AC_CHECK_LIB([apparmor], [aa_change_profile],[],[AC_MSG_ERROR([You must install the AppArmor development package in order to compile lxc])])
|
||||||
AC_SUBST([APPARMOR_LIBS], [-lapparmor])])
|
AC_SUBST([APPARMOR_LIBS], [-lapparmor])])
|
||||||
|
|
||||||
AM_CONDITIONAL([ENABLE_DOCBOOK], [test "x$have_docbook" = "xyes"])
|
AC_ARG_ENABLE([seccomp],
|
||||||
|
[AC_HELP_STRING([--enable-seccomp], [enable seccomp])],
|
||||||
|
[], [enable_seccomp=check])
|
||||||
|
|
||||||
|
if test "$enable_seccomp" = "check" ; then
|
||||||
|
AC_CHECK_LIB([seccomp],[seccomp_init],[enable_seccomp=yes],[enable_seccomp=no])
|
||||||
|
fi
|
||||||
|
|
||||||
|
AM_CONDITIONAL([ENABLE_SECCOMP], [test "x$enable_seccomp" = "xyes"])
|
||||||
|
|
||||||
|
AM_COND_IF([ENABLE_SECCOMP],
|
||||||
|
[AC_CHECK_HEADER([seccomp.h],[],[AC_MSG_ERROR([You must install the seccomp development package in order to compile lxc])])
|
||||||
|
AC_CHECK_LIB([seccomp], [seccomp_init],[],[AC_MSG_ERROR([You must install the seccomp development package in order to compile lxc])])
|
||||||
|
AC_SUBST([SECCOMP_LIBS], [-lseccomp])])
|
||||||
|
|
||||||
|
# HAVE_SCMP_FILTER_CTX=1 will tell us we have libseccomp api >= 1.0.0
|
||||||
|
AC_CHECK_TYPES([scmp_filter_ctx], [], [], [#include <seccomp.h>])
|
||||||
|
|
||||||
|
AM_CONDITIONAL([ENABLE_DOCBOOK], [test "x$db2xman" != "x"])
|
||||||
|
|
||||||
AC_ARG_ENABLE([examples],
|
AC_ARG_ENABLE([examples],
|
||||||
[AC_HELP_STRING([--disable-examples], [do not install configuration examples])],
|
[AC_HELP_STRING([--disable-examples], [do not install configuration examples])],
|
||||||
@ -47,6 +126,23 @@ AC_ARG_ENABLE([examples],
|
|||||||
|
|
||||||
AM_CONDITIONAL([ENABLE_EXAMPLES], [test "x$enable_examples" = "xyes"])
|
AM_CONDITIONAL([ENABLE_EXAMPLES], [test "x$enable_examples" = "xyes"])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([python],
|
||||||
|
[AC_HELP_STRING([--enable-python], [enable python binding])],
|
||||||
|
[enable_python=yes], [enable_python=no])
|
||||||
|
|
||||||
|
AM_CONDITIONAL([ENABLE_PYTHON], [test "x$enable_python" = "xyes"])
|
||||||
|
|
||||||
|
AM_COND_IF([ENABLE_PYTHON],
|
||||||
|
[AM_PATH_PYTHON([3.2], [], [AC_MSG_ERROR([You must install python3])])
|
||||||
|
AC_CHECK_HEADER([python$PYTHON_VERSION/Python.h],[],[AC_MSG_ERROR([You must install python3-dev])])
|
||||||
|
AC_DEFINE_UNQUOTED([ENABLE_PYTHON], 1, [Python3 is available])])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([tests],
|
||||||
|
[AC_HELP_STRING([--enable-tests], [build test/example binaries])],
|
||||||
|
[enable_tests=yes], [enable_tests=no])
|
||||||
|
|
||||||
|
AM_CONDITIONAL([ENABLE_TESTS], [test "x$enable_tests" = "xyes"])
|
||||||
|
|
||||||
AS_AC_EXPAND(PREFIX, $prefix)
|
AS_AC_EXPAND(PREFIX, $prefix)
|
||||||
AS_AC_EXPAND(LIBDIR, $libdir)
|
AS_AC_EXPAND(LIBDIR, $libdir)
|
||||||
AS_AC_EXPAND(BINDIR, $bindir)
|
AS_AC_EXPAND(BINDIR, $bindir)
|
||||||
@ -69,12 +165,13 @@ AC_ARG_WITH([rootfs-path],
|
|||||||
[lxc rootfs mount point]
|
[lxc rootfs mount point]
|
||||||
)], [], [with_rootfs_path=['${libdir}/lxc/rootfs']])
|
)], [], [with_rootfs_path=['${libdir}/lxc/rootfs']])
|
||||||
|
|
||||||
|
AS_AC_EXPAND(LXC_CONFFILE, $conffile)
|
||||||
AS_AC_EXPAND(LXC_GENERATE_DATE, "$(date)")
|
AS_AC_EXPAND(LXC_GENERATE_DATE, "$(date)")
|
||||||
AS_AC_EXPAND(LXCPATH, "${with_config_path}")
|
AS_AC_EXPAND(LXCPATH, "${with_config_path}")
|
||||||
AS_AC_EXPAND(LXCROOTFSMOUNT, "${with_rootfs_path}")
|
AS_AC_EXPAND(LXCROOTFSMOUNT, "${with_rootfs_path}")
|
||||||
AS_AC_EXPAND(LXCTEMPLATEDIR, ['${datadir}/lxc/templates'])
|
AS_AC_EXPAND(LXCTEMPLATEDIR, ['${datadir}/lxc/templates'])
|
||||||
|
|
||||||
AC_SUBST(LXCINITDIR, ['${libexecdir}'])
|
AS_AC_EXPAND(LXCINITDIR, ['${libexecdir}'])
|
||||||
|
|
||||||
AC_CHECK_HEADERS([linux/unistd.h linux/netlink.h linux/genetlink.h],
|
AC_CHECK_HEADERS([linux/unistd.h linux/netlink.h linux/genetlink.h],
|
||||||
[],
|
[],
|
||||||
@ -101,9 +198,10 @@ AC_CHECK_DECLS([PR_CAPBSET_DROP], [], [], [#include <sys/prctl.h>])
|
|||||||
AC_CHECK_HEADERS([sys/signalfd.h])
|
AC_CHECK_HEADERS([sys/signalfd.h])
|
||||||
|
|
||||||
AC_PROG_GCC_TRADITIONAL
|
AC_PROG_GCC_TRADITIONAL
|
||||||
|
AC_PROG_SED
|
||||||
|
|
||||||
if test "x$GCC" = "xyes"; then
|
if test "x$GCC" = "xyes"; then
|
||||||
CFLAGS="$CFLAGS -Wall"
|
CFLAGS="$CFLAGS -Wall -Werror"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_CONFIG_FILES([
|
AC_CONFIG_FILES([
|
||||||
@ -134,6 +232,7 @@ AC_CONFIG_FILES([
|
|||||||
doc/lxc.sgml
|
doc/lxc.sgml
|
||||||
doc/common_options.sgml
|
doc/common_options.sgml
|
||||||
doc/see_also.sgml
|
doc/see_also.sgml
|
||||||
|
doc/legacy/lxc-ls.sgml
|
||||||
|
|
||||||
doc/rootfs/Makefile
|
doc/rootfs/Makefile
|
||||||
|
|
||||||
@ -154,6 +253,7 @@ AC_CONFIG_FILES([
|
|||||||
templates/lxc-opensuse
|
templates/lxc-opensuse
|
||||||
templates/lxc-busybox
|
templates/lxc-busybox
|
||||||
templates/lxc-fedora
|
templates/lxc-fedora
|
||||||
|
templates/lxc-oracle
|
||||||
templates/lxc-altlinux
|
templates/lxc-altlinux
|
||||||
templates/lxc-sshd
|
templates/lxc-sshd
|
||||||
templates/lxc-archlinux
|
templates/lxc-archlinux
|
||||||
@ -161,7 +261,6 @@ AC_CONFIG_FILES([
|
|||||||
src/Makefile
|
src/Makefile
|
||||||
src/lxc/Makefile
|
src/lxc/Makefile
|
||||||
src/lxc/lxc-ps
|
src/lxc/lxc-ps
|
||||||
src/lxc/lxc-ls
|
|
||||||
src/lxc/lxc-netstat
|
src/lxc/lxc-netstat
|
||||||
src/lxc/lxc-checkconfig
|
src/lxc/lxc-checkconfig
|
||||||
src/lxc/lxc-setcap
|
src/lxc/lxc-setcap
|
||||||
@ -170,7 +269,15 @@ AC_CONFIG_FILES([
|
|||||||
src/lxc/lxc-create
|
src/lxc/lxc-create
|
||||||
src/lxc/lxc-clone
|
src/lxc/lxc-clone
|
||||||
src/lxc/lxc-shutdown
|
src/lxc/lxc-shutdown
|
||||||
|
src/lxc/lxc-start-ephemeral
|
||||||
src/lxc/lxc-destroy
|
src/lxc/lxc-destroy
|
||||||
|
src/lxc/legacy/lxc-ls
|
||||||
|
|
||||||
|
src/python-lxc/Makefile
|
||||||
|
src/python-lxc/lxc/__init__.py
|
||||||
|
src/python-lxc/examples/api_test.py
|
||||||
|
|
||||||
|
src/tests/Makefile
|
||||||
|
|
||||||
])
|
])
|
||||||
AC_CONFIG_COMMANDS([default],[[]],[[]])
|
AC_CONFIG_COMMANDS([default],[[]],[[]])
|
||||||
|
@ -19,7 +19,6 @@ man_MANS = \
|
|||||||
lxc-unfreeze.1 \
|
lxc-unfreeze.1 \
|
||||||
lxc-monitor.1 \
|
lxc-monitor.1 \
|
||||||
lxc-wait.1 \
|
lxc-wait.1 \
|
||||||
lxc-ls.1 \
|
|
||||||
lxc-ps.1 \
|
lxc-ps.1 \
|
||||||
lxc-cgroup.1 \
|
lxc-cgroup.1 \
|
||||||
lxc-kill.1 \
|
lxc-kill.1 \
|
||||||
@ -29,15 +28,23 @@ man_MANS = \
|
|||||||
\
|
\
|
||||||
lxc.7
|
lxc.7
|
||||||
|
|
||||||
|
if ENABLE_PYTHON
|
||||||
|
man_MANS += lxc-ls.1
|
||||||
|
else
|
||||||
|
man_MANS += legacy/lxc-ls.1
|
||||||
|
endif
|
||||||
|
|
||||||
%.1 : %.sgml
|
%.1 : %.sgml
|
||||||
docbook2man -w all $<
|
$(db2xman) $<
|
||||||
|
test "$(shell basename $@)" != "$@" && mv $(shell basename $@) $@ || true
|
||||||
|
|
||||||
%.5 : %.sgml
|
%.5 : %.sgml
|
||||||
docbook2man -w all $<
|
$(db2xman) $<
|
||||||
|
test "$(shell basename $@)" != "$@" && mv $(shell basename $@) $@ || true
|
||||||
|
|
||||||
%.7 : %.sgml
|
%.7 : %.sgml
|
||||||
docbook2man -w all $<
|
$(db2xman) $<
|
||||||
|
test "$(shell basename $@)" != "$@" && mv $(shell basename $@) $@ || true
|
||||||
|
|
||||||
lxc-%.sgml : common_options.sgml see_also.sgml
|
lxc-%.sgml : common_options.sgml see_also.sgml
|
||||||
|
|
||||||
|
156
doc/legacy/lxc-ls.sgml.in
Normal file
156
doc/legacy/lxc-ls.sgml.in
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
lxc: linux Container library
|
||||||
|
|
||||||
|
(C) Copyright IBM Corp. 2007, 2008
|
||||||
|
|
||||||
|
Authors:
|
||||||
|
Daniel Lezcano <dlezcano at fr.ibm.com>
|
||||||
|
|
||||||
|
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
|
<!ENTITY seealso SYSTEM "@builddir@/../see_also.sgml">
|
||||||
|
]>
|
||||||
|
|
||||||
|
<refentry>
|
||||||
|
|
||||||
|
<docinfo><date>@LXC_GENERATE_DATE@</date></docinfo>
|
||||||
|
|
||||||
|
<refmeta>
|
||||||
|
<refentrytitle>lxc-ls</refentrytitle>
|
||||||
|
<manvolnum>1</manvolnum>
|
||||||
|
</refmeta>
|
||||||
|
|
||||||
|
<refnamediv>
|
||||||
|
<refname>lxc-ls</refname>
|
||||||
|
|
||||||
|
<refpurpose>
|
||||||
|
list the containers existing on the system
|
||||||
|
</refpurpose>
|
||||||
|
</refnamediv>
|
||||||
|
|
||||||
|
<refsynopsisdiv>
|
||||||
|
<cmdsynopsis>
|
||||||
|
<command>lxc-ls</command>
|
||||||
|
<arg choice="opt">--active</arg>
|
||||||
|
<arg choice="opt">ls option</arg>
|
||||||
|
</cmdsynopsis>
|
||||||
|
</refsynopsisdiv>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Description</title>
|
||||||
|
<para>
|
||||||
|
<command>lxc-ls</command> list the containers existing on the
|
||||||
|
system.
|
||||||
|
</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Options</title>
|
||||||
|
<variablelist>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option><optional>--active</optional></option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
List active containers.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option><optional>ls options</optional></option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The option passed to <command>lxc-ls</command> are the
|
||||||
|
same as the <command>ls</command> command.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
</variablelist>
|
||||||
|
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Examples</title>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term>lxc-ls -l</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
list all the container and their permissions.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>lxc-ls --active -1</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
list active containers and display the list in one column.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
</variablelist>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>See Also</title>
|
||||||
|
|
||||||
|
<simpara>
|
||||||
|
<citerefentry>
|
||||||
|
<refentrytitle>ls</refentrytitle>
|
||||||
|
<manvolnum>1</manvolnum>
|
||||||
|
</citerefentry>,
|
||||||
|
</simpara>
|
||||||
|
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
&seealso;
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Author</title>
|
||||||
|
<para>Daniel Lezcano <email>daniel.lezcano@free.fr</email></para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
</refentry>
|
||||||
|
|
||||||
|
<!-- Keep this comment at the end of the file
|
||||||
|
Local variables:
|
||||||
|
mode: sgml
|
||||||
|
sgml-omittag:t
|
||||||
|
sgml-shorttag:t
|
||||||
|
sgml-minimize-attributes:nil
|
||||||
|
sgml-always-quote-attributes:t
|
||||||
|
sgml-indent-step:2
|
||||||
|
sgml-indent-data:t
|
||||||
|
sgml-parent-document:nil
|
||||||
|
sgml-default-dtd-file:nil
|
||||||
|
sgml-exposed-tags:nil
|
||||||
|
sgml-local-catalogs:nil
|
||||||
|
sgml-local-ecat-files:nil
|
||||||
|
End:
|
||||||
|
-->
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
@ -52,6 +52,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
<arg choice="req">-n <replaceable>name</replaceable></arg>
|
<arg choice="req">-n <replaceable>name</replaceable></arg>
|
||||||
<arg choice="opt">-a <replaceable>arch</replaceable></arg>
|
<arg choice="opt">-a <replaceable>arch</replaceable></arg>
|
||||||
<arg choice="opt">-e</arg>
|
<arg choice="opt">-e</arg>
|
||||||
|
<arg choice="opt">-s <replaceable>namespaces</replaceable></arg>
|
||||||
|
<arg choice="opt">-R</arg>
|
||||||
<arg choice="opt">-- <replaceable>command</replaceable></arg>
|
<arg choice="opt">-- <replaceable>command</replaceable></arg>
|
||||||
</cmdsynopsis>
|
</cmdsynopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
@ -125,6 +127,52 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option>-s, --namespaces <replaceable>namespaces</replaceable></option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Specify the namespaces to attach to, as a pipe-separated list,
|
||||||
|
e.g. <replaceable>NETWORK|IPC</replaceable>. Allowed values are
|
||||||
|
<replaceable>MOUNT</replaceable>, <replaceable>PID</replaceable>,
|
||||||
|
<replaceable>UTSNAME</replaceable>, <replaceable>IPC</replaceable>,
|
||||||
|
<replaceable>USER </replaceable> and
|
||||||
|
<replaceable>NETWORK</replaceable>. This allows one to change
|
||||||
|
the context of the process to e.g. the network namespace of the
|
||||||
|
container while retaining the other namespaces as those of the
|
||||||
|
host.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
<emphasis>Important:</emphasis> This option implies
|
||||||
|
<option>-e</option>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option>-R, --remount-sys-proc</option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
When using <option>-s</option> and the mount namespace is not
|
||||||
|
included, this flag will cause <command>lxc-attach</command>
|
||||||
|
to remount <replaceable>/proc</replaceable> and
|
||||||
|
<replaceable>/sys</replaceable> to reflect the current other
|
||||||
|
namespace contexts.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Please see the <emphasis>Notes</emphasis> section for more
|
||||||
|
details.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
This option will be ignored if one tries to attach to the
|
||||||
|
mount namespace anyway.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
@ -147,19 +195,86 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
To deactivate the network link eth1 of a running container that
|
To deactivate the network link eth1 of a running container that
|
||||||
does not have the NET_ADMIN capability, use the <option>-e</option>
|
does not have the NET_ADMIN capability, use either the
|
||||||
option to use increased capabilities:
|
<option>-e</option> option to use increased capabilities,
|
||||||
|
assuming the <command>ip</command> tool is installed:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
lxc-attach -n container -e -- /sbin/ip link delete eth1
|
lxc-attach -n container -e -- /sbin/ip link delete eth1
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
Or, alternatively, use the <option>-s</option> to use the
|
||||||
|
tools installed on the host outside the container:
|
||||||
|
<programlisting>
|
||||||
|
lxc-attach -n container -s NETWORK -- /sbin/ip link delete eth1
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Compatibility</title>
|
||||||
|
<para>
|
||||||
|
Attaching completely (including the pid and mount namespaces) to a
|
||||||
|
container requires a patched kernel, please see the lxc website for
|
||||||
|
details. <command>lxc-attach</command> will fail in that case if
|
||||||
|
used with an unpatched kernel.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Nevertheless, it will succeed on an unpatched kernel of version 3.0
|
||||||
|
or higher if the <option>-s</option> option is used to restrict the
|
||||||
|
namespaces that the process is to be attached to to one or more of
|
||||||
|
<replaceable>NETWORK</replaceable>, <replaceable>IPC</replaceable>
|
||||||
|
and <replaceable>UTSNAME</replaceable>.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Attaching to user namespaces is currently completely unsupported
|
||||||
|
by the kernel. <command>lxc-attach</command> should however be able
|
||||||
|
to do this once once future kernel versions implement this.
|
||||||
|
</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Notes</title>
|
||||||
|
<para>
|
||||||
|
The Linux <replaceable>/proc</replaceable> and
|
||||||
|
<replaceable>/sys</replaceable> filesystems contain information
|
||||||
|
about some quantities that are affected by namespaces, such as
|
||||||
|
the directories named after process ids in
|
||||||
|
<replaceable>/proc</replaceable> or the network interface infromation
|
||||||
|
in <replaceable>/sys/class/net</replaceable>. The namespace of the
|
||||||
|
process mounting the pseudo-filesystems determines what information
|
||||||
|
is shown, <emphasis>not</emphasis> the namespace of the process
|
||||||
|
accessing <replaceable>/proc</replaceable> or
|
||||||
|
<replaceable>/sys</replaceable>.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
If one uses the <option>-s</option> option to only attach to
|
||||||
|
the pid namespace of a container, but not its mount namespace
|
||||||
|
(which will contain the <replaceable>/proc</replaceable> of the
|
||||||
|
container and not the host), the contents of <option>/proc</option>
|
||||||
|
will reflect that of the host and not the container. Analogously,
|
||||||
|
the same issue occurs when reading the contents of
|
||||||
|
<replaceable>/sys/class/net</replaceable> and attaching to just
|
||||||
|
the network namespace.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
To work around this problem, the <option>-R</option> flag provides
|
||||||
|
the option to remount <replaceable>/proc</replaceable> and
|
||||||
|
<replaceable>/sys</replaceable> in order for them to reflect the
|
||||||
|
network/pid namespace context of the attached process. In order
|
||||||
|
not to interfere with the host's actual filesystem, the mount
|
||||||
|
namespace will be unshared (like <command>lxc-unshare</command>
|
||||||
|
does) before this is done, esentially giving the process a new
|
||||||
|
mount namespace, which is identical to the hosts's mount namespace
|
||||||
|
except for the <replaceable>/proc</replaceable> and
|
||||||
|
<replaceable>/sys</replaceable> filesystems.
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
<title>Security</title>
|
<title>Security</title>
|
||||||
<para>
|
<para>
|
||||||
The <option>-e</option> should be used with care, as it may break
|
The <option>-e</option> and <option>-s</option> options should
|
||||||
the isolation of the containers if used improperly.
|
be used with care, as it may break the isolation of the containers
|
||||||
|
if used improperly.
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
@ -113,6 +113,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
eg. busybox, debian, fedora, ubuntu or sshd.
|
eg. busybox, debian, fedora, ubuntu or sshd.
|
||||||
Refer to the examples in <filename>@LXCTEMPLATEDIR@</filename>
|
Refer to the examples in <filename>@LXCTEMPLATEDIR@</filename>
|
||||||
for details of the expected script structure.
|
for details of the expected script structure.
|
||||||
|
Alternatively, the full path to an executable template script
|
||||||
|
can also be passed as a parameter.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -123,9 +125,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
</term>
|
</term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
'backingstore' is one of 'none', 'lvm', or 'btrfs'. The
|
'backingstore' is one of 'none', 'dir', 'lvm', or 'btrfs'. The
|
||||||
default is 'none', meaning that the container root filesystem
|
default is 'none', meaning that the container root filesystem
|
||||||
will be a directory under <filename>@LXCPATH@/container/rootfs</filename>.
|
will be a directory under <filename>@LXCPATH@/container/rootfs</filename>.
|
||||||
|
'dir' has the same meaning as 'none', but also allows the optional
|
||||||
|
<replaceable>--dir ROOTFS</replaceable> to be specified, meaning
|
||||||
|
that the container rootfs should be placed under the specified path,
|
||||||
|
rather than the default.
|
||||||
The option 'btrfs' need not be specified as it will be used
|
The option 'btrfs' need not be specified as it will be used
|
||||||
automatically if the <filename>@LXCPATH@</filename> filesystem is found to
|
automatically if the <filename>@LXCPATH@</filename> filesystem is found to
|
||||||
be btrfs. If backingstore is 'lvm', then an lvm block device will be
|
be btrfs. If backingstore is 'lvm', then an lvm block device will be
|
||||||
@ -141,6 +147,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
filesystem) of size SIZE rather than the default, which is 1G.
|
filesystem) of size SIZE rather than the default, which is 1G.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term>
|
<term>
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
|
@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
]>
|
]>
|
||||||
@ -49,8 +49,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
<refsynopsisdiv>
|
<refsynopsisdiv>
|
||||||
<cmdsynopsis>
|
<cmdsynopsis>
|
||||||
<command>lxc-ls</command>
|
<command>lxc-ls</command>
|
||||||
|
<arg choice="opt">-1</arg>
|
||||||
<arg choice="opt">--active</arg>
|
<arg choice="opt">--active</arg>
|
||||||
<arg choice="opt">ls option</arg>
|
<arg choice="opt">--frozen</arg>
|
||||||
|
<arg choice="opt">--running</arg>
|
||||||
|
<arg choice="opt">--stopped</arg>
|
||||||
|
<arg choice="opt">--fancy</arg>
|
||||||
|
<arg choice="opt">--fancy-format</arg>
|
||||||
|
<arg choice="opt">filter</arg>
|
||||||
</cmdsynopsis>
|
</cmdsynopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
|
|
||||||
@ -65,6 +71,16 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
<refsect1>
|
<refsect1>
|
||||||
<title>Options</title>
|
<title>Options</title>
|
||||||
<variablelist>
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option><optional>-1</optional></option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Show one entry per line. (default when /dev/stdout isn't a tty)
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term>
|
<term>
|
||||||
@ -72,48 +88,79 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
</term>
|
</term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
List active containers.
|
List only active containers (same as --frozen --running).
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term>
|
<term>
|
||||||
<option><optional>ls options</optional></option>
|
<option><optional>--frozen</optional></option>
|
||||||
</term>
|
</term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
The option passed to <command>lxc-ls</command> are the
|
List only frozen containers.
|
||||||
same as the <command>ls</command> command.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
</variablelist>
|
|
||||||
|
|
||||||
</refsect1>
|
|
||||||
|
|
||||||
<refsect1>
|
|
||||||
<title>Examples</title>
|
|
||||||
<variablelist>
|
|
||||||
<varlistentry>
|
|
||||||
<term>lxc-ls -l</term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
list all the container and their permissions.
|
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term>lxc-ls --active -1</term>
|
<term>
|
||||||
|
<option><optional>--running</optional></option>
|
||||||
|
</term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
list active containers and display the list in one column.
|
List only running containers.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option><optional>--stopped</optional></option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
List only stopped containers.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option><optional>--fancy</optional></option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Use a fancy, column-based output.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option><optional>--fancy-format</optional></option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Comma separate list of column to show in the fancy output.
|
||||||
|
Valid values are: name, state, ipv4, ipv6 and pid
|
||||||
|
Default is: name,state,ipv4,ipv6
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option><optional>filter</optional></option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The filter passed to <command>lxc-ls</command> will be
|
||||||
|
applied to the container name. The format is a regular expression.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
@ -127,15 +174,33 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
</citerefentry>,
|
</citerefentry>,
|
||||||
</simpara>
|
</simpara>
|
||||||
|
|
||||||
</refsect1>
|
<title>Examples</title>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term>lxc-ls --fancy</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
list all the containers, listing one per line along with its
|
||||||
|
name, state, ipv4 and ipv6 addresses.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
&seealso;
|
<varlistentry>
|
||||||
|
<term>lxc-ls --active -1</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
list active containers and display the list in one column.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
<title>Author</title>
|
<title>Author</title>
|
||||||
<para>Daniel Lezcano <email>daniel.lezcano@free.fr</email></para>
|
<para>Stéphane Graber <email>stgraber@ubuntu.com</email></para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
</refentry>
|
</refentry>
|
||||||
|
|
||||||
<!-- Keep this comment at the end of the file
|
<!-- Keep this comment at the end of the file
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
|
@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
]>
|
]>
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
|
@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
@ -53,6 +53,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
<arg choice="opt">-f <replaceable>config_file</replaceable></arg>
|
<arg choice="opt">-f <replaceable>config_file</replaceable></arg>
|
||||||
<arg choice="opt">-c <replaceable>console_file</replaceable></arg>
|
<arg choice="opt">-c <replaceable>console_file</replaceable></arg>
|
||||||
<arg choice="opt">-d</arg>
|
<arg choice="opt">-d</arg>
|
||||||
|
<arg choice="opt">-p <replaceable>pid_file</replaceable></arg>
|
||||||
<arg choice="opt">-s KEY=VAL</arg>
|
<arg choice="opt">-s KEY=VAL</arg>
|
||||||
<arg choice="opt">-C</arg>
|
<arg choice="opt">-C</arg>
|
||||||
<arg choice="opt">command</arg>
|
<arg choice="opt">command</arg>
|
||||||
@ -107,6 +108,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option>-p, --pidfile <replaceable>pid_file</replaceable></option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Create a file with the process id.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term>
|
<term>
|
||||||
<option>-f, --rcfile <replaceable>config_file</replaceable></option>
|
<option>-f, --rcfile <replaceable>config_file</replaceable></option>
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
|
@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
@ -79,6 +79,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option>-t <replaceable>timeout</replaceable></option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Wait timeout seconds for desired state to be reached.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
]>
|
]>
|
||||||
@ -374,6 +374,26 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option>lxc.network.script.down</option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
add a configuration option to specify a script to be
|
||||||
|
executed before destroying the network used from the
|
||||||
|
host side. The following arguments are passed to the
|
||||||
|
script: container name and config section name (net)
|
||||||
|
Additional arguments depend on the config section
|
||||||
|
employing a script hook; the following are used by the
|
||||||
|
network system: execution context (down), network type
|
||||||
|
(empty/veth/macvlan/phys), Depending on the network
|
||||||
|
type, other arguments may be passed:
|
||||||
|
veth/macvlan/phys. And finally (host-sided) device name.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
@ -481,6 +501,31 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
|
<refsect2>
|
||||||
|
<title>/dev directory</title>
|
||||||
|
<para>
|
||||||
|
By default, lxc does nothing with the container's
|
||||||
|
<filename>/dev</filename>. This allows the container's
|
||||||
|
<filename>/dev</filename> to be set up as needed in the container
|
||||||
|
rootfs. If lxc.autodev is set to 1, then after mounting the container's
|
||||||
|
rootfs LXC will mount a fresh tmpfs under <filename>/dev</filename>
|
||||||
|
(limited to 100k) and fill in a minimal set of initial devices.
|
||||||
|
</para>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option>lxc.autodev</option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Set this to 1 to have LXC mount and populate a minimal
|
||||||
|
<filename>/dev</filename> when starting the container.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</refsect2>
|
||||||
|
|
||||||
<refsect2>
|
<refsect2>
|
||||||
<title>Mount points</title>
|
<title>Mount points</title>
|
||||||
<para>
|
<para>
|
||||||
@ -640,6 +685,84 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
|
<refsect2>
|
||||||
|
<title>Startup hooks</title>
|
||||||
|
<para>
|
||||||
|
Startup hooks are programs or scripts which can be executed
|
||||||
|
at various times in a container's lifetime.
|
||||||
|
</para>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option>lxc.hook.pre-start</option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A hook to be run in the host's namespace before the
|
||||||
|
container ttys, consoles, or mounts are up.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option>lxc.hook.pre-mount</option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A hook to be run in the container's fs namespace but before
|
||||||
|
the rootfs has been set up. This allows for manipulation
|
||||||
|
of the rootfs, i.e. to mount an encrypted filesystem. Mounts
|
||||||
|
done in this hook will not be reflected on the host (apart from
|
||||||
|
mounts propagation), so they will be automatically cleaned up
|
||||||
|
when the container shuts down.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option>lxc.hook.mount</option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A hook to be run in the container's namespace after
|
||||||
|
mounting has been done, but before the pivot_root.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option>lxc.hook.start</option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A hook to be run in the container's namespace immediately
|
||||||
|
before executing the container's init. This requires the
|
||||||
|
program to be available in the container.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option>lxc.hook.post-stop</option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A hook to be run in the host's namespace after the
|
||||||
|
container has been shut down.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</refsect2>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||||||
|
|
||||||
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
|
||||||
]>
|
]>
|
||||||
@ -280,7 +280,7 @@ rootfs
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<programlisting>
|
<programlisting>
|
||||||
|
<![CDATA[
|
||||||
---------
|
---------
|
||||||
| STOPPED |<---------------
|
| STOPPED |<---------------
|
||||||
--------- |
|
--------- |
|
||||||
@ -305,7 +305,7 @@ rootfs
|
|||||||
---------- |
|
---------- |
|
||||||
| |
|
| |
|
||||||
---------------------
|
---------------------
|
||||||
|
]]>
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
@ -570,7 +570,7 @@ rootfs
|
|||||||
to the background.
|
to the background.
|
||||||
|
|
||||||
<programlisting>
|
<programlisting>
|
||||||
|
<![CDATA[
|
||||||
# launch lxc-wait in background
|
# launch lxc-wait in background
|
||||||
lxc-wait -n foo -s STOPPED &
|
lxc-wait -n foo -s STOPPED &
|
||||||
LXC_WAIT_PID=$!
|
LXC_WAIT_PID=$!
|
||||||
@ -583,7 +583,7 @@ rootfs
|
|||||||
# is STOPPED
|
# is STOPPED
|
||||||
wait $LXC_WAIT_PID
|
wait $LXC_WAIT_PID
|
||||||
echo "'foo' is finished"
|
echo "'foo' is finished"
|
||||||
|
]]>
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
@ -29,8 +29,8 @@ Summary: %{name} : Linux Container
|
|||||||
Group: Applications/System
|
Group: Applications/System
|
||||||
License: LGPL
|
License: LGPL
|
||||||
BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
||||||
Requires: libcap
|
Requires: libcap openssl rsync
|
||||||
BuildRequires: libcap libcap-devel docbook-utils
|
BuildRequires: libcap libcap-devel docbook2X
|
||||||
|
|
||||||
%description
|
%description
|
||||||
|
|
||||||
@ -91,11 +91,13 @@ rm -rf %{buildroot}
|
|||||||
%{_mandir}/*
|
%{_mandir}/*
|
||||||
%{_datadir}/doc/*
|
%{_datadir}/doc/*
|
||||||
%{_datadir}/lxc/*
|
%{_datadir}/lxc/*
|
||||||
|
%{_sysconfdir}/lxc/*
|
||||||
|
|
||||||
%files libs
|
%files libs
|
||||||
%defattr(-,root,root)
|
%defattr(-,root,root)
|
||||||
%{_libdir}/*.so.*
|
%{_libdir}/*.so.*
|
||||||
%{_libdir}/%{name}
|
%{_libdir}/%{name}
|
||||||
|
%{_localstatedir}/*
|
||||||
%attr(4555,root,root) %{_libexecdir}/%{name}/lxc-init
|
%attr(4555,root,root) %{_libexecdir}/%{name}/lxc-init
|
||||||
|
|
||||||
%files devel
|
%files devel
|
||||||
|
32
runapitests.sh
Normal file
32
runapitests.sh
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -f /etc/lxc/test-busybox.conf
|
||||||
|
rm -f liblxc.so.0
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ `id -u` -ne 0 ]; then
|
||||||
|
echo "Run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > /etc/lxc/test-busybox.conf << EOF
|
||||||
|
lxc.network.type=veth
|
||||||
|
lxc.network.link=lxcbr0
|
||||||
|
lxc.network.flags=up
|
||||||
|
EOF
|
||||||
|
|
||||||
|
[ -f liblxc.so.0 ] || ln -s src/lxc/liblxc.so ./liblxc.so.0
|
||||||
|
export LD_LIBRARY_PATH=.
|
||||||
|
TESTS="lxc-test-containertests lxc-test-locktests lxc-test-startone"
|
||||||
|
for curtest in $TESTS; do
|
||||||
|
echo "running $curtest"
|
||||||
|
./src/tests/$curtest
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Test $curtest failed. Stopping"
|
||||||
|
cleanup
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "All tests passed"
|
||||||
|
cleanup
|
@ -1 +1 @@
|
|||||||
SUBDIRS = lxc
|
SUBDIRS = lxc tests python-lxc
|
||||||
|
@ -13,7 +13,9 @@ pkginclude_HEADERS = \
|
|||||||
list.h \
|
list.h \
|
||||||
log.h \
|
log.h \
|
||||||
state.h \
|
state.h \
|
||||||
attach.h
|
attach.h \
|
||||||
|
lxccontainer.h \
|
||||||
|
lxclock.h
|
||||||
|
|
||||||
sodir=$(libdir)
|
sodir=$(libdir)
|
||||||
# use PROGRAMS to avoid complains from automake
|
# use PROGRAMS to avoid complains from automake
|
||||||
@ -50,33 +52,41 @@ liblxc_so_SOURCES = \
|
|||||||
genl.c genl.h \
|
genl.c genl.h \
|
||||||
\
|
\
|
||||||
caps.c caps.h \
|
caps.c caps.h \
|
||||||
|
lxcseccomp.h \
|
||||||
mainloop.c mainloop.h \
|
mainloop.c mainloop.h \
|
||||||
af_unix.c af_unix.h \
|
af_unix.c af_unix.h \
|
||||||
\
|
\
|
||||||
utmp.c utmp.h \
|
utmp.c utmp.h \
|
||||||
apparmor.c apparmor.h
|
apparmor.c apparmor.h \
|
||||||
|
lxclock.h lxclock.c \
|
||||||
|
lxccontainer.c lxccontainer.h
|
||||||
|
|
||||||
AM_CFLAGS=-I$(top_srcdir)/src \
|
AM_CFLAGS=-I$(top_srcdir)/src \
|
||||||
-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
|
-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
|
||||||
-DLXCPATH=\"$(LXCPATH)\" \
|
-DLXCPATH=\"$(LXCPATH)\" \
|
||||||
-DLXCINITDIR=\"$(LXCINITDIR)\"
|
-DLXCINITDIR=\"$(LXCINITDIR)\" \
|
||||||
|
-DLXCTEMPLATEDIR=\"$(LXCTEMPLATEDIR)\"
|
||||||
|
|
||||||
if ENABLE_APPARMOR
|
if ENABLE_APPARMOR
|
||||||
AM_CFLAGS += -DHAVE_APPARMOR
|
AM_CFLAGS += -DHAVE_APPARMOR
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if ENABLE_SECCOMP
|
||||||
|
AM_CFLAGS += -DHAVE_SECCOMP
|
||||||
|
liblxc_so_SOURCES += seccomp.c
|
||||||
|
endif
|
||||||
|
|
||||||
liblxc_so_CFLAGS = -fPIC -DPIC $(AM_CFLAGS)
|
liblxc_so_CFLAGS = -fPIC -DPIC $(AM_CFLAGS)
|
||||||
|
|
||||||
liblxc_so_LDFLAGS = \
|
liblxc_so_LDFLAGS = \
|
||||||
-shared \
|
-shared \
|
||||||
-Wl,-soname,liblxc.so.$(firstword $(subst ., ,$(VERSION)))
|
-Wl,-soname,liblxc.so.$(firstword $(subst ., ,$(VERSION)))
|
||||||
|
|
||||||
liblxc_so_LDADD = -lutil $(CAP_LIBS) $(APPARMOR_LIBS)
|
liblxc_so_LDADD = -lutil $(CAP_LIBS) $(APPARMOR_LIBS) $(SECCOMP_LIBS) -lrt
|
||||||
|
|
||||||
bin_SCRIPTS = \
|
bin_SCRIPTS = \
|
||||||
lxc-ps \
|
lxc-ps \
|
||||||
lxc-netstat \
|
lxc-netstat \
|
||||||
lxc-ls \
|
|
||||||
lxc-checkconfig \
|
lxc-checkconfig \
|
||||||
lxc-setcap \
|
lxc-setcap \
|
||||||
lxc-setuid \
|
lxc-setuid \
|
||||||
@ -86,6 +96,14 @@ bin_SCRIPTS = \
|
|||||||
lxc-shutdown \
|
lxc-shutdown \
|
||||||
lxc-destroy
|
lxc-destroy
|
||||||
|
|
||||||
|
if ENABLE_PYTHON
|
||||||
|
bin_SCRIPTS += lxc-device
|
||||||
|
bin_SCRIPTS += lxc-ls
|
||||||
|
bin_SCRIPTS += lxc-start-ephemeral
|
||||||
|
else
|
||||||
|
bin_SCRIPTS += legacy/lxc-ls
|
||||||
|
endif
|
||||||
|
|
||||||
bin_PROGRAMS = \
|
bin_PROGRAMS = \
|
||||||
lxc-attach \
|
lxc-attach \
|
||||||
lxc-unshare \
|
lxc-unshare \
|
||||||
@ -110,7 +128,7 @@ AM_LDFLAGS = -Wl,-E
|
|||||||
if ENABLE_RPATH
|
if ENABLE_RPATH
|
||||||
AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir)
|
AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir)
|
||||||
endif
|
endif
|
||||||
LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@
|
LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@ @SECCOMP_LIBS@ -lrt
|
||||||
|
|
||||||
lxc_attach_SOURCES = lxc_attach.c
|
lxc_attach_SOURCES = lxc_attach.c
|
||||||
lxc_cgroup_SOURCES = lxc_cgroup.c
|
lxc_cgroup_SOURCES = lxc_cgroup.c
|
||||||
|
@ -45,6 +45,7 @@ struct lxc_arguments {
|
|||||||
int daemonize;
|
int daemonize;
|
||||||
const char *rcfile;
|
const char *rcfile;
|
||||||
const char *console;
|
const char *console;
|
||||||
|
const char *pidfile;
|
||||||
|
|
||||||
/* for lxc-checkpoint/restart */
|
/* for lxc-checkpoint/restart */
|
||||||
const char *statefile;
|
const char *statefile;
|
||||||
@ -57,6 +58,7 @@ struct lxc_arguments {
|
|||||||
|
|
||||||
/* for lxc-wait */
|
/* for lxc-wait */
|
||||||
char *states;
|
char *states;
|
||||||
|
long timeout;
|
||||||
|
|
||||||
/* close fds from parent? */
|
/* close fds from parent? */
|
||||||
int close_all_fds;
|
int close_all_fds;
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
#include <linux/unistd.h>
|
#include <linux/unistd.h>
|
||||||
|
|
||||||
#if !HAVE_DECL_PR_CAPBSET_DROP
|
#if !HAVE_DECL_PR_CAPBSET_DROP
|
||||||
@ -121,13 +122,22 @@ out_error:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lxc_attach_to_ns(pid_t pid)
|
int lxc_attach_to_ns(pid_t pid, int which)
|
||||||
{
|
{
|
||||||
char path[MAXPATHLEN];
|
char path[MAXPATHLEN];
|
||||||
char *ns[] = { "pid", "mnt", "net", "ipc", "uts" };
|
/* according to <http://article.gmane.org/gmane.linux.kernel.containers.lxc.devel/1429>,
|
||||||
const int size = sizeof(ns) / sizeof(char *);
|
* the file for user namepsaces in /proc/$pid/ns will be called
|
||||||
|
* 'user' once the kernel supports it
|
||||||
|
*/
|
||||||
|
static char *ns[] = { "mnt", "pid", "uts", "ipc", "user", "net" };
|
||||||
|
static int flags[] = {
|
||||||
|
CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC,
|
||||||
|
CLONE_NEWUSER, CLONE_NEWNET
|
||||||
|
};
|
||||||
|
static const int size = sizeof(ns) / sizeof(char *);
|
||||||
int fd[size];
|
int fd[size];
|
||||||
int i;
|
int i, j, saved_errno;
|
||||||
|
|
||||||
|
|
||||||
snprintf(path, MAXPATHLEN, "/proc/%d/ns", pid);
|
snprintf(path, MAXPATHLEN, "/proc/%d/ns", pid);
|
||||||
if (access(path, X_OK)) {
|
if (access(path, X_OK)) {
|
||||||
@ -136,16 +146,39 @@ int lxc_attach_to_ns(pid_t pid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < size; i++) {
|
for (i = 0; i < size; i++) {
|
||||||
|
/* ignore if we are not supposed to attach to that
|
||||||
|
* namespace
|
||||||
|
*/
|
||||||
|
if (which != -1 && !(which & flags[i])) {
|
||||||
|
fd[i] = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
snprintf(path, MAXPATHLEN, "/proc/%d/ns/%s", pid, ns[i]);
|
snprintf(path, MAXPATHLEN, "/proc/%d/ns/%s", pid, ns[i]);
|
||||||
fd[i] = open(path, O_RDONLY);
|
fd[i] = open(path, O_RDONLY);
|
||||||
if (fd[i] < 0) {
|
if (fd[i] < 0) {
|
||||||
|
saved_errno = errno;
|
||||||
|
|
||||||
|
/* close all already opened file descriptors before
|
||||||
|
* we return an error, so we don't leak them
|
||||||
|
*/
|
||||||
|
for (j = 0; j < i; j++)
|
||||||
|
close(fd[j]);
|
||||||
|
|
||||||
|
errno = saved_errno;
|
||||||
SYSERROR("failed to open '%s'", path);
|
SYSERROR("failed to open '%s'", path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < size; i++) {
|
for (i = 0; i < size; i++) {
|
||||||
if (setns(fd[i], 0)) {
|
if (fd[i] >= 0 && setns(fd[i], 0) != 0) {
|
||||||
|
saved_errno = errno;
|
||||||
|
|
||||||
|
for (j = i; j < size; j++)
|
||||||
|
close(fd[j]);
|
||||||
|
|
||||||
|
errno = saved_errno;
|
||||||
SYSERROR("failed to set namespace '%s'", ns[i]);
|
SYSERROR("failed to set namespace '%s'", ns[i]);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -156,6 +189,49 @@ int lxc_attach_to_ns(pid_t pid)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lxc_attach_remount_sys_proc()
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = unshare(CLONE_NEWNS);
|
||||||
|
if (ret < 0) {
|
||||||
|
SYSERROR("failed to unshare mount namespace");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* assume /proc is always mounted, so remount it */
|
||||||
|
ret = umount2("/proc", MNT_DETACH);
|
||||||
|
if (ret < 0) {
|
||||||
|
SYSERROR("failed to unmount /proc");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mount("none", "/proc", "proc", 0, NULL);
|
||||||
|
if (ret < 0) {
|
||||||
|
SYSERROR("failed to remount /proc");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* try to umount /sys - if it's not a mount point,
|
||||||
|
* we'll get EINVAL, then we ignore it because it
|
||||||
|
* may not have been mounted in the first place
|
||||||
|
*/
|
||||||
|
ret = umount2("/sys", MNT_DETACH);
|
||||||
|
if (ret < 0 && errno != EINVAL) {
|
||||||
|
SYSERROR("failed to unmount /sys");
|
||||||
|
return -1;
|
||||||
|
} else if (ret == 0) {
|
||||||
|
/* remount it */
|
||||||
|
ret = mount("none", "/sys", "sysfs", 0, NULL);
|
||||||
|
if (ret < 0) {
|
||||||
|
SYSERROR("failed to remount /sys");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx)
|
int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx)
|
||||||
{
|
{
|
||||||
int last_cap = lxc_caps_last_cap();
|
int last_cap = lxc_caps_last_cap();
|
||||||
|
@ -33,7 +33,8 @@ struct lxc_proc_context_info {
|
|||||||
|
|
||||||
extern struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid);
|
extern struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid);
|
||||||
|
|
||||||
extern int lxc_attach_to_ns(pid_t other_pid);
|
extern int lxc_attach_to_ns(pid_t other_pid, int which);
|
||||||
|
extern int lxc_attach_remount_sys_proc();
|
||||||
extern int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx);
|
extern int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
173
src/lxc/cgroup.c
173
src/lxc/cgroup.c
@ -254,13 +254,38 @@ static int cgroup_enable_clone_children(const char *path)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lxc_one_cgroup_attach(const char *name,
|
static int lxc_one_cgroup_finish_attach(int fd, pid_t pid)
|
||||||
struct mntent *mntent, pid_t pid)
|
|
||||||
{
|
{
|
||||||
FILE *f;
|
char buf[32];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
snprintf(buf, 32, "%ld", (long)pid);
|
||||||
|
|
||||||
|
ret = write(fd, buf, strlen(buf));
|
||||||
|
if (ret <= 0) {
|
||||||
|
SYSERROR("failed to write pid '%ld' to fd '%d'", (long)pid, fd);
|
||||||
|
ret = -1;
|
||||||
|
} else {
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lxc_one_cgroup_dispose_attach(int fd)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lxc_one_cgroup_prepare_attach(const char *name,
|
||||||
|
struct mntent *mntent)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
char tasks[MAXPATHLEN], initcgroup[MAXPATHLEN];
|
char tasks[MAXPATHLEN], initcgroup[MAXPATHLEN];
|
||||||
char *cgmnt = mntent->mnt_dir;
|
char *cgmnt = mntent->mnt_dir;
|
||||||
int flags, ret = 0;
|
int flags;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
flags = get_cgroup_flags(mntent);
|
flags = get_cgroup_flags(mntent);
|
||||||
@ -274,31 +299,83 @@ static int lxc_one_cgroup_attach(const char *name,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
f = fopen(tasks, "w");
|
fd = open(tasks, O_WRONLY);
|
||||||
if (!f) {
|
if (fd < 0) {
|
||||||
SYSERROR("failed to open '%s'", tasks);
|
SYSERROR("failed to open '%s'", tasks);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fprintf(f, "%d", pid) <= 0) {
|
return fd;
|
||||||
SYSERROR("failed to write pid '%d' to '%s'", pid, tasks);
|
}
|
||||||
ret = -1;
|
|
||||||
|
static int lxc_one_cgroup_attach(const char *name, struct mntent *mntent, pid_t pid)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = lxc_one_cgroup_prepare_attach(name, mntent);
|
||||||
|
if (fd < 0) {
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(f);
|
return lxc_one_cgroup_finish_attach(fd, pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lxc_cgroup_dispose_attach(void *data)
|
||||||
|
{
|
||||||
|
int *fds = data;
|
||||||
|
int ret, err;
|
||||||
|
|
||||||
|
if (!fds) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
for (; *fds >= 0; fds++) {
|
||||||
|
err = lxc_one_cgroup_dispose_attach(*fds);
|
||||||
|
if (err) {
|
||||||
|
ret = err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
int lxc_cgroup_finish_attach(void *data, pid_t pid)
|
||||||
* for each mounted cgroup, attach a pid to the cgroup for the container
|
{
|
||||||
*/
|
int *fds = data;
|
||||||
int lxc_cgroup_attach(const char *name, pid_t pid)
|
int err;
|
||||||
|
|
||||||
|
if (!fds) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; *fds >= 0; fds++) {
|
||||||
|
err = lxc_one_cgroup_finish_attach(*fds, pid);
|
||||||
|
if (err) {
|
||||||
|
/* get rid of the rest of them */
|
||||||
|
lxc_cgroup_dispose_attach(data);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*fds = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lxc_cgroup_prepare_attach(const char *name, void **data)
|
||||||
{
|
{
|
||||||
struct mntent *mntent;
|
struct mntent *mntent;
|
||||||
FILE *file = NULL;
|
FILE *file = NULL;
|
||||||
int err = -1;
|
int err = -1;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
|
int *fds;
|
||||||
|
int i;
|
||||||
|
static const int MAXFDS = 256;
|
||||||
|
|
||||||
file = setmntent(MTAB, "r");
|
file = setmntent(MTAB, "r");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
@ -306,7 +383,29 @@ int lxc_cgroup_attach(const char *name, pid_t pid)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* create a large enough buffer for all practical
|
||||||
|
* use cases
|
||||||
|
*/
|
||||||
|
fds = malloc(sizeof(int) * MAXFDS);
|
||||||
|
if (!fds) {
|
||||||
|
err = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
for (i = 0; i < MAXFDS; i++) {
|
||||||
|
fds[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
i = 0;
|
||||||
while ((mntent = getmntent(file))) {
|
while ((mntent = getmntent(file))) {
|
||||||
|
if (i >= MAXFDS - 1) {
|
||||||
|
ERROR("too many cgroups to attach to, aborting");
|
||||||
|
lxc_cgroup_dispose_attach(fds);
|
||||||
|
errno = ENOMEM;
|
||||||
|
err = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
DEBUG("checking '%s' (%s)", mntent->mnt_dir, mntent->mnt_type);
|
DEBUG("checking '%s' (%s)", mntent->mnt_dir, mntent->mnt_type);
|
||||||
|
|
||||||
if (strcmp(mntent->mnt_type, "cgroup"))
|
if (strcmp(mntent->mnt_type, "cgroup"))
|
||||||
@ -317,19 +416,41 @@ int lxc_cgroup_attach(const char *name, pid_t pid)
|
|||||||
INFO("[%d] found cgroup mounted at '%s',opts='%s'",
|
INFO("[%d] found cgroup mounted at '%s',opts='%s'",
|
||||||
++found, mntent->mnt_dir, mntent->mnt_opts);
|
++found, mntent->mnt_dir, mntent->mnt_opts);
|
||||||
|
|
||||||
err = lxc_one_cgroup_attach(name, mntent, pid);
|
fds[i] = lxc_one_cgroup_prepare_attach(name, mntent);
|
||||||
if (err)
|
if (fds[i] < 0) {
|
||||||
|
err = fds[i];
|
||||||
|
lxc_cgroup_dispose_attach(fds);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
ERROR("No cgroup mounted on the system");
|
ERROR("No cgroup mounted on the system");
|
||||||
|
|
||||||
|
*data = fds;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
endmntent(file);
|
endmntent(file);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* for each mounted cgroup, attach a pid to the cgroup for the container
|
||||||
|
*/
|
||||||
|
int lxc_cgroup_attach(const char *name, pid_t pid)
|
||||||
|
{
|
||||||
|
void *data = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = lxc_cgroup_prepare_attach(name, &data);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lxc_cgroup_finish_attach(data, pid);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* rename cgname, which is under cgparent, to a new name starting
|
* rename cgname, which is under cgparent, to a new name starting
|
||||||
* with 'cgparent/dead'. That way cgname can be reused. Return
|
* with 'cgparent/dead'. That way cgname can be reused. Return
|
||||||
@ -421,7 +542,7 @@ static int lxc_one_cgroup_create(const char *name,
|
|||||||
/* if cgparent does not exist, create it */
|
/* if cgparent does not exist, create it */
|
||||||
if (access(cgparent, F_OK)) {
|
if (access(cgparent, F_OK)) {
|
||||||
ret = mkdir(cgparent, 0755);
|
ret = mkdir(cgparent, 0755);
|
||||||
if (ret == -1 && errno == EEXIST) {
|
if (ret == -1 && errno != EEXIST) {
|
||||||
SYSERROR("failed to create '%s' directory", cgparent);
|
SYSERROR("failed to create '%s' directory", cgparent);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -668,6 +789,13 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If you pass in NULL value or 0 len, then you are asking for the size
|
||||||
|
* of the file. Note that we can't get the file size quickly through stat
|
||||||
|
* or lseek. Therefore if you pass in len > 0 but less than the file size,
|
||||||
|
* your only indication will be that the return value will be equal to the
|
||||||
|
* passed-in ret. We will not return the actual full file size.
|
||||||
|
*/
|
||||||
int lxc_cgroup_get(const char *name, const char *filename,
|
int lxc_cgroup_get(const char *name, const char *filename,
|
||||||
char *value, size_t len)
|
char *value, size_t len)
|
||||||
{
|
{
|
||||||
@ -692,7 +820,18 @@ int lxc_cgroup_get(const char *name, const char *filename,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!len || !value) {
|
||||||
|
char buf[100];
|
||||||
|
int count = 0;
|
||||||
|
while ((ret = read(fd, buf, 100)) > 0)
|
||||||
|
count += ret;
|
||||||
|
if (ret >= 0)
|
||||||
|
ret = count;
|
||||||
|
} else {
|
||||||
|
memset(value, 0, len);
|
||||||
ret = read(fd, value, len);
|
ret = read(fd, value, len);
|
||||||
|
}
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
ERROR("read %s : %s", path, strerror(errno));
|
ERROR("read %s : %s", path, strerror(errno));
|
||||||
|
|
||||||
|
@ -31,5 +31,8 @@ extern int lxc_cgroup_destroy(const char *name);
|
|||||||
extern int lxc_cgroup_path_get(char **path, const char *subsystem, const char *name);
|
extern int lxc_cgroup_path_get(char **path, const char *subsystem, const char *name);
|
||||||
extern int lxc_cgroup_nrtasks(const char *name);
|
extern int lxc_cgroup_nrtasks(const char *name);
|
||||||
extern int lxc_cgroup_attach(const char *name, pid_t pid);
|
extern int lxc_cgroup_attach(const char *name, pid_t pid);
|
||||||
|
extern int lxc_cgroup_prepare_attach(const char *name, void **data);
|
||||||
|
extern int lxc_cgroup_finish_attach(void *data, pid_t pid);
|
||||||
|
extern int lxc_cgroup_dispose_attach(void *data);
|
||||||
extern int lxc_ns_is_mounted(void);
|
extern int lxc_ns_is_mounted(void);
|
||||||
#endif
|
#endif
|
||||||
|
@ -154,11 +154,32 @@ pid_t get_init_pid(const char *name)
|
|||||||
return command.answer.pid;
|
return command.answer.pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lxc_get_clone_flags(const char *name)
|
||||||
|
{
|
||||||
|
struct lxc_command command = {
|
||||||
|
.request = { .type = LXC_COMMAND_CLONE_FLAGS },
|
||||||
|
};
|
||||||
|
|
||||||
|
int ret, stopped = 0;
|
||||||
|
|
||||||
|
ret = lxc_command(name, &command, &stopped);
|
||||||
|
if (ret < 0 && stopped)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
ERROR("failed to send command");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return command.answer.ret;
|
||||||
|
}
|
||||||
|
|
||||||
extern void lxc_console_remove_fd(int, struct lxc_tty_info *);
|
extern void lxc_console_remove_fd(int, struct lxc_tty_info *);
|
||||||
extern int lxc_console_callback(int, struct lxc_request *, struct lxc_handler *);
|
extern int lxc_console_callback(int, struct lxc_request *, struct lxc_handler *);
|
||||||
extern int lxc_stop_callback(int, struct lxc_request *, struct lxc_handler *);
|
extern int lxc_stop_callback(int, struct lxc_request *, struct lxc_handler *);
|
||||||
extern int lxc_state_callback(int, struct lxc_request *, struct lxc_handler *);
|
extern int lxc_state_callback(int, struct lxc_request *, struct lxc_handler *);
|
||||||
extern int lxc_pid_callback(int, struct lxc_request *, struct lxc_handler *);
|
extern int lxc_pid_callback(int, struct lxc_request *, struct lxc_handler *);
|
||||||
|
extern int lxc_clone_flags_callback(int, struct lxc_request *, struct lxc_handler *);
|
||||||
|
|
||||||
static int trigger_command(int fd, struct lxc_request *request,
|
static int trigger_command(int fd, struct lxc_request *request,
|
||||||
struct lxc_handler *handler)
|
struct lxc_handler *handler)
|
||||||
@ -170,6 +191,7 @@ static int trigger_command(int fd, struct lxc_request *request,
|
|||||||
[LXC_COMMAND_STOP] = lxc_stop_callback,
|
[LXC_COMMAND_STOP] = lxc_stop_callback,
|
||||||
[LXC_COMMAND_STATE] = lxc_state_callback,
|
[LXC_COMMAND_STATE] = lxc_state_callback,
|
||||||
[LXC_COMMAND_PID] = lxc_pid_callback,
|
[LXC_COMMAND_PID] = lxc_pid_callback,
|
||||||
|
[LXC_COMMAND_CLONE_FLAGS] = lxc_clone_flags_callback,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (request->type < 0 || request->type >= LXC_COMMAND_MAX)
|
if (request->type < 0 || request->type >= LXC_COMMAND_MAX)
|
||||||
@ -305,5 +327,6 @@ extern int lxc_command_mainloop_add(const char *name,
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handler->conf->maincmd_fd = fd;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ enum {
|
|||||||
LXC_COMMAND_STOP,
|
LXC_COMMAND_STOP,
|
||||||
LXC_COMMAND_STATE,
|
LXC_COMMAND_STATE,
|
||||||
LXC_COMMAND_PID,
|
LXC_COMMAND_PID,
|
||||||
|
LXC_COMMAND_CLONE_FLAGS,
|
||||||
LXC_COMMAND_MAX,
|
LXC_COMMAND_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ struct lxc_command {
|
|||||||
};
|
};
|
||||||
|
|
||||||
extern pid_t get_init_pid(const char *name);
|
extern pid_t get_init_pid(const char *name);
|
||||||
|
extern int lxc_get_clone_flags(const char *name);
|
||||||
|
|
||||||
extern int lxc_command(const char *name, struct lxc_command *command,
|
extern int lxc_command(const char *name, struct lxc_command *command,
|
||||||
int *stopped);
|
int *stopped);
|
||||||
|
580
src/lxc/conf.c
580
src/lxc/conf.c
@ -66,6 +66,8 @@
|
|||||||
#include <apparmor.h>
|
#include <apparmor.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "lxcseccomp.h"
|
||||||
|
|
||||||
lxc_log_define(lxc_conf, lxc);
|
lxc_log_define(lxc_conf, lxc);
|
||||||
|
|
||||||
#define MAXHWLEN 18
|
#define MAXHWLEN 18
|
||||||
@ -109,6 +111,9 @@ lxc_log_define(lxc_conf, lxc);
|
|||||||
#define PR_CAPBSET_DROP 24
|
#define PR_CAPBSET_DROP 24
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
char *lxchook_names[NUM_LXC_HOOKS] = {
|
||||||
|
"pre-start", "pre-mount", "mount", "start", "post-stop" };
|
||||||
|
|
||||||
extern int pivot_root(const char * new_root, const char * put_old);
|
extern int pivot_root(const char * new_root, const char * put_old);
|
||||||
|
|
||||||
typedef int (*instanciate_cb)(struct lxc_handler *, struct lxc_netdev *);
|
typedef int (*instanciate_cb)(struct lxc_handler *, struct lxc_netdev *);
|
||||||
@ -138,6 +143,20 @@ static instanciate_cb netdev_conf[LXC_NET_MAXCONFTYPE + 1] = {
|
|||||||
[LXC_NET_EMPTY] = instanciate_empty,
|
[LXC_NET_EMPTY] = instanciate_empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int shutdown_veth(struct lxc_handler *, struct lxc_netdev *);
|
||||||
|
static int shutdown_macvlan(struct lxc_handler *, struct lxc_netdev *);
|
||||||
|
static int shutdown_vlan(struct lxc_handler *, struct lxc_netdev *);
|
||||||
|
static int shutdown_phys(struct lxc_handler *, struct lxc_netdev *);
|
||||||
|
static int shutdown_empty(struct lxc_handler *, struct lxc_netdev *);
|
||||||
|
|
||||||
|
static instanciate_cb netdev_deconf[LXC_NET_MAXCONFTYPE + 1] = {
|
||||||
|
[LXC_NET_VETH] = shutdown_veth,
|
||||||
|
[LXC_NET_MACVLAN] = shutdown_macvlan,
|
||||||
|
[LXC_NET_VLAN] = shutdown_vlan,
|
||||||
|
[LXC_NET_PHYS] = shutdown_phys,
|
||||||
|
[LXC_NET_EMPTY] = shutdown_empty,
|
||||||
|
};
|
||||||
|
|
||||||
static struct mount_opt mount_opt[] = {
|
static struct mount_opt mount_opt[] = {
|
||||||
{ "defaults", 0, 0 },
|
{ "defaults", 0, 0 },
|
||||||
{ "ro", 0, MS_RDONLY },
|
{ "ro", 0, MS_RDONLY },
|
||||||
@ -214,12 +233,41 @@ static struct caps_opt caps_opt[] = {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int run_buffer(char *buffer)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
char *output;
|
||||||
|
|
||||||
|
f = popen(buffer, "r");
|
||||||
|
if (!f) {
|
||||||
|
SYSERROR("popen failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
output = malloc(LXC_LOG_BUFFER_SIZE);
|
||||||
|
if (!output) {
|
||||||
|
ERROR("failed to allocate memory for script output");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(fgets(output, LXC_LOG_BUFFER_SIZE, f))
|
||||||
|
DEBUG("script output: %s", output);
|
||||||
|
|
||||||
|
free(output);
|
||||||
|
|
||||||
|
if (pclose(f) == -1) {
|
||||||
|
SYSERROR("Script exited on error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int run_script(const char *name, const char *section,
|
static int run_script(const char *name, const char *section,
|
||||||
const char *script, ...)
|
const char *script, ...)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
FILE *f;
|
char *buffer, *p;
|
||||||
char *buffer, *p, *output;
|
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
@ -266,29 +314,7 @@ static int run_script(const char *name, const char *section,
|
|||||||
}
|
}
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
f = popen(buffer, "r");
|
return run_buffer(buffer);
|
||||||
if (!f) {
|
|
||||||
SYSERROR("popen failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
output = malloc(LXC_LOG_BUFFER_SIZE);
|
|
||||||
if (!output) {
|
|
||||||
ERROR("failed to allocate memory for script output");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while(fgets(output, LXC_LOG_BUFFER_SIZE, f))
|
|
||||||
DEBUG("script output: %s", output);
|
|
||||||
|
|
||||||
free(output);
|
|
||||||
|
|
||||||
if (pclose(f) == -1) {
|
|
||||||
SYSERROR("Script exited on error");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_fstype_cb(char* buffer, void *data)
|
static int find_fstype_cb(char* buffer, void *data)
|
||||||
@ -636,6 +662,15 @@ static int setup_tty(const struct lxc_rootfs *rootfs,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
/* If we populated /dev, then we need to create /dev/ttyN */
|
||||||
|
if (access(path, F_OK)) {
|
||||||
|
ret = creat(path, 0660);
|
||||||
|
if (ret==-1) {
|
||||||
|
SYSERROR("error creating %s\n", path);
|
||||||
|
/* this isn't fatal, continue */
|
||||||
|
} else
|
||||||
|
close(ret);
|
||||||
|
}
|
||||||
if (mount(pty_info->name, path, "none", MS_BIND, 0)) {
|
if (mount(pty_info->name, path, "none", MS_BIND, 0)) {
|
||||||
WARN("failed to mount '%s'->'%s'",
|
WARN("failed to mount '%s'->'%s'",
|
||||||
pty_info->name, path);
|
pty_info->name, path);
|
||||||
@ -708,7 +743,7 @@ static int umount_oldrootfs(const char *oldrootfs)
|
|||||||
{
|
{
|
||||||
char path[MAXPATHLEN];
|
char path[MAXPATHLEN];
|
||||||
void *cbparm[2];
|
void *cbparm[2];
|
||||||
struct lxc_list mountlist, *iterator;
|
struct lxc_list mountlist, *iterator, *next;
|
||||||
int ok, still_mounted, last_still_mounted;
|
int ok, still_mounted, last_still_mounted;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@ -748,7 +783,7 @@ static int umount_oldrootfs(const char *oldrootfs)
|
|||||||
last_still_mounted = still_mounted;
|
last_still_mounted = still_mounted;
|
||||||
still_mounted = 0;
|
still_mounted = 0;
|
||||||
|
|
||||||
lxc_list_for_each(iterator, &mountlist) {
|
lxc_list_for_each_safe(iterator, &mountlist, next) {
|
||||||
|
|
||||||
/* umount normally */
|
/* umount normally */
|
||||||
if (!umount(iterator->elem)) {
|
if (!umount(iterator->elem)) {
|
||||||
@ -843,6 +878,114 @@ static int setup_rootfs_pivot_root(const char *rootfs, const char *pivotdir)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do we want to add options for max size of /dev and a file to
|
||||||
|
* specify which devices to create?
|
||||||
|
*/
|
||||||
|
static int mount_autodev(char *root)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
char path[MAXPATHLEN];
|
||||||
|
|
||||||
|
INFO("Mounting /dev under %s\n", root);
|
||||||
|
ret = snprintf(path, MAXPATHLEN, "%s/dev", root);
|
||||||
|
if (ret < 0 || ret > MAXPATHLEN)
|
||||||
|
return -1;
|
||||||
|
ret = mount("none", path, "tmpfs", 0, "size=100000");
|
||||||
|
if (ret) {
|
||||||
|
SYSERROR("Failed to mount /dev at %s\n", root);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ret = snprintf(path, MAXPATHLEN, "%s/dev/pts", root);
|
||||||
|
if (ret < 0 || ret >= MAXPATHLEN)
|
||||||
|
return -1;
|
||||||
|
ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||||||
|
if (ret) {
|
||||||
|
SYSERROR("Failed to create /dev/pts in container");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO("Mounted /dev under %s\n", root);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to run MAKEDEV console in the container. If something fails,
|
||||||
|
* continue anyway as it should not be detrimental to the container.
|
||||||
|
* This makes sure that things like /dev/vcs* exist.
|
||||||
|
* (Pass devpath in to reduce stack usage)
|
||||||
|
*/
|
||||||
|
static void run_makedev(char *devpath)
|
||||||
|
{
|
||||||
|
int curd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
curd = open(".", O_RDONLY);
|
||||||
|
if (curd < 0)
|
||||||
|
return;
|
||||||
|
ret = chdir(devpath);
|
||||||
|
if (ret) {
|
||||||
|
close(curd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (run_buffer("/sbin/MAKEDEV console"))
|
||||||
|
INFO("Error running MAKEDEV console in %s", devpath);
|
||||||
|
ret = fchdir(curd);
|
||||||
|
if (ret)
|
||||||
|
INFO("Error returning to original directory: expect breakage");
|
||||||
|
close(curd);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct lxc_devs {
|
||||||
|
char *name;
|
||||||
|
mode_t mode;
|
||||||
|
int maj;
|
||||||
|
int min;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lxc_devs lxc_devs[] = {
|
||||||
|
{ "null", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 3 },
|
||||||
|
{ "zero", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 5 },
|
||||||
|
{ "full", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 7 },
|
||||||
|
{ "urandom", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 9 },
|
||||||
|
{ "random", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 8 },
|
||||||
|
{ "tty", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 5, 0 },
|
||||||
|
{ "console", S_IFCHR | S_IRUSR | S_IWUSR, 5, 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int setup_autodev(char *root)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct lxc_devs *d;
|
||||||
|
char path[MAXPATHLEN];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
INFO("Creating initial consoles under %s/dev\n", root);
|
||||||
|
|
||||||
|
ret = snprintf(path, MAXPATHLEN, "%s/dev", root);
|
||||||
|
if (ret < 0 || ret >= MAXPATHLEN) {
|
||||||
|
ERROR("Error calculating container /dev location");
|
||||||
|
return -1;
|
||||||
|
} else
|
||||||
|
run_makedev(path);
|
||||||
|
|
||||||
|
INFO("Populating /dev under %s\n", root);
|
||||||
|
for (i = 0; i < sizeof(lxc_devs) / sizeof(lxc_devs[0]); i++) {
|
||||||
|
d = &lxc_devs[i];
|
||||||
|
ret = snprintf(path, MAXPATHLEN, "%s/dev/%s", root, d->name);
|
||||||
|
if (ret < 0 || ret >= MAXPATHLEN)
|
||||||
|
return -1;
|
||||||
|
ret = mknod(path, d->mode, makedev(d->maj, d->min));
|
||||||
|
if (ret && errno != EEXIST) {
|
||||||
|
SYSERROR("Error creating %s\n", d->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO("Populated /dev under %s\n", root);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int setup_rootfs(const struct lxc_rootfs *rootfs)
|
static int setup_rootfs(const struct lxc_rootfs *rootfs)
|
||||||
{
|
{
|
||||||
if (!rootfs->path)
|
if (!rootfs->path)
|
||||||
@ -1061,6 +1204,8 @@ static int setup_kmsg(const struct lxc_rootfs *rootfs,
|
|||||||
char kpath[MAXPATHLEN];
|
char kpath[MAXPATHLEN];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (!rootfs->path)
|
||||||
|
return 0;
|
||||||
ret = snprintf(kpath, sizeof(kpath), "%s/dev/kmsg", rootfs->mount);
|
ret = snprintf(kpath, sizeof(kpath), "%s/dev/kmsg", rootfs->mount);
|
||||||
if (ret < 0 || ret >= sizeof(kpath))
|
if (ret < 0 || ret >= sizeof(kpath))
|
||||||
return -1;
|
return -1;
|
||||||
@ -1228,8 +1373,8 @@ static int mount_entry_on_absolute_rootfs(struct mntent *mntent,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* if rootfs->path is a blockdev path, allow container fstab to
|
/* if rootfs->path is a blockdev path, allow container fstab to
|
||||||
* use /var/lib/lxc/CN/rootfs as the target prefix */
|
* use $LXCPATH/CN/rootfs as the target prefix */
|
||||||
r = snprintf(path, MAXPATHLEN, "/var/lib/lxc/%s/rootfs", lxc_name);
|
r = snprintf(path, MAXPATHLEN, LXCPATH "/%s/rootfs", lxc_name);
|
||||||
if (r < 0 || r >= MAXPATHLEN)
|
if (r < 0 || r >= MAXPATHLEN)
|
||||||
goto skipvarlib;
|
goto skipvarlib;
|
||||||
|
|
||||||
@ -1647,7 +1792,7 @@ static int setup_netdev(struct lxc_netdev *netdev)
|
|||||||
ifname, strerror(-err));
|
ifname, strerror(-err));
|
||||||
if (netdev->ipv6_gateway_auto) {
|
if (netdev->ipv6_gateway_auto) {
|
||||||
char buf[INET6_ADDRSTRLEN];
|
char buf[INET6_ADDRSTRLEN];
|
||||||
inet_ntop(AF_INET, netdev->ipv6_gateway, buf, sizeof(buf));
|
inet_ntop(AF_INET6, netdev->ipv6_gateway, buf, sizeof(buf));
|
||||||
ERROR("tried to set autodetected ipv6 gateway '%s'", buf);
|
ERROR("tried to set autodetected ipv6 gateway '%s'", buf);
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
@ -1680,6 +1825,21 @@ static int setup_network(struct lxc_list *network)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lxc_rename_phys_nics_on_shutdown(struct lxc_conf *conf)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
INFO("running to reset %d nic names", conf->num_savednics);
|
||||||
|
for (i=0; i<conf->num_savednics; i++) {
|
||||||
|
struct saved_nic *s = &conf->saved_nics[i];
|
||||||
|
INFO("resetting nic %d to %s\n", s->ifindex, s->orig_name);
|
||||||
|
lxc_netdev_rename_by_index(s->ifindex, s->orig_name);
|
||||||
|
free(s->orig_name);
|
||||||
|
}
|
||||||
|
conf->num_savednics = 0;
|
||||||
|
free(conf->saved_nics);
|
||||||
|
}
|
||||||
|
|
||||||
static int setup_private_host_hw_addr(char *veth1)
|
static int setup_private_host_hw_addr(char *veth1)
|
||||||
{
|
{
|
||||||
struct ifreq ifr;
|
struct ifreq ifr;
|
||||||
@ -1715,6 +1875,8 @@ static int setup_private_host_hw_addr(char *veth1)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *default_rootfs_mount = LXCROOTFSMOUNT;
|
||||||
|
|
||||||
struct lxc_conf *lxc_conf_init(void)
|
struct lxc_conf *lxc_conf_init(void)
|
||||||
{
|
{
|
||||||
struct lxc_conf *new;
|
struct lxc_conf *new;
|
||||||
@ -1733,7 +1895,8 @@ struct lxc_conf *lxc_conf_init(void)
|
|||||||
new->console.master = -1;
|
new->console.master = -1;
|
||||||
new->console.slave = -1;
|
new->console.slave = -1;
|
||||||
new->console.name[0] = '\0';
|
new->console.name[0] = '\0';
|
||||||
new->rootfs.mount = LXCROOTFSMOUNT;
|
new->rootfs.mount = default_rootfs_mount;
|
||||||
|
new->loglevel = LXC_LOG_PRIORITY_NOTSET;
|
||||||
lxc_list_init(&new->cgroup);
|
lxc_list_init(&new->cgroup);
|
||||||
lxc_list_init(&new->network);
|
lxc_list_init(&new->network);
|
||||||
lxc_list_init(&new->mount_list);
|
lxc_list_init(&new->mount_list);
|
||||||
@ -1765,6 +1928,8 @@ static int instanciate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
veth1 = mktemp(veth1buf);
|
veth1 = mktemp(veth1buf);
|
||||||
|
/* store away for deconf */
|
||||||
|
memcpy(netdev->priv.veth_attr.veth1, veth1, IFNAMSIZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(veth2buf, sizeof(veth2buf), "vethXXXXXX");
|
snprintf(veth2buf, sizeof(veth2buf), "vethXXXXXX");
|
||||||
@ -1841,6 +2006,25 @@ out_delete:
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int shutdown_veth(struct lxc_handler *handler, struct lxc_netdev *netdev)
|
||||||
|
{
|
||||||
|
char *veth1;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (netdev->priv.veth_attr.pair)
|
||||||
|
veth1 = netdev->priv.veth_attr.pair;
|
||||||
|
else
|
||||||
|
veth1 = netdev->priv.veth_attr.veth1;
|
||||||
|
|
||||||
|
if (netdev->downscript) {
|
||||||
|
err = run_script(handler->name, "net", netdev->downscript,
|
||||||
|
"down", "veth", veth1, (char*) NULL);
|
||||||
|
if (err)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int instanciate_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
|
static int instanciate_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
|
||||||
{
|
{
|
||||||
char peerbuf[IFNAMSIZ], *peer;
|
char peerbuf[IFNAMSIZ], *peer;
|
||||||
@ -1889,6 +2073,20 @@ static int instanciate_macvlan(struct lxc_handler *handler, struct lxc_netdev *n
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int shutdown_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (netdev->downscript) {
|
||||||
|
err = run_script(handler->name, "net", netdev->downscript,
|
||||||
|
"down", "macvlan", netdev->link,
|
||||||
|
(char*) NULL);
|
||||||
|
if (err)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* XXX: merge with instanciate_macvlan */
|
/* XXX: merge with instanciate_macvlan */
|
||||||
static int instanciate_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
|
static int instanciate_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
|
||||||
{
|
{
|
||||||
@ -1926,6 +2124,11 @@ static int instanciate_vlan(struct lxc_handler *handler, struct lxc_netdev *netd
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int shutdown_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int instanciate_phys(struct lxc_handler *handler, struct lxc_netdev *netdev)
|
static int instanciate_phys(struct lxc_handler *handler, struct lxc_netdev *netdev)
|
||||||
{
|
{
|
||||||
if (!netdev->link) {
|
if (!netdev->link) {
|
||||||
@ -1950,6 +2153,19 @@ static int instanciate_phys(struct lxc_handler *handler, struct lxc_netdev *netd
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int shutdown_phys(struct lxc_handler *handler, struct lxc_netdev *netdev)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (netdev->downscript) {
|
||||||
|
err = run_script(handler->name, "net", netdev->downscript,
|
||||||
|
"down", "phys", netdev->link, (char*) NULL);
|
||||||
|
if (err)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int instanciate_empty(struct lxc_handler *handler, struct lxc_netdev *netdev)
|
static int instanciate_empty(struct lxc_handler *handler, struct lxc_netdev *netdev)
|
||||||
{
|
{
|
||||||
netdev->ifindex = 0;
|
netdev->ifindex = 0;
|
||||||
@ -1963,6 +2179,19 @@ static int instanciate_empty(struct lxc_handler *handler, struct lxc_netdev *net
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int shutdown_empty(struct lxc_handler *handler, struct lxc_netdev *netdev)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (netdev->downscript) {
|
||||||
|
err = run_script(handler->name, "net", netdev->downscript,
|
||||||
|
"down", "empty", (char*) NULL);
|
||||||
|
if (err)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int lxc_create_network(struct lxc_handler *handler)
|
int lxc_create_network(struct lxc_handler *handler)
|
||||||
{
|
{
|
||||||
struct lxc_list *network = &handler->conf->network;
|
struct lxc_list *network = &handler->conf->network;
|
||||||
@ -1989,28 +2218,32 @@ int lxc_create_network(struct lxc_handler *handler)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lxc_delete_network(struct lxc_list *network)
|
void lxc_delete_network(struct lxc_handler *handler)
|
||||||
{
|
{
|
||||||
|
struct lxc_list *network = &handler->conf->network;
|
||||||
struct lxc_list *iterator;
|
struct lxc_list *iterator;
|
||||||
struct lxc_netdev *netdev;
|
struct lxc_netdev *netdev;
|
||||||
|
|
||||||
lxc_list_for_each(iterator, network) {
|
lxc_list_for_each(iterator, network) {
|
||||||
netdev = iterator->elem;
|
netdev = iterator->elem;
|
||||||
if (netdev->ifindex == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (netdev->type == LXC_NET_PHYS) {
|
if (netdev->ifindex != 0 && netdev->type == LXC_NET_PHYS) {
|
||||||
if (lxc_netdev_rename_by_index(netdev->ifindex, netdev->link))
|
if (lxc_netdev_rename_by_index(netdev->ifindex, netdev->link))
|
||||||
WARN("failed to rename to the initial name the " \
|
WARN("failed to rename to the initial name the " \
|
||||||
"netdev '%s'", netdev->link);
|
"netdev '%s'", netdev->link);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (netdev_deconf[netdev->type](handler, netdev)) {
|
||||||
|
WARN("failed to destroy netdev");
|
||||||
|
}
|
||||||
|
|
||||||
/* Recent kernel remove the virtual interfaces when the network
|
/* Recent kernel remove the virtual interfaces when the network
|
||||||
* namespace is destroyed but in case we did not moved the
|
* namespace is destroyed but in case we did not moved the
|
||||||
* interface to the network namespace, we have to destroy it
|
* interface to the network namespace, we have to destroy it
|
||||||
*/
|
*/
|
||||||
if (lxc_netdev_delete_by_index(netdev->ifindex))
|
if (netdev->ifindex != 0 &&
|
||||||
|
lxc_netdev_delete_by_index(netdev->ifindex))
|
||||||
WARN("failed to remove interface '%s'", netdev->name);
|
WARN("failed to remove interface '%s'", netdev->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2166,11 +2399,23 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (run_lxc_hooks(name, "pre-mount", lxc_conf)) {
|
||||||
|
ERROR("failed to run pre-mount hooks for container '%s'.", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (setup_rootfs(&lxc_conf->rootfs)) {
|
if (setup_rootfs(&lxc_conf->rootfs)) {
|
||||||
ERROR("failed to setup rootfs for '%s'", name);
|
ERROR("failed to setup rootfs for '%s'", name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lxc_conf->autodev) {
|
||||||
|
if (mount_autodev(lxc_conf->rootfs.mount)) {
|
||||||
|
ERROR("failed to mount /dev in the container");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (setup_mount(&lxc_conf->rootfs, lxc_conf->fstab, name)) {
|
if (setup_mount(&lxc_conf->rootfs, lxc_conf->fstab, name)) {
|
||||||
ERROR("failed to setup the mounts for '%s'", name);
|
ERROR("failed to setup the mounts for '%s'", name);
|
||||||
return -1;
|
return -1;
|
||||||
@ -2186,6 +2431,13 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lxc_conf->autodev) {
|
||||||
|
if (setup_autodev(lxc_conf->rootfs.mount)) {
|
||||||
|
ERROR("failed to populate /dev in the container");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (setup_cgroup(name, &lxc_conf->cgroup)) {
|
if (setup_cgroup(name, &lxc_conf->cgroup)) {
|
||||||
ERROR("failed to setup the cgroups for '%s'", name);
|
ERROR("failed to setup the cgroups for '%s'", name);
|
||||||
return -1;
|
return -1;
|
||||||
@ -2196,10 +2448,8 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setup_kmsg(&lxc_conf->rootfs, &lxc_conf->console)) {
|
if (setup_kmsg(&lxc_conf->rootfs, &lxc_conf->console)) // don't fail
|
||||||
ERROR("failed to setup kmsg for '%s'", name);
|
ERROR("failed to setup kmsg for '%s'", name);
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setup_tty(&lxc_conf->rootfs, &lxc_conf->tty_info, lxc_conf->ttydir)) {
|
if (setup_tty(&lxc_conf->rootfs, &lxc_conf->tty_info, lxc_conf->ttydir)) {
|
||||||
ERROR("failed to setup the ttys for '%s'", name);
|
ERROR("failed to setup the ttys for '%s'", name);
|
||||||
@ -2253,6 +2503,8 @@ int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf)
|
|||||||
|
|
||||||
if (strcmp(hook, "pre-start") == 0)
|
if (strcmp(hook, "pre-start") == 0)
|
||||||
which = LXCHOOK_PRESTART;
|
which = LXCHOOK_PRESTART;
|
||||||
|
else if (strcmp(hook, "pre-mount") == 0)
|
||||||
|
which = LXCHOOK_PREMOUNT;
|
||||||
else if (strcmp(hook, "mount") == 0)
|
else if (strcmp(hook, "mount") == 0)
|
||||||
which = LXCHOOK_MOUNT;
|
which = LXCHOOK_MOUNT;
|
||||||
else if (strcmp(hook, "start") == 0)
|
else if (strcmp(hook, "start") == 0)
|
||||||
@ -2270,3 +2522,253 @@ int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf)
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void lxc_remove_nic(struct lxc_list *it)
|
||||||
|
{
|
||||||
|
struct lxc_netdev *netdev = it->elem;
|
||||||
|
struct lxc_list *it2,*next;
|
||||||
|
|
||||||
|
lxc_list_del(it);
|
||||||
|
|
||||||
|
if (netdev->link)
|
||||||
|
free(netdev->link);
|
||||||
|
if (netdev->name)
|
||||||
|
free(netdev->name);
|
||||||
|
if (netdev->upscript)
|
||||||
|
free(netdev->upscript);
|
||||||
|
if (netdev->hwaddr)
|
||||||
|
free(netdev->hwaddr);
|
||||||
|
if (netdev->mtu)
|
||||||
|
free(netdev->mtu);
|
||||||
|
if (netdev->ipv4_gateway)
|
||||||
|
free(netdev->ipv4_gateway);
|
||||||
|
if (netdev->ipv6_gateway)
|
||||||
|
free(netdev->ipv6_gateway);
|
||||||
|
lxc_list_for_each_safe(it2, &netdev->ipv4, next) {
|
||||||
|
lxc_list_del(it2);
|
||||||
|
free(it2->elem);
|
||||||
|
free(it2);
|
||||||
|
}
|
||||||
|
lxc_list_for_each_safe(it2, &netdev->ipv6, next) {
|
||||||
|
lxc_list_del(it2);
|
||||||
|
free(it2->elem);
|
||||||
|
free(it2);
|
||||||
|
}
|
||||||
|
free(netdev);
|
||||||
|
free(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we get passed in something like '0', '0.ipv4' or '1.ipv6' */
|
||||||
|
int lxc_clear_nic(struct lxc_conf *c, const char *key)
|
||||||
|
{
|
||||||
|
char *p1;
|
||||||
|
int ret, idx, i;
|
||||||
|
struct lxc_list *it;
|
||||||
|
struct lxc_netdev *netdev;
|
||||||
|
|
||||||
|
p1 = index(key, '.');
|
||||||
|
if (!p1 || *(p1+1) == '\0')
|
||||||
|
p1 = NULL;
|
||||||
|
|
||||||
|
ret = sscanf(key, "%d", &idx);
|
||||||
|
if (ret != 1) return -1;
|
||||||
|
if (idx < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
lxc_list_for_each(it, &c->network) {
|
||||||
|
if (i == idx)
|
||||||
|
break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i < idx) // we don't have that many nics defined
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!it || !it->elem)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
netdev = it->elem;
|
||||||
|
|
||||||
|
if (!p1) {
|
||||||
|
lxc_remove_nic(it);
|
||||||
|
} else if (strcmp(p1, "ipv4") == 0) {
|
||||||
|
struct lxc_list *it2,*next;
|
||||||
|
lxc_list_for_each_safe(it2, &netdev->ipv4, next) {
|
||||||
|
lxc_list_del(it2);
|
||||||
|
free(it2->elem);
|
||||||
|
free(it2);
|
||||||
|
}
|
||||||
|
} else if (strcmp(p1, "ipv6") == 0) {
|
||||||
|
struct lxc_list *it2,*next;
|
||||||
|
lxc_list_for_each_safe(it2, &netdev->ipv6, next) {
|
||||||
|
lxc_list_del(it2);
|
||||||
|
free(it2->elem);
|
||||||
|
free(it2);
|
||||||
|
}
|
||||||
|
} else if (strcmp(p1, "link") == 0) {
|
||||||
|
if (netdev->link) {
|
||||||
|
free(netdev->link);
|
||||||
|
netdev->link = NULL;
|
||||||
|
}
|
||||||
|
} else if (strcmp(p1, "name") == 0) {
|
||||||
|
if (netdev->name) {
|
||||||
|
free(netdev->name);
|
||||||
|
netdev->name = NULL;
|
||||||
|
}
|
||||||
|
} else if (strcmp(p1, "script.up") == 0) {
|
||||||
|
if (netdev->upscript) {
|
||||||
|
free(netdev->upscript);
|
||||||
|
netdev->upscript = NULL;
|
||||||
|
}
|
||||||
|
} else if (strcmp(p1, "hwaddr") == 0) {
|
||||||
|
if (netdev->hwaddr) {
|
||||||
|
free(netdev->hwaddr);
|
||||||
|
netdev->hwaddr = NULL;
|
||||||
|
}
|
||||||
|
} else if (strcmp(p1, "mtu") == 0) {
|
||||||
|
if (netdev->mtu) {
|
||||||
|
free(netdev->mtu);
|
||||||
|
netdev->mtu = NULL;
|
||||||
|
}
|
||||||
|
} else if (strcmp(p1, "ipv4_gateway") == 0) {
|
||||||
|
if (netdev->ipv4_gateway) {
|
||||||
|
free(netdev->ipv4_gateway);
|
||||||
|
netdev->ipv4_gateway = NULL;
|
||||||
|
}
|
||||||
|
} else if (strcmp(p1, "ipv6_gateway") == 0) {
|
||||||
|
if (netdev->ipv6_gateway) {
|
||||||
|
free(netdev->ipv6_gateway);
|
||||||
|
netdev->ipv6_gateway = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lxc_clear_config_network(struct lxc_conf *c)
|
||||||
|
{
|
||||||
|
struct lxc_list *it,*next;
|
||||||
|
lxc_list_for_each_safe(it, &c->network, next) {
|
||||||
|
lxc_remove_nic(it);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lxc_clear_config_caps(struct lxc_conf *c)
|
||||||
|
{
|
||||||
|
struct lxc_list *it,*next;
|
||||||
|
|
||||||
|
lxc_list_for_each_safe(it, &c->caps, next) {
|
||||||
|
lxc_list_del(it);
|
||||||
|
free(it->elem);
|
||||||
|
free(it);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lxc_clear_cgroups(struct lxc_conf *c, const char *key)
|
||||||
|
{
|
||||||
|
struct lxc_list *it,*next;
|
||||||
|
bool all = false;
|
||||||
|
const char *k = key + 11;
|
||||||
|
|
||||||
|
if (strcmp(key, "lxc.cgroup") == 0)
|
||||||
|
all = true;
|
||||||
|
|
||||||
|
lxc_list_for_each_safe(it, &c->cgroup, next) {
|
||||||
|
struct lxc_cgroup *cg = it->elem;
|
||||||
|
if (!all && strcmp(cg->subsystem, k) != 0)
|
||||||
|
continue;
|
||||||
|
lxc_list_del(it);
|
||||||
|
free(cg->subsystem);
|
||||||
|
free(cg->value);
|
||||||
|
free(cg);
|
||||||
|
free(it);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lxc_clear_mount_entries(struct lxc_conf *c)
|
||||||
|
{
|
||||||
|
struct lxc_list *it,*next;
|
||||||
|
|
||||||
|
lxc_list_for_each_safe(it, &c->mount_list, next) {
|
||||||
|
lxc_list_del(it);
|
||||||
|
free(it->elem);
|
||||||
|
free(it);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lxc_clear_hooks(struct lxc_conf *c, const char *key)
|
||||||
|
{
|
||||||
|
struct lxc_list *it,*next;
|
||||||
|
bool all = false, done = false;
|
||||||
|
const char *k = key + 9;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (strcmp(key, "lxc.hook") == 0)
|
||||||
|
all = true;
|
||||||
|
|
||||||
|
for (i=0; i<NUM_LXC_HOOKS; i++) {
|
||||||
|
if (all || strcmp(k, lxchook_names[i]) == 0) {
|
||||||
|
lxc_list_for_each_safe(it, &c->hooks[i], next) {
|
||||||
|
lxc_list_del(it);
|
||||||
|
free(it->elem);
|
||||||
|
free(it);
|
||||||
|
}
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!done) {
|
||||||
|
ERROR("Invalid hook key: %s", key);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lxc_clear_saved_nics(struct lxc_conf *conf)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!conf->num_savednics)
|
||||||
|
return;
|
||||||
|
for (i=0; i < conf->num_savednics; i++)
|
||||||
|
free(conf->saved_nics[i].orig_name);
|
||||||
|
conf->saved_nics = 0;
|
||||||
|
free(conf->saved_nics);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lxc_conf_free(struct lxc_conf *conf)
|
||||||
|
{
|
||||||
|
if (!conf)
|
||||||
|
return;
|
||||||
|
if (conf->console.path)
|
||||||
|
free(conf->console.path);
|
||||||
|
if (conf->rootfs.mount != default_rootfs_mount)
|
||||||
|
free(conf->rootfs.mount);
|
||||||
|
if (conf->rootfs.path)
|
||||||
|
free(conf->rootfs.path);
|
||||||
|
if (conf->utsname)
|
||||||
|
free(conf->utsname);
|
||||||
|
if (conf->ttydir)
|
||||||
|
free(conf->ttydir);
|
||||||
|
if (conf->fstab)
|
||||||
|
free(conf->fstab);
|
||||||
|
if (conf->logfile)
|
||||||
|
free(conf->logfile);
|
||||||
|
lxc_clear_config_network(conf);
|
||||||
|
#if HAVE_APPARMOR
|
||||||
|
if (conf->aa_profile)
|
||||||
|
free(conf->aa_profile);
|
||||||
|
#endif
|
||||||
|
lxc_seccomp_free(conf);
|
||||||
|
lxc_clear_config_caps(conf);
|
||||||
|
lxc_clear_cgroups(conf, "lxc.cgroup");
|
||||||
|
lxc_clear_hooks(conf, "lxc.hook");
|
||||||
|
lxc_clear_mount_entries(conf);
|
||||||
|
lxc_clear_saved_nics(conf);
|
||||||
|
free(conf);
|
||||||
|
}
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#define _conf_h
|
#define _conf_h
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <net/if.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
@ -31,6 +32,10 @@
|
|||||||
|
|
||||||
#include <lxc/start.h> /* for lxc_handler */
|
#include <lxc/start.h> /* for lxc_handler */
|
||||||
|
|
||||||
|
#if HAVE_SCMP_FILTER_CTX
|
||||||
|
typedef void * scmp_filter_ctx;
|
||||||
|
#endif
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
LXC_NET_EMPTY,
|
LXC_NET_EMPTY,
|
||||||
LXC_NET_VETH,
|
LXC_NET_VETH,
|
||||||
@ -76,6 +81,7 @@ struct lxc_route6 {
|
|||||||
|
|
||||||
struct ifla_veth {
|
struct ifla_veth {
|
||||||
char *pair; /* pair name */
|
char *pair; /* pair name */
|
||||||
|
char veth1[IFNAMSIZ]; /* needed for deconf */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ifla_vlan {
|
struct ifla_vlan {
|
||||||
@ -103,6 +109,7 @@ union netdev_p {
|
|||||||
* @ipv4 : a list of ipv4 addresses to be set on the network device
|
* @ipv4 : a list of ipv4 addresses to be set on the network device
|
||||||
* @ipv6 : a list of ipv6 addresses to be set on the network device
|
* @ipv6 : a list of ipv6 addresses to be set on the network device
|
||||||
* @upscript : a script filename to be executed during interface configuration
|
* @upscript : a script filename to be executed during interface configuration
|
||||||
|
* @downscript : a script filename to be executed during interface destruction
|
||||||
*/
|
*/
|
||||||
struct lxc_netdev {
|
struct lxc_netdev {
|
||||||
int type;
|
int type;
|
||||||
@ -120,6 +127,7 @@ struct lxc_netdev {
|
|||||||
struct in6_addr *ipv6_gateway;
|
struct in6_addr *ipv6_gateway;
|
||||||
bool ipv6_gateway_auto;
|
bool ipv6_gateway_auto;
|
||||||
char *upscript;
|
char *upscript;
|
||||||
|
char *downscript;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -203,8 +211,15 @@ struct lxc_rootfs {
|
|||||||
#endif
|
#endif
|
||||||
*/
|
*/
|
||||||
enum lxchooks {
|
enum lxchooks {
|
||||||
LXCHOOK_PRESTART, LXCHOOK_MOUNT, LXCHOOK_START,
|
LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_START,
|
||||||
LXCHOOK_POSTSTOP, NUM_LXC_HOOKS};
|
LXCHOOK_POSTSTOP, NUM_LXC_HOOKS};
|
||||||
|
extern char *lxchook_names[NUM_LXC_HOOKS];
|
||||||
|
|
||||||
|
struct saved_nic {
|
||||||
|
int ifindex;
|
||||||
|
char *orig_name;
|
||||||
|
};
|
||||||
|
|
||||||
struct lxc_conf {
|
struct lxc_conf {
|
||||||
char *fstab;
|
char *fstab;
|
||||||
int tty;
|
int tty;
|
||||||
@ -215,6 +230,8 @@ struct lxc_conf {
|
|||||||
struct utsname *utsname;
|
struct utsname *utsname;
|
||||||
struct lxc_list cgroup;
|
struct lxc_list cgroup;
|
||||||
struct lxc_list network;
|
struct lxc_list network;
|
||||||
|
struct saved_nic *saved_nics;
|
||||||
|
int num_savednics;
|
||||||
struct lxc_list mount_list;
|
struct lxc_list mount_list;
|
||||||
struct lxc_list caps;
|
struct lxc_list caps;
|
||||||
struct lxc_tty_info tty_info;
|
struct lxc_tty_info tty_info;
|
||||||
@ -226,9 +243,18 @@ struct lxc_conf {
|
|||||||
#if HAVE_APPARMOR
|
#if HAVE_APPARMOR
|
||||||
char *aa_profile;
|
char *aa_profile;
|
||||||
#endif
|
#endif
|
||||||
|
char *logfile;
|
||||||
|
int loglevel;
|
||||||
|
|
||||||
#if HAVE_APPARMOR /* || HAVE_SELINUX || HAVE_SMACK */
|
#if HAVE_APPARMOR /* || HAVE_SELINUX || HAVE_SMACK */
|
||||||
int lsm_umount_proc;
|
int lsm_umount_proc;
|
||||||
#endif
|
#endif
|
||||||
|
char *seccomp; // filename with the seccomp rules
|
||||||
|
#if HAVE_SCMP_FILTER_CTX
|
||||||
|
scmp_filter_ctx *seccomp_ctx;
|
||||||
|
#endif
|
||||||
|
int maincmd_fd;
|
||||||
|
int autodev; // if 1, mount and fill a /dev at start
|
||||||
};
|
};
|
||||||
|
|
||||||
int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf);
|
int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf);
|
||||||
@ -237,20 +263,30 @@ int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf);
|
|||||||
* Initialize the lxc configuration structure
|
* Initialize the lxc configuration structure
|
||||||
*/
|
*/
|
||||||
extern struct lxc_conf *lxc_conf_init(void);
|
extern struct lxc_conf *lxc_conf_init(void);
|
||||||
|
extern void lxc_conf_free(struct lxc_conf *conf);
|
||||||
|
|
||||||
extern int pin_rootfs(const char *rootfs);
|
extern int pin_rootfs(const char *rootfs);
|
||||||
|
|
||||||
extern int lxc_create_network(struct lxc_handler *handler);
|
extern int lxc_create_network(struct lxc_handler *handler);
|
||||||
extern void lxc_delete_network(struct lxc_list *networks);
|
extern void lxc_delete_network(struct lxc_handler *handler);
|
||||||
extern int lxc_assign_network(struct lxc_list *networks, pid_t pid);
|
extern int lxc_assign_network(struct lxc_list *networks, pid_t pid);
|
||||||
extern int lxc_find_gateway_addresses(struct lxc_handler *handler);
|
extern int lxc_find_gateway_addresses(struct lxc_handler *handler);
|
||||||
|
|
||||||
extern int lxc_create_tty(const char *name, struct lxc_conf *conf);
|
extern int lxc_create_tty(const char *name, struct lxc_conf *conf);
|
||||||
extern void lxc_delete_tty(struct lxc_tty_info *tty_info);
|
extern void lxc_delete_tty(struct lxc_tty_info *tty_info);
|
||||||
|
|
||||||
|
extern int lxc_clear_config_network(struct lxc_conf *c);
|
||||||
|
extern int lxc_clear_nic(struct lxc_conf *c, const char *key);
|
||||||
|
extern int lxc_clear_config_caps(struct lxc_conf *c);
|
||||||
|
extern int lxc_clear_cgroups(struct lxc_conf *c, const char *key);
|
||||||
|
extern int lxc_clear_mount_entries(struct lxc_conf *c);
|
||||||
|
extern int lxc_clear_hooks(struct lxc_conf *c, const char *key);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Configure the container from inside
|
* Configure the container from inside
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern int lxc_setup(const char *name, struct lxc_conf *lxc_conf);
|
extern int lxc_setup(const char *name, struct lxc_conf *lxc_conf);
|
||||||
|
|
||||||
|
extern void lxc_rename_phys_nics_on_shutdown(struct lxc_conf *conf);
|
||||||
#endif
|
#endif
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -27,6 +27,15 @@
|
|||||||
struct lxc_conf;
|
struct lxc_conf;
|
||||||
struct lxc_list;
|
struct lxc_list;
|
||||||
|
|
||||||
|
typedef int (*config_cb)(const char *, const char *, struct lxc_conf *);
|
||||||
|
struct lxc_config_t {
|
||||||
|
char *name;
|
||||||
|
config_cb cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct lxc_config_t *lxc_getconfig(const char *key);
|
||||||
|
extern int lxc_list_nicconfigs(struct lxc_conf *c, const char *key, char *retv, int inlen);
|
||||||
|
extern int lxc_listconfigs(char *retv, int inlen);
|
||||||
extern int lxc_config_read(const char *file, struct lxc_conf *conf);
|
extern int lxc_config_read(const char *file, struct lxc_conf *conf);
|
||||||
extern int lxc_config_readline(char *buffer, struct lxc_conf *conf);
|
extern int lxc_config_readline(char *buffer, struct lxc_conf *conf);
|
||||||
|
|
||||||
@ -37,4 +46,7 @@ extern int lxc_config_define_load(struct lxc_list *defines,
|
|||||||
/* needed for lxc-attach */
|
/* needed for lxc-attach */
|
||||||
extern signed long lxc_config_parse_arch(const char *arch);
|
extern signed long lxc_config_parse_arch(const char *arch);
|
||||||
|
|
||||||
|
extern int lxc_get_config_item(struct lxc_conf *c, const char *key, char *retv, int inlen);
|
||||||
|
extern int lxc_clear_config_item(struct lxc_conf *c, const char *key);
|
||||||
|
extern void write_config(FILE *fout, struct lxc_conf *c);
|
||||||
#endif
|
#endif
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "start.h"
|
#include "start.h"
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ directory=$(readlink -f "$lxc_path")
|
|||||||
for i in "$@"; do
|
for i in "$@"; do
|
||||||
case $i in
|
case $i in
|
||||||
--help)
|
--help)
|
||||||
help; exit 1;;
|
help; exit;;
|
||||||
--active)
|
--active)
|
||||||
get_parent_cgroup; directory="$parent_cgroup"; shift;;
|
get_parent_cgroup; directory="$parent_cgroup"; shift;;
|
||||||
--)
|
--)
|
||||||
@ -90,10 +90,5 @@ if [ ! -z "$directory" ]; then
|
|||||||
containers=$(find $directory -mindepth 1 -maxdepth 1 -type d -printf "%f\n" 2>/dev/null)
|
containers=$(find $directory -mindepth 1 -maxdepth 1 -type d -printf "%f\n" 2>/dev/null)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$containers" ]; then
|
|
||||||
echo "$(basename $0): no containers found" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "$directory"
|
cd "$directory"
|
||||||
ls -d $@ -- $containers
|
ls -d $@ -- $containers
|
@ -14,6 +14,11 @@ struct lxc_list {
|
|||||||
__iterator != __list; \
|
__iterator != __list; \
|
||||||
__iterator = __iterator->next)
|
__iterator = __iterator->next)
|
||||||
|
|
||||||
|
#define lxc_list_for_each_safe(__iterator, __list, __next) \
|
||||||
|
for (__iterator = (__list)->next, __next = __iterator->next; \
|
||||||
|
__iterator != __list; \
|
||||||
|
__iterator = __next, __next = __next->next)
|
||||||
|
|
||||||
static inline void lxc_list_init(struct lxc_list *list)
|
static inline void lxc_list_init(struct lxc_list *list)
|
||||||
{
|
{
|
||||||
list->elem = NULL;
|
list->elem = NULL;
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
|
|
||||||
int lxc_log_fd = -1;
|
int lxc_log_fd = -1;
|
||||||
static char log_prefix[LXC_LOG_PREFIX_SIZE] = "lxc";
|
static char log_prefix[LXC_LOG_PREFIX_SIZE] = "lxc";
|
||||||
|
int lxc_loglevel_specified = 0;
|
||||||
|
|
||||||
lxc_log_define(lxc_log, lxc);
|
lxc_log_define(lxc_log, lxc);
|
||||||
|
|
||||||
@ -153,7 +154,11 @@ extern int lxc_log_init(const char *file, const char *priority,
|
|||||||
{
|
{
|
||||||
int lxc_priority = LXC_LOG_PRIORITY_ERROR;
|
int lxc_priority = LXC_LOG_PRIORITY_ERROR;
|
||||||
|
|
||||||
|
if (lxc_log_fd != -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (priority) {
|
if (priority) {
|
||||||
|
lxc_loglevel_specified = 1;
|
||||||
lxc_priority = lxc_log_priority_to_int(priority);
|
lxc_priority = lxc_log_priority_to_int(priority);
|
||||||
|
|
||||||
if (lxc_priority == LXC_LOG_PRIORITY_NOTSET) {
|
if (lxc_priority == LXC_LOG_PRIORITY_NOTSET) {
|
||||||
@ -185,3 +190,39 @@ extern int lxc_log_init(const char *file, const char *priority,
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is called when we read a lxc.loglevel entry in a lxc.conf file. This
|
||||||
|
* happens after processing command line arguments, which override the .conf
|
||||||
|
* settings. So only set the level if previously unset.
|
||||||
|
*/
|
||||||
|
extern int lxc_log_set_level(int level)
|
||||||
|
{
|
||||||
|
if (lxc_loglevel_specified)
|
||||||
|
return 0;
|
||||||
|
if (level < 0 || level >= LXC_LOG_PRIORITY_NOTSET) {
|
||||||
|
ERROR("invalid log priority %d", level);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
lxc_log_category_lxc.priority = level;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is called when we read a lxc.logfile entry in a lxc.conf file. This
|
||||||
|
* happens after processing command line arguments, which override the .conf
|
||||||
|
* settings. So only set the logfile if previously unset.
|
||||||
|
*/
|
||||||
|
extern int lxc_log_set_file(char *fname)
|
||||||
|
{
|
||||||
|
if (lxc_log_fd != -1) {
|
||||||
|
INFO("Configuration file was specified on command line, configuration file entry being ignored");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
lxc_log_fd = log_open(fname);
|
||||||
|
if (lxc_log_fd == -1) {
|
||||||
|
ERROR("failed to open log file %s\n", fname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
#define LXC_LOG_BUFFER_SIZE 512
|
#define LXC_LOG_BUFFER_SIZE 512
|
||||||
|
|
||||||
/* predefined priorities. */
|
/* predefined priorities. */
|
||||||
enum {
|
enum lxc_loglevel {
|
||||||
LXC_LOG_PRIORITY_TRACE,
|
LXC_LOG_PRIORITY_TRACE,
|
||||||
LXC_LOG_PRIORITY_DEBUG,
|
LXC_LOG_PRIORITY_DEBUG,
|
||||||
LXC_LOG_PRIORITY_INFO,
|
LXC_LOG_PRIORITY_INFO,
|
||||||
@ -291,4 +291,6 @@ extern int lxc_log_init(const char *file, const char *priority,
|
|||||||
const char *prefix, int quiet);
|
const char *prefix, int quiet);
|
||||||
|
|
||||||
extern void lxc_log_setprefix(const char *a_prefix);
|
extern void lxc_log_setprefix(const char *a_prefix);
|
||||||
|
extern int lxc_log_set_level(int level);
|
||||||
|
extern int lxc_log_set_file(char *fname);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
# Allow environment variables to override grep and config
|
# Allow environment variables to override grep and config
|
||||||
: ${CONFIG:=/proc/config.gz}
|
: ${CONFIG:=/proc/config.gz}
|
||||||
: ${GREP:=zgrep}
|
: ${GREP:=zgrep}
|
||||||
|
|
||||||
SETCOLOR_SUCCESS="echo -en \\033[1;32m"
|
SETCOLOR_SUCCESS="printf \\e[1;32m"
|
||||||
SETCOLOR_FAILURE="echo -en \\033[1;31m"
|
SETCOLOR_FAILURE="printf \\e[1;31m"
|
||||||
SETCOLOR_WARNING="echo -en \\033[1;33m"
|
SETCOLOR_WARNING="printf \\e[1;33m"
|
||||||
SETCOLOR_NORMAL="echo -en \\033[0;39m"
|
SETCOLOR_NORMAL="printf \\e[0;39m"
|
||||||
|
|
||||||
is_set() {
|
is_set() {
|
||||||
$GREP -q "$1=[y|m]" $CONFIG
|
$GREP -q "$1=[y|m]" $CONFIG
|
||||||
@ -21,12 +21,12 @@ is_enabled() {
|
|||||||
RES=$?
|
RES=$?
|
||||||
|
|
||||||
if [ $RES -eq 0 ]; then
|
if [ $RES -eq 0 ]; then
|
||||||
$SETCOLOR_SUCCESS && echo -e "enabled" && $SETCOLOR_NORMAL
|
$SETCOLOR_SUCCESS && echo "enabled" && $SETCOLOR_NORMAL
|
||||||
else
|
else
|
||||||
if [ ! -z "$mandatory" -a "$mandatory" = yes ]; then
|
if [ ! -z "$mandatory" -a "$mandatory" = yes ]; then
|
||||||
$SETCOLOR_FAILURE && echo -e "required" && $SETCOLOR_NORMAL
|
$SETCOLOR_FAILURE && echo "required" && $SETCOLOR_NORMAL
|
||||||
else
|
else
|
||||||
$SETCOLOR_WARNING && echo -e "missing" && $SETCOLOR_NORMAL
|
$SETCOLOR_WARNING && echo "missing" && $SETCOLOR_NORMAL
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@ -68,40 +68,45 @@ print_cgroups() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CGROUP_MNT_PATH=`print_cgroups cgroup /proc/self/mounts | head -1`
|
CGROUP_MNT_PATH=`print_cgroups cgroup /proc/self/mounts | head -1`
|
||||||
|
|
||||||
echo -n "Cgroup: " && is_enabled CONFIG_CGROUPS yes
|
|
||||||
|
|
||||||
if [ -f $CGROUP_MNT_PATH/cgroup.clone_children ]; then
|
|
||||||
echo -n "Cgroup clone_children flag: " &&
|
|
||||||
$SETCOLOR_SUCCESS && echo -e "enabled" && $SETCOLOR_NORMAL
|
|
||||||
else
|
|
||||||
echo -n "Cgroup namespace: " && is_enabled CONFIG_CGROUP_NS yes
|
|
||||||
fi
|
|
||||||
echo -n "Cgroup device: " && is_enabled CONFIG_CGROUP_DEVICE
|
|
||||||
echo -n "Cgroup sched: " && is_enabled CONFIG_CGROUP_SCHED
|
|
||||||
echo -n "Cgroup cpu account: " && is_enabled CONFIG_CGROUP_CPUACCT
|
|
||||||
echo -n "Cgroup memory controller: " && is_enabled CONFIG_CGROUP_MEM_RES_CTLR
|
|
||||||
is_set CONFIG_SMP && echo -n "Cgroup cpuset: " && is_enabled CONFIG_CPUSETS
|
|
||||||
echo
|
|
||||||
echo "--- Misc ---"
|
|
||||||
echo -n "Veth pair device: " && is_enabled CONFIG_VETH
|
|
||||||
echo -n "Macvlan: " && is_enabled CONFIG_MACVLAN
|
|
||||||
echo -n "Vlan: " && is_enabled CONFIG_VLAN_8021Q
|
|
||||||
KVER_MAJOR=$($GREP '^# Linux' $CONFIG | \
|
KVER_MAJOR=$($GREP '^# Linux' $CONFIG | \
|
||||||
sed -r 's/.* ([0-9])\.[0-9]{1,2}\.[0-9]{1,3}.*/\1/')
|
sed -r 's/.* ([0-9])\.[0-9]{1,2}\.[0-9]{1,3}.*/\1/')
|
||||||
if [[ $KVER_MAJOR == 2 ]]; then
|
if [ "$KVER_MAJOR" = "2" ]; then
|
||||||
KVER_MINOR=$($GREP '^# Linux' $CONFIG | \
|
KVER_MINOR=$($GREP '^# Linux' $CONFIG | \
|
||||||
sed -r 's/.* 2.6.([0-9]{2}).*/\1/')
|
sed -r 's/.* 2.6.([0-9]{2}).*/\1/')
|
||||||
else
|
else
|
||||||
KVER_MINOR=$($GREP '^# Linux' $CONFIG | \
|
KVER_MINOR=$($GREP '^# Linux' $CONFIG | \
|
||||||
sed -r 's/.* [0-9]\.([0-9]{1,3})\.[0-9]{1,3}.*/\1/')
|
sed -r 's/.* [0-9]\.([0-9]{1,3})\.[0-9]{1,3}.*/\1/')
|
||||||
fi
|
fi
|
||||||
echo -n "File capabilities: " &&
|
|
||||||
( [[ ${KVER_MAJOR} == 2 && ${KVER_MINOR} < 33 ]] &&
|
echo -n "Cgroup: " && is_enabled CONFIG_CGROUPS yes
|
||||||
is_enabled CONFIG_SECURITY_FILE_CAPABILITIES ) ||
|
|
||||||
( [[ ( ${KVER_MAJOR} == 2 && ${KVER_MINOR} > 32 ) ||
|
if [ -f $CGROUP_MNT_PATH/cgroup.clone_children ]; then
|
||||||
${KVER_MAJOR} > 2 ]] && $SETCOLOR_SUCCESS &&
|
echo -n "Cgroup clone_children flag: " &&
|
||||||
echo -e "enabled" && $SETCOLOR_NORMAL )
|
$SETCOLOR_SUCCESS && echo "enabled" && $SETCOLOR_NORMAL
|
||||||
|
else
|
||||||
|
echo -n "Cgroup namespace: " && is_enabled CONFIG_CGROUP_NS yes
|
||||||
|
fi
|
||||||
|
echo -n "Cgroup device: " && is_enabled CONFIG_CGROUP_DEVICE
|
||||||
|
echo -n "Cgroup sched: " && is_enabled CONFIG_CGROUP_SCHED
|
||||||
|
echo -n "Cgroup cpu account: " && is_enabled CONFIG_CGROUP_CPUACCT
|
||||||
|
echo -n "Cgroup memory controller: "
|
||||||
|
if [ $KVER_MAJOR -ge 3 -a $KVER_MINOR -ge 6 ]; then
|
||||||
|
is_enabled CONFIG_MEMCG
|
||||||
|
else
|
||||||
|
is_enabled CONFIG_CGROUP_MEM_RES_CTLR
|
||||||
|
fi
|
||||||
|
is_set CONFIG_SMP && echo -n "Cgroup cpuset: " && is_enabled CONFIG_CPUSETS
|
||||||
|
echo
|
||||||
|
echo "--- Misc ---"
|
||||||
|
echo -n "Veth pair device: " && is_enabled CONFIG_VETH
|
||||||
|
echo -n "Macvlan: " && is_enabled CONFIG_MACVLAN
|
||||||
|
echo -n "Vlan: " && is_enabled CONFIG_VLAN_8021Q
|
||||||
|
echo -n "File capabilities: " && \
|
||||||
|
( [ "${KVER_MAJOR}" = 2 ] && [ ${KVER_MINOR} -lt 33 ] && \
|
||||||
|
is_enabled CONFIG_SECURITY_FILE_CAPABILITIES ) || \
|
||||||
|
( ( [ "${KVER_MAJOR}" = "2" ] && [ ${KVER_MINOR} -gt 32 ] ) || \
|
||||||
|
[ ${KVER_MAJOR} -gt 2 ] && $SETCOLOR_SUCCESS && \
|
||||||
|
echo "enabled" && $SETCOLOR_NORMAL )
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Note : Before booting a new kernel, you can check its configuration"
|
echo "Note : Before booting a new kernel, you can check its configuration"
|
||||||
|
@ -225,7 +225,7 @@ if [ -b $oldroot ]; then
|
|||||||
mkfs -t $fstype /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new
|
mkfs -t $fstype /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new
|
||||||
mount /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new $rootfs || { echo "$(basename $0): failed to mount new rootfs" >&2; false; }
|
mount /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new $rootfs || { echo "$(basename $0): failed to mount new rootfs" >&2; false; }
|
||||||
mounted=1
|
mounted=1
|
||||||
rsync -ax ${rootfs}_snapshot/ ${rootfs}/ || { echo "$(basename $0): copying data to new lv failed" >&2; false; }
|
rsync -Hax ${rootfs}_snapshot/ ${rootfs}/ || { echo "$(basename $0): copying data to new lv failed" >&2; false; }
|
||||||
umount ${rootfs}_snapshot
|
umount ${rootfs}_snapshot
|
||||||
rmdir ${rootfs}_snapshot
|
rmdir ${rootfs}_snapshot
|
||||||
lvremove -f $lxc_vg/${lxc_lv_prefix}${lxc_new}_snapshot
|
lvremove -f $lxc_vg/${lxc_lv_prefix}${lxc_new}_snapshot
|
||||||
@ -252,7 +252,7 @@ else
|
|||||||
frozen=1
|
frozen=1
|
||||||
fi
|
fi
|
||||||
mkdir -p $rootfs/
|
mkdir -p $rootfs/
|
||||||
rsync -ax $oldroot/ $rootfs/
|
rsync -Hax $oldroot/ $rootfs/
|
||||||
echo "lxc.rootfs = $rootfs" >> $lxc_path/$lxc_new/config
|
echo "lxc.rootfs = $rootfs" >> $lxc_path/$lxc_new/config
|
||||||
if [ $container_running = "True" ]; then
|
if [ $container_running = "True" ]; then
|
||||||
lxc-unfreeze -n $lxc_orig
|
lxc-unfreeze -n $lxc_orig
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# lxc: linux Container library
|
# lxc: linux Container library
|
||||||
@ -26,6 +26,7 @@ usage() {
|
|||||||
echo >&2
|
echo >&2
|
||||||
echo "where FS_OPTIONS is one of:" >&2
|
echo "where FS_OPTIONS is one of:" >&2
|
||||||
echo " -B none" >&2
|
echo " -B none" >&2
|
||||||
|
echo " -B dir [--dir rootfs_dir]" >&2
|
||||||
echo " -B lvm [--lvname LV_NAME] [--vgname VG_NAME] [--fstype FS_TYPE]" >&2
|
echo " -B lvm [--lvname LV_NAME] [--vgname VG_NAME] [--fstype FS_TYPE]" >&2
|
||||||
echo " [--fssize FS_SIZE]" >&2
|
echo " [--fssize FS_SIZE]" >&2
|
||||||
echo " -B btrfs" >&2
|
echo " -B btrfs" >&2
|
||||||
@ -43,25 +44,35 @@ help() {
|
|||||||
echo " -B BACKING_STORE alter the container backing store (default: none)" >&2
|
echo " -B BACKING_STORE alter the container backing store (default: none)" >&2
|
||||||
echo " --lvname LV_NAME specify the LVM logical volume name" >&2
|
echo " --lvname LV_NAME specify the LVM logical volume name" >&2
|
||||||
echo " (default: container name)" >&2
|
echo " (default: container name)" >&2
|
||||||
|
echo " --dir ROOTFS_DIR specify path for custom rootfs directory location" >&2
|
||||||
echo " --vgname VG_NAME specify the LVM volume group name (default: lxc)" >&2
|
echo " --vgname VG_NAME specify the LVM volume group name (default: lxc)" >&2
|
||||||
echo " --fstype FS_TYPE specify the filesystem type (default: ext4)" >&2
|
echo " --fstype FS_TYPE specify the filesystem type (default: ext4)" >&2
|
||||||
echo " --fssize FS_SIZE specify the filesystem size (default: 500M)" >&2
|
echo " --fssize FS_SIZE specify the filesystem size (default: 500M)" >&2
|
||||||
echo >&2
|
echo >&2
|
||||||
if [ -z $lxc_template ]; then
|
if [ -z "$lxc_template" ]; then
|
||||||
echo "To see template-specific options, specify a template. For example:" >&2
|
echo "To see template-specific options, specify a template. For example:" >&2
|
||||||
echo " $(basename $0) -t ubuntu -h" >&2
|
echo " $(basename $0) -t ubuntu -h" >&2
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
type ${templatedir}/lxc-$lxc_template 2>/dev/null
|
if [ -x ${templatedir}/lxc-$lxc_template ]; then
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo >&2
|
echo >&2
|
||||||
echo "Template-specific options (TEMPLATE_OPTIONS):" >&2
|
echo "Template-specific options (TEMPLATE_OPTIONS):" >&2
|
||||||
${templatedir}/lxc-$lxc_template -h
|
${templatedir}/lxc-$lxc_template -h
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
shortoptions='hn:f:t:B:'
|
usage_err() {
|
||||||
longoptions='help,name:,config:,template:,backingstore:,fstype:,lvname:,vgname:,fssize:'
|
[ -n "$1" ] && echo "$1" >&2
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
optarg_check() {
|
||||||
|
if [ -z "$2" ]; then
|
||||||
|
usage_err "option '$1' requires an argument"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
lxc_path=@LXCPATH@
|
lxc_path=@LXCPATH@
|
||||||
bindir=@BINDIR@
|
bindir=@BINDIR@
|
||||||
templatedir=@LXCTEMPLATEDIR@
|
templatedir=@LXCTEMPLATEDIR@
|
||||||
@ -69,64 +80,70 @@ backingstore=_unset
|
|||||||
fstype=ext4
|
fstype=ext4
|
||||||
fssize=500M
|
fssize=500M
|
||||||
vgname=lxc
|
vgname=lxc
|
||||||
|
custom_rootfs=""
|
||||||
|
|
||||||
getopt=$(getopt -o $shortoptions --longoptions $longoptions -- "$@")
|
while [ $# -gt 0 ]; do
|
||||||
if [ $? != 0 ]; then
|
opt="$1"
|
||||||
usage
|
shift
|
||||||
exit 1;
|
case "$opt" in
|
||||||
fi
|
|
||||||
|
|
||||||
eval set -- "$getopt"
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
case "$1" in
|
|
||||||
-h|--help)
|
-h|--help)
|
||||||
help
|
help
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
-n|--name)
|
-n|--name)
|
||||||
shift
|
optarg_check $opt "$1"
|
||||||
lxc_name=$1
|
lxc_name=$1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-f|--config)
|
-f|--config)
|
||||||
shift
|
optarg_check $opt "$1"
|
||||||
lxc_config=$1
|
lxc_config=$1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-t|--template)
|
-t|--template)
|
||||||
shift
|
optarg_check $opt "$1"
|
||||||
lxc_template=$1
|
lxc_template=$1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-B|--backingstore)
|
-B|--backingstore)
|
||||||
shift
|
optarg_check $opt "$1"
|
||||||
backingstore=$1
|
backingstore=$1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--lvname)
|
--dir)
|
||||||
|
optarg_check $opt "$1"
|
||||||
|
custom_rootfs=$1
|
||||||
shift
|
shift
|
||||||
|
;;
|
||||||
|
--lvname)
|
||||||
|
optarg_check $opt "$1"
|
||||||
lvname=$1
|
lvname=$1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--vgname)
|
--vgname)
|
||||||
shift
|
optarg_check $opt "$1"
|
||||||
vgname=$1
|
vgname=$1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--fstype)
|
--fstype)
|
||||||
shift
|
optarg_check $opt "$1"
|
||||||
fstype=$1
|
fstype=$1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--fssize)
|
--fssize)
|
||||||
shift
|
optarg_check $opt "$1"
|
||||||
fssize=$1
|
fssize=$1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--)
|
--)
|
||||||
shift
|
|
||||||
break;;
|
break;;
|
||||||
|
-?)
|
||||||
|
usage_err "unknown option '$opt'"
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
# split opts -abc into -a -b -c
|
||||||
|
set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
usage
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
@ -137,12 +154,11 @@ done
|
|||||||
# If -h or --help was passed into the container, we'll want to cleanup
|
# If -h or --help was passed into the container, we'll want to cleanup
|
||||||
# afterward
|
# afterward
|
||||||
wantedhelp=0
|
wantedhelp=0
|
||||||
for var in "$@"
|
for var in "$@"; do
|
||||||
do
|
if [ "$var" = "-h" ] || [ "$var" = "--help" ]; then
|
||||||
if [ "$var" = "-h" -o "$var" = "--help" ]; then
|
|
||||||
help
|
help
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
@ -171,9 +187,14 @@ if [ "$(id -u)" != "0" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -n "$custom_rootfs" ] && [ "$backingstore" != "dir" ]; then
|
||||||
|
echo "--dir is only valid with -B dir"
|
||||||
|
fi
|
||||||
|
|
||||||
case "$backingstore" in
|
case "$backingstore" in
|
||||||
lvm|none|btrfs|_unset) :;;
|
dir|lvm|none|btrfs|_unset) :;;
|
||||||
*) echo "$(basename $0): '$backingstore' is not known (try 'none', 'lvm', 'btrfs')" >&2
|
*)
|
||||||
|
echo "$(basename $0): '$backingstore' is not known (try 'none', 'dir', 'lvm', 'btrfs')" >&2
|
||||||
usage
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
@ -186,9 +207,9 @@ fi
|
|||||||
|
|
||||||
rootfs="$lxc_path/$lxc_name/rootfs"
|
rootfs="$lxc_path/$lxc_name/rootfs"
|
||||||
|
|
||||||
if [ "$backingstore" = "_unset" -o "$backingstore" = "btrfs" ]; then
|
if [ "$backingstore" = "_unset" ] || [ "$backingstore" = "btrfs" ]; then
|
||||||
# if no backing store was given, then see if btrfs would work
|
# if no backing store was given, then see if btrfs would work
|
||||||
if which btrfs >/dev/null 2>&1 &&
|
if which btrfs >/dev/null 2>&1 && \
|
||||||
btrfs filesystem df "$lxc_path/" >/dev/null 2>&1; then
|
btrfs filesystem df "$lxc_path/" >/dev/null 2>&1; then
|
||||||
backingstore="btrfs"
|
backingstore="btrfs"
|
||||||
else
|
else
|
||||||
@ -200,12 +221,13 @@ if [ "$backingstore" = "_unset" -o "$backingstore" = "btrfs" ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $backingstore = "lvm" ]; then
|
if [ "$backingstore" = "lvm" ]; then
|
||||||
which vgscan > /dev/null
|
which vgscan > /dev/null
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "$(basename $0): vgscan not found (is lvm2 installed?)" >&2
|
echo "$(basename $0): vgscan not found (is lvm2 installed?)" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
grep -q "\<$fstype\>" /proc/filesystems
|
grep -q "\<$fstype\>" /proc/filesystems
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "$(basename $0): $fstype is not listed in /proc/filesystems" >&2
|
echo "$(basename $0): $fstype is not listed in /proc/filesystems" >&2
|
||||||
@ -225,6 +247,7 @@ if [ $backingstore = "lvm" ]; then
|
|||||||
echo "please delete it (using \"lvremove $rootdev\") and try again" >&2
|
echo "please delete it (using \"lvremove $rootdev\") and try again" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
elif [ "$backingstore" = "btrfs" ]; then
|
elif [ "$backingstore" = "btrfs" ]; then
|
||||||
mkdir "$lxc_path/$lxc_name"
|
mkdir "$lxc_path/$lxc_name"
|
||||||
if ! out=$(btrfs subvolume create "$rootfs" 2>&1); then
|
if ! out=$(btrfs subvolume create "$rootfs" 2>&1); then
|
||||||
@ -234,10 +257,13 @@ elif [ "$backingstore" = "btrfs" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if [ $backingstore = "lvm" ]; then
|
if [ "$backingstore" = "lvm" ]; then
|
||||||
umount $rootfs
|
umount $rootfs
|
||||||
lvremove -f $rootdev
|
lvremove -f $rootdev
|
||||||
|
elif [ "$backingstore" = "btrfs" ]; then
|
||||||
|
btrfs subvolume delete "$rootfs"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
${bindir}/lxc-destroy -n $lxc_name
|
${bindir}/lxc-destroy -n $lxc_name
|
||||||
echo "$(basename $0): aborted" >&2
|
echo "$(basename $0): aborted" >&2
|
||||||
exit 1
|
exit 1
|
||||||
@ -248,18 +274,54 @@ trap cleanup HUP INT TERM
|
|||||||
mkdir -p $lxc_path/$lxc_name
|
mkdir -p $lxc_path/$lxc_name
|
||||||
|
|
||||||
if [ -z "$lxc_config" ]; then
|
if [ -z "$lxc_config" ]; then
|
||||||
touch $lxc_path/$lxc_name/config
|
lxc_config="@SYSCONFDIR@/lxc/lxc.conf"
|
||||||
else
|
echo
|
||||||
if [ ! -r "$lxc_config" ]; then
|
echo "$(basename $0): No config file specified, using the default config $lxc_config"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -r "$lxc_config" ]; then
|
||||||
echo "$(basename $0): '$lxc_config' configuration file not found" >&2
|
echo "$(basename $0): '$lxc_config' configuration file not found" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -z "$lxc_template" ]; then
|
||||||
|
# Allow for a path to be provided as the template name
|
||||||
|
if [ -x "$lxc_template" ]; then
|
||||||
|
template_path=$lxc_template
|
||||||
|
else
|
||||||
|
template_path=${templatedir}/lxc-$lxc_template
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cp $lxc_config $lxc_path/$lxc_name/config
|
if ! [ -x "$template_path" ]; then
|
||||||
|
echo "$(basename $0): unknown template '$lxc_template'" >&2
|
||||||
|
cleanup
|
||||||
|
fi
|
||||||
|
|
||||||
|
sum=$(sha1sum $template_path | cut -d ' ' -f1)
|
||||||
|
echo "# Template used to create this container: $lxc_template" >> $lxc_path/$lxc_name/config
|
||||||
|
if [ -n "$*" ]; then
|
||||||
|
echo "# Parameters passed to the template: $*" >> $lxc_path/$lxc_name/config
|
||||||
|
fi
|
||||||
|
echo "# Checksum of the template script (SHA-1): $sum" >> $lxc_path/$lxc_name/config
|
||||||
|
echo "" >> $lxc_path/$lxc_name/config
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat $lxc_config >> $lxc_path/$lxc_name/config
|
||||||
|
|
||||||
|
if [ -n "$custom_rootfs" ]; then
|
||||||
|
if grep -q "lxc.rootfs" $lxc_path/$lxc_name/config ; then
|
||||||
|
echo "configuration file already specifies a lxc.rootfs"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -d "$custom_rootfs" ]; then
|
||||||
|
echo "specified rootfs ($custom_rootfs) already exists. Bailing."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "lxc.rootfs = $custom_rootfs" >> $lxc_path/$lxc_name/config
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create the fs as needed
|
# Create the fs as needed
|
||||||
if [ $backingstore = "lvm" ]; then
|
if [ "$backingstore" = "lvm" ]; then
|
||||||
[ -d "$rootfs" ] || mkdir $rootfs
|
[ -d "$rootfs" ] || mkdir $rootfs
|
||||||
lvcreate -L $fssize -n $lvname $vgname || exit 1
|
lvcreate -L $fssize -n $lvname $vgname || exit 1
|
||||||
udevadm settle
|
udevadm settle
|
||||||
@ -267,22 +329,8 @@ if [ $backingstore = "lvm" ]; then
|
|||||||
mount -t $fstype $rootdev $rootfs
|
mount -t $fstype $rootdev $rootfs
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -z $lxc_template ]; then
|
if [ ! -z "$lxc_template" ]; then
|
||||||
|
$template_path --path=$lxc_path/$lxc_name --name=$lxc_name $*
|
||||||
type ${templatedir}/lxc-$lxc_template 2>/dev/null
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "$(basename $0): unknown template '$lxc_template'" >&2
|
|
||||||
cleanup
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$lxc_config" ]; then
|
|
||||||
echo "Note: Usually the template option is called with a configuration"
|
|
||||||
echo "file option too, mostly to configure the network."
|
|
||||||
echo "For more information look at lxc.conf (5)"
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
|
|
||||||
${templatedir}/lxc-$lxc_template --path=$lxc_path/$lxc_name --name=$lxc_name $*
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "$(basename $0): failed to execute template '$lxc_template'" >&2
|
echo "$(basename $0): failed to execute template '$lxc_template'" >&2
|
||||||
cleanup
|
cleanup
|
||||||
@ -291,7 +339,7 @@ if [ ! -z $lxc_template ]; then
|
|||||||
echo "'$lxc_template' template installed"
|
echo "'$lxc_template' template installed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $backingstore = "lvm" ]; then
|
if [ "$backingstore" = "lvm" ]; then
|
||||||
echo "Unmounting LVM"
|
echo "Unmounting LVM"
|
||||||
umount $rootfs
|
umount $rootfs
|
||||||
|
|
||||||
|
@ -69,7 +69,8 @@ while true; do
|
|||||||
;;
|
;;
|
||||||
--)
|
--)
|
||||||
shift
|
shift
|
||||||
break;;
|
break
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
usage
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
@ -110,7 +111,7 @@ fi
|
|||||||
# else, ignore it. We'll support deletion of others later.
|
# else, ignore it. We'll support deletion of others later.
|
||||||
rootdev=`grep lxc.rootfs $lxc_path/$lxc_name/config 2>/dev/null | sed -e 's/^[^/]*/\//'`
|
rootdev=`grep lxc.rootfs $lxc_path/$lxc_name/config 2>/dev/null | sed -e 's/^[^/]*/\//'`
|
||||||
if [ -n "$rootdev" ]; then
|
if [ -n "$rootdev" ]; then
|
||||||
if [ -b "$rootdev" ]; then
|
if [ -b "$rootdev" -o -h "$rootdev" ]; then
|
||||||
lvdisplay $rootdev > /dev/null 2>&1
|
lvdisplay $rootdev > /dev/null 2>&1
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo "removing backing store: $rootdev"
|
echo "removing backing store: $rootdev"
|
||||||
@ -126,5 +127,6 @@ if [ -n "$rootdev" ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# recursively remove the container to remove old container configuration
|
# recursively remove the container to remove old container configuration
|
||||||
rm -rf --one-file-system --preserve-root $lxc_path/$lxc_name
|
rm -rf --one-file-system --preserve-root $lxc_path/$lxc_name
|
||||||
|
95
src/lxc/lxc-device
Normal file
95
src/lxc/lxc-device
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
#
|
||||||
|
# lxc-device: Add devices to a running container
|
||||||
|
#
|
||||||
|
# This python implementation is based on the work done in the original
|
||||||
|
# shell implementation done by Serge Hallyn in Ubuntu (and other contributors)
|
||||||
|
#
|
||||||
|
# (C) Copyright Canonical Ltd. 2012
|
||||||
|
#
|
||||||
|
# Authors:
|
||||||
|
# Stéphane Graber <stgraber@ubuntu.com>
|
||||||
|
#
|
||||||
|
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
|
||||||
|
# NOTE: To remove once the API is stabilized
|
||||||
|
import warnings
|
||||||
|
warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import gettext
|
||||||
|
import lxc
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
_ = gettext.gettext
|
||||||
|
gettext.textdomain("lxc-device")
|
||||||
|
|
||||||
|
# Begin parsing the command line
|
||||||
|
parser = argparse.ArgumentParser(description=_("LXC: Manage devices"),
|
||||||
|
formatter_class=argparse.RawTextHelpFormatter)
|
||||||
|
|
||||||
|
# Global arguments
|
||||||
|
parser.add_argument("-n", dest="container", metavar="CONTAINER",
|
||||||
|
help=_("Name of the container to add the device to"),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
# Commands
|
||||||
|
subparsers = parser.add_subparsers()
|
||||||
|
subparser_add = subparsers.add_parser('add', help=_('Add a device'))
|
||||||
|
subparser_add.set_defaults(action="add")
|
||||||
|
|
||||||
|
subparser_add.add_argument(dest="device", metavar="DEVICE",
|
||||||
|
help=_("Add a device "
|
||||||
|
"(path to a node or interface name)"))
|
||||||
|
|
||||||
|
subparser_add.add_argument(dest="name", metavar="NAME", nargs="?",
|
||||||
|
help=_("Use an alternative path or name "
|
||||||
|
"in the container"))
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Some basic checks
|
||||||
|
if not hasattr(args, "action"):
|
||||||
|
parser.error(_("You must specify an action."))
|
||||||
|
|
||||||
|
## The user needs to be uid 0
|
||||||
|
if not os.geteuid() == 0:
|
||||||
|
parser.error(_("You must be root to run this script. Try running: sudo %s"
|
||||||
|
% (sys.argv[0])))
|
||||||
|
|
||||||
|
## Don't rename if no alternative name
|
||||||
|
if not args.name:
|
||||||
|
args.name = args.device
|
||||||
|
|
||||||
|
## Check that the container is ready
|
||||||
|
container = lxc.Container(args.container)
|
||||||
|
if not container.running:
|
||||||
|
parser.error("The container must be running.")
|
||||||
|
|
||||||
|
# Do the work
|
||||||
|
if args.action == "add":
|
||||||
|
if os.path.exists("/sys/class/net/%s/" % args.device):
|
||||||
|
ret = container.add_device_net(args.device, args.name)
|
||||||
|
else:
|
||||||
|
ret = container.add_device_node(args.device, args.name)
|
||||||
|
|
||||||
|
if ret:
|
||||||
|
print("Added '%s' to '%s' as '%s'." %
|
||||||
|
(args.device, container.name, args.name))
|
||||||
|
else:
|
||||||
|
print("Failed to add '%s' to '%s' as '%s'." %
|
||||||
|
(args.device, container.name, args.name))
|
251
src/lxc/lxc-ls
Normal file
251
src/lxc/lxc-ls
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
#
|
||||||
|
# lxc-ls: List containers
|
||||||
|
#
|
||||||
|
# This python implementation is based on the work done in the original
|
||||||
|
# shell implementation done by Serge Hallyn in Ubuntu (and other contributors)
|
||||||
|
#
|
||||||
|
# (C) Copyright Canonical Ltd. 2012
|
||||||
|
#
|
||||||
|
# Authors:
|
||||||
|
# Stéphane Graber <stgraber@ubuntu.com>
|
||||||
|
#
|
||||||
|
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
|
||||||
|
# NOTE: To remove once the API is stabilized
|
||||||
|
import warnings
|
||||||
|
warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import gettext
|
||||||
|
import lxc
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
_ = gettext.gettext
|
||||||
|
gettext.textdomain("lxc-ls")
|
||||||
|
|
||||||
|
|
||||||
|
# Functions used later on
|
||||||
|
def batch(iterable, cols=1):
|
||||||
|
import math
|
||||||
|
|
||||||
|
length = len(iterable)
|
||||||
|
lines = math.ceil(length / cols)
|
||||||
|
|
||||||
|
for line in range(lines):
|
||||||
|
fields = []
|
||||||
|
for col in range(cols):
|
||||||
|
index = line + (col * lines)
|
||||||
|
if index < length:
|
||||||
|
fields.append(iterable[index])
|
||||||
|
yield fields
|
||||||
|
|
||||||
|
|
||||||
|
def getTerminalSize():
|
||||||
|
import os
|
||||||
|
env = os.environ
|
||||||
|
|
||||||
|
def ioctl_GWINSZ(fd):
|
||||||
|
try:
|
||||||
|
import fcntl
|
||||||
|
import termios
|
||||||
|
import struct
|
||||||
|
cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,
|
||||||
|
'1234'))
|
||||||
|
return cr
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
|
||||||
|
cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
|
||||||
|
if not cr:
|
||||||
|
try:
|
||||||
|
fd = os.open(os.ctermid(), os.O_RDONLY)
|
||||||
|
cr = ioctl_GWINSZ(fd)
|
||||||
|
os.close(fd)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not cr:
|
||||||
|
cr = (env.get('LINES', 25), env.get('COLUMNS', 80))
|
||||||
|
|
||||||
|
return int(cr[1]), int(cr[0])
|
||||||
|
|
||||||
|
# Begin parsing the command line
|
||||||
|
parser = argparse.ArgumentParser(description=_("LXC: List containers"),
|
||||||
|
formatter_class=argparse.RawTextHelpFormatter)
|
||||||
|
|
||||||
|
parser.add_argument("-1", dest="one", action="store_true",
|
||||||
|
help=_("list one container per line (default when piped)"))
|
||||||
|
|
||||||
|
parser.add_argument("--active", action="store_true",
|
||||||
|
help=_("list only active containers "
|
||||||
|
"(same as --running --frozen)"))
|
||||||
|
|
||||||
|
parser.add_argument("--frozen", dest="state", action="append_const",
|
||||||
|
const="FROZEN", help=_("list only frozen containers"))
|
||||||
|
|
||||||
|
parser.add_argument("--running", dest="state", action="append_const",
|
||||||
|
const="RUNNING", help=_("list only running containers"))
|
||||||
|
|
||||||
|
parser.add_argument("--stopped", dest="state", action="append_const",
|
||||||
|
const="STOPPED", help=_("list only stopped containers"))
|
||||||
|
|
||||||
|
parser.add_argument("--fancy", action="store_true",
|
||||||
|
help=_("use fancy output"))
|
||||||
|
|
||||||
|
parser.add_argument("--fancy-format", type=str, default="name,state,ipv4,ipv6",
|
||||||
|
help=_("comma separated list of fields to show"))
|
||||||
|
|
||||||
|
parser.add_argument("filter", metavar='FILTER', type=str, nargs="?",
|
||||||
|
help=_("regexp to be applied on the container list"))
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# --active is the same as --running --frozen
|
||||||
|
if args.active:
|
||||||
|
if not args.state:
|
||||||
|
args.state = []
|
||||||
|
args.state += ["RUNNING", "FROZEN"]
|
||||||
|
|
||||||
|
# If the output is piped, default to --one
|
||||||
|
if not sys.stdout.isatty():
|
||||||
|
args.one = True
|
||||||
|
|
||||||
|
# Turn args.fancy_format into a list
|
||||||
|
args.fancy_format = args.fancy_format.strip().split(",")
|
||||||
|
|
||||||
|
# Basic checks
|
||||||
|
## The user needs to be uid 0
|
||||||
|
if not os.geteuid() == 0 and (args.fancy or args.state):
|
||||||
|
parser.error(_("You must be root to access advanced container properties. "
|
||||||
|
"Try running: sudo %s"
|
||||||
|
% (sys.argv[0])))
|
||||||
|
|
||||||
|
# List of containers, stored as dictionaries
|
||||||
|
containers = []
|
||||||
|
for container_name in lxc.list_containers():
|
||||||
|
entry = {}
|
||||||
|
entry['name'] = container_name
|
||||||
|
|
||||||
|
# Apply filter
|
||||||
|
if args.filter and not re.match(args.filter, container_name):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Return before grabbing the object (non-root)
|
||||||
|
if not args.state and not args.fancy:
|
||||||
|
containers.append(entry)
|
||||||
|
continue
|
||||||
|
|
||||||
|
container = lxc.Container(container_name)
|
||||||
|
|
||||||
|
# Filter by status
|
||||||
|
if args.state and container.state not in args.state:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Nothing more is needed if we're not printing some fancy output
|
||||||
|
if not args.fancy:
|
||||||
|
containers.append(entry)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Some extra field we may want
|
||||||
|
if 'state' in args.fancy_format:
|
||||||
|
entry['state'] = container.state
|
||||||
|
if 'pid' in args.fancy_format:
|
||||||
|
entry['pid'] = "-"
|
||||||
|
if container.init_pid != -1:
|
||||||
|
entry['pid'] = str(container.init_pid)
|
||||||
|
|
||||||
|
# Get the IPs
|
||||||
|
for protocol in ('ipv4', 'ipv6'):
|
||||||
|
if protocol in args.fancy_format:
|
||||||
|
entry[protocol] = "-"
|
||||||
|
ips = container.get_ips(protocol=protocol, timeout=1)
|
||||||
|
if ips:
|
||||||
|
entry[protocol] = ", ".join(ips)
|
||||||
|
|
||||||
|
containers.append(entry)
|
||||||
|
|
||||||
|
|
||||||
|
# Print the list
|
||||||
|
## Standard list with one entry per line
|
||||||
|
if not args.fancy and args.one:
|
||||||
|
for container in sorted(containers,
|
||||||
|
key=lambda container: container['name']):
|
||||||
|
print(container['name'])
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
## Standard list with multiple entries per line
|
||||||
|
if not args.fancy and not args.one:
|
||||||
|
# Get the longest name and extra simple list
|
||||||
|
field_maxlength = 0
|
||||||
|
container_names = []
|
||||||
|
for container in containers:
|
||||||
|
if len(container['name']) > field_maxlength:
|
||||||
|
field_maxlength = len(container['name'])
|
||||||
|
container_names.append(container['name'])
|
||||||
|
|
||||||
|
# Figure out how many we can put per line
|
||||||
|
width = getTerminalSize()[0]
|
||||||
|
|
||||||
|
entries = int(width / (field_maxlength + 2))
|
||||||
|
if entries == 0:
|
||||||
|
entries = 1
|
||||||
|
|
||||||
|
for line in batch(sorted(container_names), entries):
|
||||||
|
line_format = ""
|
||||||
|
for index in range(len(line)):
|
||||||
|
line_format += "{line[%s]:%s}" % (index, field_maxlength + 2)
|
||||||
|
|
||||||
|
print(line_format.format(line=line))
|
||||||
|
|
||||||
|
## Fancy listing
|
||||||
|
if args.fancy:
|
||||||
|
field_maxlength = {}
|
||||||
|
|
||||||
|
# Get the maximum length per field
|
||||||
|
for field in args.fancy_format:
|
||||||
|
field_maxlength[field] = len(field)
|
||||||
|
|
||||||
|
for container in containers:
|
||||||
|
for field in args.fancy_format:
|
||||||
|
if len(container[field]) > field_maxlength[field]:
|
||||||
|
field_maxlength[field] = len(container[field])
|
||||||
|
|
||||||
|
# Generate the line format string based on the maximum length and
|
||||||
|
# a 2 character padding
|
||||||
|
line_format = ""
|
||||||
|
index = 0
|
||||||
|
for field in args.fancy_format:
|
||||||
|
line_format += "{fields[%s]:%s}" % (index, field_maxlength[field] + 2)
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
# Get the line length minus the padding of the last field
|
||||||
|
line_length = -2
|
||||||
|
for field in field_maxlength:
|
||||||
|
line_length += field_maxlength[field] + 2
|
||||||
|
|
||||||
|
# Print header
|
||||||
|
print(line_format.format(fields=[header.upper()
|
||||||
|
for header in args.fancy_format]))
|
||||||
|
print("-" * line_length)
|
||||||
|
|
||||||
|
# Print the entries
|
||||||
|
for container in sorted(containers,
|
||||||
|
key=lambda container: container['name']):
|
||||||
|
fields = [container[field] for field in args.fancy_format]
|
||||||
|
print(line_format.format(fields=fields))
|
@ -140,4 +140,3 @@ while read line; do
|
|||||||
printf "%-${container_field_width}s %s\n" "${container_of_pid[pid]}" "$line"
|
printf "%-${container_field_width}s %s\n" "${container_of_pid[pid]}" "$line"
|
||||||
fi
|
fi
|
||||||
done < <(ps "$@")
|
done < <(ps "$@")
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# lxc: linux Container library
|
# lxc: linux Container library
|
||||||
@ -81,31 +81,39 @@ lxc_dropcaps()
|
|||||||
chmod 0755 @LXCPATH@
|
chmod 0755 @LXCPATH@
|
||||||
}
|
}
|
||||||
|
|
||||||
shortoptions='hd'
|
usage_err() {
|
||||||
longoptions='help'
|
[ -n "$1" ] && echo "$1" >&2
|
||||||
|
|
||||||
getopt=$(getopt -o $shortoptions --longoptions $longoptions -- "$@")
|
|
||||||
if [ $? != 0 ]; then
|
|
||||||
usage
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
}
|
||||||
|
|
||||||
eval set -- "$getopt"
|
optarg_check() {
|
||||||
|
if [ -z "$2" ]; then
|
||||||
|
usage_err "option '$1' requires an argument"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
while true; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
opt="$1"
|
||||||
|
shift
|
||||||
|
case "$opt" in
|
||||||
-d)
|
-d)
|
||||||
LXC_DROP_CAPS="yes"
|
LXC_DROP_CAPS="yes"
|
||||||
shift
|
|
||||||
;;
|
;;
|
||||||
-h|--help)
|
-h|--help)
|
||||||
help
|
help
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
--)
|
--)
|
||||||
shift
|
|
||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
|
-?)
|
||||||
|
usage_err "unknown option '$opt'"
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
# split opts -abc into -a -b -c
|
||||||
|
set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
usage
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# lxc: linux Container library
|
# lxc: linux Container library
|
||||||
@ -78,31 +78,39 @@ lxc_dropuid()
|
|||||||
chmod 0755 @LXCPATH@
|
chmod 0755 @LXCPATH@
|
||||||
}
|
}
|
||||||
|
|
||||||
shortoptions='hd'
|
usage_err() {
|
||||||
longoptions='help'
|
[ -n "$1" ] && echo "$1" >&2
|
||||||
|
|
||||||
getopt=$(getopt -o $shortoptions --longoptions $longoptions -- "$@")
|
|
||||||
if [ $? != 0 ]; then
|
|
||||||
usage
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
}
|
||||||
|
|
||||||
eval set -- "$getopt"
|
optarg_check() {
|
||||||
|
if [ -z "$2" ]; then
|
||||||
|
usage_err "option '$1' requires an argument"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
while true; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
opt="$1"
|
||||||
|
shift
|
||||||
|
case "$opt" in
|
||||||
-d)
|
-d)
|
||||||
LXC_DROP_CAPS="yes"
|
LXC_DROP_CAPS="yes"
|
||||||
shift
|
|
||||||
;;
|
;;
|
||||||
-h|--help)
|
-h|--help)
|
||||||
help
|
help
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
--)
|
--)
|
||||||
shift
|
|
||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
|
-?)
|
||||||
|
usage_err "unknown option '$opt'"
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
# split opts -abc into -a -b -c
|
||||||
|
set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
usage
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
|
289
src/lxc/lxc-start-ephemeral.in
Normal file
289
src/lxc/lxc-start-ephemeral.in
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
#
|
||||||
|
# lxc-start-ephemeral: Start a copy of a container using an overlay
|
||||||
|
#
|
||||||
|
# This python implementation is based on the work done in the original
|
||||||
|
# shell implementation done by Serge Hallyn in Ubuntu (and other contributors)
|
||||||
|
#
|
||||||
|
# (C) Copyright Canonical Ltd. 2012
|
||||||
|
#
|
||||||
|
# Authors:
|
||||||
|
# Stéphane Graber <stgraber@ubuntu.com>
|
||||||
|
#
|
||||||
|
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
|
||||||
|
# NOTE: To remove once the API is stabilized
|
||||||
|
import warnings
|
||||||
|
warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import gettext
|
||||||
|
import lxc
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
_ = gettext.gettext
|
||||||
|
gettext.textdomain("lxc-start-ephemeral")
|
||||||
|
|
||||||
|
|
||||||
|
# Other functions
|
||||||
|
def randomMAC():
|
||||||
|
import random
|
||||||
|
|
||||||
|
mac = [0x00, 0x16, 0x3e,
|
||||||
|
random.randint(0x00, 0x7f),
|
||||||
|
random.randint(0x00, 0xff),
|
||||||
|
random.randint(0x00, 0xff)]
|
||||||
|
return ':'.join(map(lambda x: "%02x" % x, mac))
|
||||||
|
|
||||||
|
# Begin parsing the command line
|
||||||
|
parser = argparse.ArgumentParser(description=_(
|
||||||
|
"LXC: Start an ephemeral container"),
|
||||||
|
formatter_class=argparse.RawTextHelpFormatter,
|
||||||
|
epilog=_("If a COMMAND is given, then the "
|
||||||
|
"""container will run only as long
|
||||||
|
as the command runs.
|
||||||
|
If no COMMAND is given, this command will attach to tty1 and stop the
|
||||||
|
container when exiting (with ctrl-a-q).
|
||||||
|
|
||||||
|
If no COMMAND is given and -d is used, the name and IP addresses of the
|
||||||
|
container will be printed to the console."""))
|
||||||
|
|
||||||
|
parser.add_argument("--orig", "-o", type=str, required=True,
|
||||||
|
help=_("name of the original container"))
|
||||||
|
|
||||||
|
parser.add_argument("--bdir", "-b", type=str,
|
||||||
|
help=_("directory to bind mount into container"))
|
||||||
|
|
||||||
|
parser.add_argument("--user", "-u", type=str,
|
||||||
|
help=_("the user to connect to the container as"))
|
||||||
|
|
||||||
|
parser.add_argument("--key", "-S", type=str,
|
||||||
|
help=_("the path to the SSH key to use to connect"))
|
||||||
|
|
||||||
|
parser.add_argument("--daemon", "-d", action="store_true",
|
||||||
|
help=_("run in the background"))
|
||||||
|
|
||||||
|
parser.add_argument("--union-type", "-U", type=str, default="overlayfs",
|
||||||
|
choices=("overlayfs", "aufs"),
|
||||||
|
help=_("type of union (overlayfs or aufs), "
|
||||||
|
"defaults to overlayfs."))
|
||||||
|
|
||||||
|
parser.add_argument("--keep-data", "-k", action="store_true",
|
||||||
|
help=_("Use a persistent backend instead of tmpfs."))
|
||||||
|
|
||||||
|
parser.add_argument("command", metavar='CMD', type=str, nargs="*",
|
||||||
|
help=_("Run specific command in container "
|
||||||
|
"(command as argument)"))
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Basic requirements check
|
||||||
|
## Check that -d and CMD aren't used at the same time
|
||||||
|
if args.command and args.daemon:
|
||||||
|
parser.error(_("You can't use -d and a command at the same time."))
|
||||||
|
|
||||||
|
## The user needs to be uid 0
|
||||||
|
if not os.geteuid() == 0:
|
||||||
|
parser.error(_("You must be root to run this script. Try running: sudo %s"
|
||||||
|
% (sys.argv[0])))
|
||||||
|
|
||||||
|
# Load the orig container
|
||||||
|
orig = lxc.Container(args.orig)
|
||||||
|
if not orig.defined:
|
||||||
|
parser.error(_("Source container '%s' doesn't exist." % args.orig))
|
||||||
|
|
||||||
|
# Create the new container paths
|
||||||
|
dest_path = tempfile.mkdtemp(prefix="%s-" % args.orig, dir="@LXCPATH@")
|
||||||
|
os.mkdir(os.path.join(dest_path, "rootfs"))
|
||||||
|
|
||||||
|
# Setup the new container's configuration
|
||||||
|
dest = lxc.Container(os.path.basename(dest_path))
|
||||||
|
dest.load_config(orig.config_file_name)
|
||||||
|
dest.set_config_item("lxc.utsname", dest.name)
|
||||||
|
dest.set_config_item("lxc.rootfs", os.path.join(dest_path, "rootfs"))
|
||||||
|
dest.set_config_item("lxc.network.hwaddr", randomMAC())
|
||||||
|
|
||||||
|
overlay_dirs = [(orig.get_config_item("lxc.rootfs"), "%s/rootfs/" % dest_path)]
|
||||||
|
|
||||||
|
# Generate a new fstab
|
||||||
|
if orig.get_config_item("lxc.mount"):
|
||||||
|
dest.set_config_item("lxc.mount", os.path.join(dest_path, "fstab"))
|
||||||
|
with open(orig.get_config_item("lxc.mount"), "r") as orig_fd:
|
||||||
|
with open(dest.get_config_item("lxc.mount"), "w+") as dest_fd:
|
||||||
|
for line in orig_fd.read().split("\n"):
|
||||||
|
# Start by replacing any reference to the container rootfs
|
||||||
|
line.replace(orig.get_config_item("lxc.rootfs"),
|
||||||
|
dest.get_config_item("lxc.rootfs"))
|
||||||
|
|
||||||
|
# Skip any line that's not a bind mount
|
||||||
|
fields = line.split()
|
||||||
|
if len(fields) < 4:
|
||||||
|
dest_fd.write("%s\n" % line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if fields[2] != "bind" and "bind" not in fields[3]:
|
||||||
|
dest_fd.write("%s\n" % line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Process any remaining line
|
||||||
|
dest_mount = os.path.abspath(os.path.join("%s/rootfs/" % (
|
||||||
|
dest_path), fields[1]))
|
||||||
|
|
||||||
|
if dest_mount == os.path.abspath("%s/rootfs/%s" % (
|
||||||
|
dest_path, args.bdir)):
|
||||||
|
|
||||||
|
dest_fd.write("%s\n" % line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if "%s/rootfs/" % dest_path not in dest_mount:
|
||||||
|
print(_("Skipping mount entry '%s' as it's outside "
|
||||||
|
"of the container rootfs.") % line)
|
||||||
|
|
||||||
|
overlay_dirs += [(fields[0], dest_mount)]
|
||||||
|
|
||||||
|
# Generate pre-mount script
|
||||||
|
with open(os.path.join(dest_path, "pre-mount"), "w+") as fd:
|
||||||
|
os.fchmod(fd.fileno(), 0o755)
|
||||||
|
fd.write("""#!/bin/sh
|
||||||
|
LXC_DIR="%s"
|
||||||
|
LXC_BASE="%s"
|
||||||
|
LXC_NAME="%s"
|
||||||
|
""" % (dest_path, orig.name, dest.name))
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for entry in overlay_dirs:
|
||||||
|
target = "%s/delta%s" % (dest_path, count)
|
||||||
|
fd.write("mkdir -p %s %s\n" % (target, entry[1]))
|
||||||
|
|
||||||
|
if not args.keep_data:
|
||||||
|
fd.write("mount -n -t tmpfs none %s\n" % (target))
|
||||||
|
|
||||||
|
if args.union_type == "overlayfs":
|
||||||
|
fd.write("mount -n -t overlayfs"
|
||||||
|
" -oupperdir=%s,lowerdir=%s none %s\n" % (
|
||||||
|
target,
|
||||||
|
entry[0],
|
||||||
|
entry[1]))
|
||||||
|
elif args.union_type == "aufs":
|
||||||
|
fd.write("mount -n -t aufs "
|
||||||
|
"-o br=${upper}=rw:${lower}=ro,noplink none %s\n" % (
|
||||||
|
target,
|
||||||
|
entry[0],
|
||||||
|
entry[1]))
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
if args.bdir:
|
||||||
|
if not os.path.exists(args.bdir):
|
||||||
|
print(_("Path '%s' doesn't exist, won't be bind-mounted.") %
|
||||||
|
args.bdir)
|
||||||
|
else:
|
||||||
|
src_path = os.path.abspath(args.bdir)
|
||||||
|
dst_path = "%s/rootfs/%s" % (dest_path, os.path.abspath(args.bdir))
|
||||||
|
fd.write("mkdir -p %s\nmount -n --bind %s %s\n" % (
|
||||||
|
dst_path, src_path, dst_path))
|
||||||
|
|
||||||
|
fd.write("""
|
||||||
|
[ -e $LXC_DIR/configured ] && exit 0
|
||||||
|
for file in $LXC_DIR/rootfs/etc/hostname \\
|
||||||
|
$LXC_DIR/rootfs/etc/hosts \\
|
||||||
|
$LXC_DIR/rootfs/etc/sysconfig/network \\
|
||||||
|
$LXC_DIR/rootfs/etc/sysconfig/network-scripts/ifcfg-eth0; do
|
||||||
|
[ -f "$file" ] && sed -i -e "s/$LXC_BASE/$LXC_NAME/" $file
|
||||||
|
done
|
||||||
|
touch $LXC_DIR/configured
|
||||||
|
""")
|
||||||
|
|
||||||
|
dest.set_config_item("lxc.hook.pre-mount",
|
||||||
|
os.path.join(dest_path, "pre-mount"))
|
||||||
|
|
||||||
|
# Generate post-stop script
|
||||||
|
if not args.keep_data:
|
||||||
|
with open(os.path.join(dest_path, "post-stop"), "w+") as fd:
|
||||||
|
os.fchmod(fd.fileno(), 0o755)
|
||||||
|
fd.write("""#!/bin/sh
|
||||||
|
[ -d "%s" ] && rm -Rf "%s"
|
||||||
|
""" % (dest_path, dest_path))
|
||||||
|
|
||||||
|
dest.set_config_item("lxc.hook.post-stop",
|
||||||
|
os.path.join(dest_path, "post-stop"))
|
||||||
|
|
||||||
|
dest.save_config()
|
||||||
|
|
||||||
|
# Start the container
|
||||||
|
if not dest.start() or not dest.wait("RUNNING", timeout=5):
|
||||||
|
print(_("The container '%s' failed to start.") % dest.name)
|
||||||
|
dest.stop()
|
||||||
|
if dest.defined:
|
||||||
|
dest.destroy()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Deal with the case where we just attach to the container's console
|
||||||
|
if not args.command and not args.daemon:
|
||||||
|
dest.console(tty=1)
|
||||||
|
dest.shutdown(timeout=5)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Try to get the IP addresses
|
||||||
|
ips = dest.get_ips(timeout=10)
|
||||||
|
|
||||||
|
# Deal with the case where we just print info about the container
|
||||||
|
if args.daemon:
|
||||||
|
print(_("""The ephemeral container is now started.
|
||||||
|
|
||||||
|
You can enter it from the command line with: lxc-console -n %s
|
||||||
|
The following IP addresses have be found in the container:
|
||||||
|
%s""") % (dest.name,
|
||||||
|
"\n".join([" - %s" % entry for entry in ips]
|
||||||
|
or [" - %s" % _("No address could be found")])))
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Now deal with the case where we want to run a command in the container
|
||||||
|
if not ips:
|
||||||
|
print(_("Failed to get an IP for container '%s'.") % dest.name)
|
||||||
|
dest.stop()
|
||||||
|
if dest.defined:
|
||||||
|
dest.destroy()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# NOTE: To replace by .attach() once the kernel supports it
|
||||||
|
cmd = ["ssh",
|
||||||
|
"-o", "StrictHostKeyChecking=no",
|
||||||
|
"-o", "UserKnownHostsFile=/dev/null"]
|
||||||
|
|
||||||
|
if args.user:
|
||||||
|
cmd += ["-l", args.user]
|
||||||
|
|
||||||
|
if args.key:
|
||||||
|
cmd += ["-k", args.key]
|
||||||
|
|
||||||
|
for ip in ips:
|
||||||
|
ssh_cmd = cmd + [ip] + args.command
|
||||||
|
retval = subprocess.call(ssh_cmd, universal_newlines=True)
|
||||||
|
if retval == 255:
|
||||||
|
print(_("SSH failed to connect, trying next IP address."))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if retval != 0:
|
||||||
|
print(_("Command returned with non-zero return code: %s") % retval)
|
||||||
|
break
|
||||||
|
|
||||||
|
# Shutdown the container
|
||||||
|
dest.shutdown(timeout=5)
|
||||||
|
|
||||||
|
sys.exit(retval)
|
@ -1,3 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
echo "lxc version: @PACKAGE_VERSION@"
|
echo "lxc version: @PACKAGE_VERSION@"
|
||||||
|
@ -84,6 +84,7 @@ extern int lxc_monitor_open(void);
|
|||||||
* data was readen, < 0 otherwise
|
* data was readen, < 0 otherwise
|
||||||
*/
|
*/
|
||||||
extern int lxc_monitor_read(int fd, struct lxc_msg *msg);
|
extern int lxc_monitor_read(int fd, struct lxc_msg *msg);
|
||||||
|
extern int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Close the fd associated with the monitoring
|
* Close the fd associated with the monitoring
|
||||||
@ -178,6 +179,30 @@ extern int lxc_restart(const char *, int, struct lxc_conf *, int);
|
|||||||
*/
|
*/
|
||||||
extern const char const *lxc_version(void);
|
extern const char const *lxc_version(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create and return a new lxccontainer struct.
|
||||||
|
*/
|
||||||
|
extern struct lxc_container *lxc_container_new(const char *name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns 1 on success, 0 on failure.
|
||||||
|
*/
|
||||||
|
extern int lxc_container_get(struct lxc_container *c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Put a lxccontainer struct reference.
|
||||||
|
* Return -1 on error.
|
||||||
|
* Return 0 if this was not the last reference.
|
||||||
|
* If it is the last reference, free the lxccontainer and return 1.
|
||||||
|
*/
|
||||||
|
extern int lxc_container_put(struct lxc_container *c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get a list of valid wait states.
|
||||||
|
* If states is NULL, simply return the number of states
|
||||||
|
*/
|
||||||
|
extern int lxc_get_wait_states(const char **states);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -40,22 +40,30 @@
|
|||||||
#include "start.h"
|
#include "start.h"
|
||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "namespace.h"
|
||||||
|
|
||||||
lxc_log_define(lxc_attach_ui, lxc);
|
lxc_log_define(lxc_attach_ui, lxc);
|
||||||
|
|
||||||
static const struct option my_longopts[] = {
|
static const struct option my_longopts[] = {
|
||||||
{"elevated-privileges", no_argument, 0, 'e'},
|
{"elevated-privileges", no_argument, 0, 'e'},
|
||||||
{"arch", required_argument, 0, 'a'},
|
{"arch", required_argument, 0, 'a'},
|
||||||
|
{"namespaces", required_argument, 0, 's'},
|
||||||
|
{"remount-sys-proc", no_argument, 0, 'R'},
|
||||||
LXC_COMMON_OPTIONS
|
LXC_COMMON_OPTIONS
|
||||||
};
|
};
|
||||||
|
|
||||||
static int elevated_privileges = 0;
|
static int elevated_privileges = 0;
|
||||||
static signed long new_personality = -1;
|
static signed long new_personality = -1;
|
||||||
|
static int namespace_flags = -1;
|
||||||
|
static int remount_sys_proc = 0;
|
||||||
|
|
||||||
static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'e': elevated_privileges = 1; break;
|
case 'e': elevated_privileges = 1; break;
|
||||||
|
case 'R': remount_sys_proc = 1; break;
|
||||||
case 'a':
|
case 'a':
|
||||||
new_personality = lxc_config_parse_arch(arg);
|
new_personality = lxc_config_parse_arch(arg);
|
||||||
if (new_personality < 0) {
|
if (new_personality < 0) {
|
||||||
@ -63,6 +71,14 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
namespace_flags = 0;
|
||||||
|
ret = lxc_fill_namespace_flags(arg, &namespace_flags);
|
||||||
|
if (ret)
|
||||||
|
return -1;
|
||||||
|
/* -s implies -e */
|
||||||
|
elevated_privileges = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -83,7 +99,18 @@ Options :\n\
|
|||||||
WARNING: This may leak privleges into the container.\n\
|
WARNING: This may leak privleges into the container.\n\
|
||||||
Use with care.\n\
|
Use with care.\n\
|
||||||
-a, --arch=ARCH Use ARCH for program instead of container's own\n\
|
-a, --arch=ARCH Use ARCH for program instead of container's own\n\
|
||||||
architecture.\n",
|
architecture.\n\
|
||||||
|
-s, --namespaces=FLAGS\n\
|
||||||
|
Don't attach to all the namespaces of the container\n\
|
||||||
|
but just to the following OR'd list of flags:\n\
|
||||||
|
MOUNT, PID, UTSNAME, IPC, USER or NETWORK\n\
|
||||||
|
WARNING: Using -s implies -e, it may therefore\n\
|
||||||
|
leak privileges into the container. Use with care.\n\
|
||||||
|
-R, --remount-sys-proc\n\
|
||||||
|
Remount /sys and /proc if not attaching to the\n\
|
||||||
|
mount namespace when using -s in order to properly\n\
|
||||||
|
reflect the correct namespace context. See the\n\
|
||||||
|
lxc-attach(1) manual page for details.\n",
|
||||||
.options = my_longopts,
|
.options = my_longopts,
|
||||||
.parser = my_parser,
|
.parser = my_parser,
|
||||||
.checker = NULL,
|
.checker = NULL,
|
||||||
@ -96,6 +123,7 @@ int main(int argc, char *argv[])
|
|||||||
struct passwd *passwd;
|
struct passwd *passwd;
|
||||||
struct lxc_proc_context_info *init_ctx;
|
struct lxc_proc_context_info *init_ctx;
|
||||||
struct lxc_handler *handler;
|
struct lxc_handler *handler;
|
||||||
|
void *cgroup_data = NULL;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
char *curdir;
|
char *curdir;
|
||||||
|
|
||||||
@ -124,6 +152,48 @@ int main(int argc, char *argv[])
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!elevated_privileges) {
|
||||||
|
/* we have to do this now since /sys/fs/cgroup may not
|
||||||
|
* be available inside the container or we may not have
|
||||||
|
* the required permissions anymore
|
||||||
|
*/
|
||||||
|
ret = lxc_cgroup_prepare_attach(my_args.name, &cgroup_data);
|
||||||
|
if (ret < 0) {
|
||||||
|
ERROR("failed to prepare attaching to cgroup");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curdir = get_current_dir_name();
|
||||||
|
|
||||||
|
/* determine which namespaces the container was created with
|
||||||
|
* by asking lxc-start
|
||||||
|
*/
|
||||||
|
if (namespace_flags == -1) {
|
||||||
|
namespace_flags = lxc_get_clone_flags(my_args.name);
|
||||||
|
/* call failed */
|
||||||
|
if (namespace_flags == -1) {
|
||||||
|
ERROR("failed to automatically determine the "
|
||||||
|
"namespaces which the container unshared");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we need to attach before we fork since certain namespaces
|
||||||
|
* (such as pid namespaces) only really affect children of the
|
||||||
|
* current process and not the process itself
|
||||||
|
*/
|
||||||
|
ret = lxc_attach_to_ns(init_pid, namespace_flags);
|
||||||
|
if (ret < 0) {
|
||||||
|
ERROR("failed to enter the namespace");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curdir && chdir(curdir))
|
||||||
|
WARN("could not change directory to '%s'", curdir);
|
||||||
|
|
||||||
|
free(curdir);
|
||||||
|
|
||||||
/* hack: we need sync.h infrastructure - and that needs a handler */
|
/* hack: we need sync.h infrastructure - and that needs a handler */
|
||||||
handler = calloc(1, sizeof(*handler));
|
handler = calloc(1, sizeof(*handler));
|
||||||
|
|
||||||
@ -150,8 +220,22 @@ int main(int argc, char *argv[])
|
|||||||
if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE))
|
if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!elevated_privileges && lxc_cgroup_attach(my_args.name, pid))
|
/* now that we are done with all privileged operations,
|
||||||
|
* we can add ourselves to the cgroup. Since we smuggled in
|
||||||
|
* the fds earlier, we still have write permission
|
||||||
|
*/
|
||||||
|
if (!elevated_privileges) {
|
||||||
|
/* since setns() for pid namespaces only really
|
||||||
|
* affects child processes, the pid we have is
|
||||||
|
* still valid outside the container, so this is
|
||||||
|
* fine
|
||||||
|
*/
|
||||||
|
ret = lxc_cgroup_finish_attach(cgroup_data, pid);
|
||||||
|
if (ret < 0) {
|
||||||
|
ERROR("failed to attach process to cgroup");
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* tell the child we are done initializing */
|
/* tell the child we are done initializing */
|
||||||
if (lxc_sync_wake_child(handler, LXC_SYNC_POST_CONFIGURE))
|
if (lxc_sync_wake_child(handler, LXC_SYNC_POST_CONFIGURE))
|
||||||
@ -175,19 +259,19 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (!pid) {
|
if (!pid) {
|
||||||
lxc_sync_fini_parent(handler);
|
lxc_sync_fini_parent(handler);
|
||||||
|
lxc_cgroup_dispose_attach(cgroup_data);
|
||||||
|
|
||||||
curdir = get_current_dir_name();
|
/* A description of the purpose of this functionality is
|
||||||
|
* provided in the lxc-attach(1) manual page. We have to
|
||||||
ret = lxc_attach_to_ns(init_pid);
|
* remount here and not in the parent process, otherwise
|
||||||
|
* /proc may not properly reflect the new pid namespace.
|
||||||
|
*/
|
||||||
|
if (!(namespace_flags & CLONE_NEWNS) && remount_sys_proc) {
|
||||||
|
ret = lxc_attach_remount_sys_proc();
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
ERROR("failed to enter the namespace");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (curdir && chdir(curdir))
|
|
||||||
WARN("could not change directory to '%s'", curdir);
|
|
||||||
|
|
||||||
free(curdir);
|
|
||||||
|
|
||||||
if (new_personality < 0)
|
if (new_personality < 0)
|
||||||
new_personality = init_ctx->personality;
|
new_personality = init_ctx->personality;
|
||||||
|
@ -34,12 +34,14 @@
|
|||||||
|
|
||||||
static bool state;
|
static bool state;
|
||||||
static bool pid;
|
static bool pid;
|
||||||
|
static char *test_state = NULL;
|
||||||
|
|
||||||
static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 's': state = true; break;
|
case 's': state = true; break;
|
||||||
case 'p': pid = true; break;
|
case 'p': pid = true; break;
|
||||||
|
case 't': test_state = arg; break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -47,6 +49,7 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
|||||||
static const struct option my_longopts[] = {
|
static const struct option my_longopts[] = {
|
||||||
{"state", no_argument, 0, 's'},
|
{"state", no_argument, 0, 's'},
|
||||||
{"pid", no_argument, 0, 'p'},
|
{"pid", no_argument, 0, 'p'},
|
||||||
|
{"state-is", required_argument, 0, 't'},
|
||||||
LXC_COMMON_OPTIONS,
|
LXC_COMMON_OPTIONS,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -60,7 +63,9 @@ lxc-info display some information about a container with the identifier NAME\n\
|
|||||||
Options :\n\
|
Options :\n\
|
||||||
-n, --name=NAME NAME for name of the container\n\
|
-n, --name=NAME NAME for name of the container\n\
|
||||||
-s, --state shows the state of the container\n\
|
-s, --state shows the state of the container\n\
|
||||||
-p, --pid shows the process id of the init container\n",
|
-p, --pid shows the process id of the init container\n\
|
||||||
|
-t, --state-is=STATE test if current state is STATE\n\
|
||||||
|
returns success if it matches, false otherwise\n",
|
||||||
.options = my_longopts,
|
.options = my_longopts,
|
||||||
.parser = my_parser,
|
.parser = my_parser,
|
||||||
.checker = NULL,
|
.checker = NULL,
|
||||||
@ -81,10 +86,12 @@ int main(int argc, char *argv[])
|
|||||||
if (!state && !pid)
|
if (!state && !pid)
|
||||||
state = pid = true;
|
state = pid = true;
|
||||||
|
|
||||||
if (state) {
|
if (state || test_state) {
|
||||||
ret = lxc_getstate(my_args.name);
|
ret = lxc_getstate(my_args.name);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
if (test_state)
|
||||||
|
return strcmp(lxc_state2str(ret), test_state) != 0;
|
||||||
|
|
||||||
printf("state:%10s\n", lxc_state2str(ret));
|
printf("state:%10s\n", lxc_state2str(ret));
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,7 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
|||||||
case 'f': args->rcfile = arg; break;
|
case 'f': args->rcfile = arg; break;
|
||||||
case 'C': args->close_all_fds = 1; break;
|
case 'C': args->close_all_fds = 1; break;
|
||||||
case 's': return lxc_config_define_add(&defines, arg);
|
case 's': return lxc_config_define_add(&defines, arg);
|
||||||
|
case 'p': args->pidfile = arg; break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -72,6 +73,7 @@ static const struct option my_longopts[] = {
|
|||||||
{"define", required_argument, 0, 's'},
|
{"define", required_argument, 0, 's'},
|
||||||
{"console", required_argument, 0, 'c'},
|
{"console", required_argument, 0, 'c'},
|
||||||
{"close-all-fds", no_argument, 0, 'C'},
|
{"close-all-fds", no_argument, 0, 'C'},
|
||||||
|
{"pidfile", required_argument, 0, 'p'},
|
||||||
LXC_COMMON_OPTIONS
|
LXC_COMMON_OPTIONS
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -85,6 +87,7 @@ lxc-start start COMMAND in specified container NAME\n\
|
|||||||
Options :\n\
|
Options :\n\
|
||||||
-n, --name=NAME NAME for name of the container\n\
|
-n, --name=NAME NAME for name of the container\n\
|
||||||
-d, --daemon daemonize the container\n\
|
-d, --daemon daemonize the container\n\
|
||||||
|
-p, --pidfile=FILE Create a file with the process id\n\
|
||||||
-f, --rcfile=FILE Load configuration file FILE\n\
|
-f, --rcfile=FILE Load configuration file FILE\n\
|
||||||
-c, --console=FILE Set the file output for the container console\n\
|
-c, --console=FILE Set the file output for the container console\n\
|
||||||
-C, --close-all-fds If any fds are inherited, close them\n\
|
-C, --close-all-fds If any fds are inherited, close them\n\
|
||||||
@ -95,6 +98,7 @@ Options :\n\
|
|||||||
.parser = my_parser,
|
.parser = my_parser,
|
||||||
.checker = NULL,
|
.checker = NULL,
|
||||||
.daemonize = 0,
|
.daemonize = 0,
|
||||||
|
.pidfile = NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
@ -107,6 +111,7 @@ int main(int argc, char *argv[])
|
|||||||
"/sbin/init",
|
"/sbin/init",
|
||||||
'\0',
|
'\0',
|
||||||
};
|
};
|
||||||
|
FILE *pid_fp = NULL;
|
||||||
|
|
||||||
lxc_list_init(&defines);
|
lxc_list_init(&defines);
|
||||||
|
|
||||||
@ -199,6 +204,15 @@ int main(int argc, char *argv[])
|
|||||||
free(console);
|
free(console);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (my_args.pidfile != NULL) {
|
||||||
|
pid_fp = fopen(my_args.pidfile, "w");
|
||||||
|
if (pid_fp == NULL) {
|
||||||
|
SYSERROR("failed to create pidfile '%s' for '%s'",
|
||||||
|
my_args.pidfile, my_args.name);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (my_args.daemonize) {
|
if (my_args.daemonize) {
|
||||||
/* do an early check for needed privs, since otherwise the
|
/* do an early check for needed privs, since otherwise the
|
||||||
* user won't see the error */
|
* user won't see the error */
|
||||||
@ -214,6 +228,14 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pid_fp != NULL) {
|
||||||
|
if (fprintf(pid_fp, "%d\n", getpid()) < 0) {
|
||||||
|
SYSERROR("failed to write '%s'", my_args.pidfile);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
fclose(pid_fp);
|
||||||
|
}
|
||||||
|
|
||||||
if (my_args.close_all_fds)
|
if (my_args.close_all_fds)
|
||||||
conf->close_all_fds = 1;
|
conf->close_all_fds = 1;
|
||||||
|
|
||||||
@ -230,6 +252,9 @@ int main(int argc, char *argv[])
|
|||||||
err = -1;
|
err = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (my_args.pidfile)
|
||||||
|
unlink(my_args.pidfile);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,51 +84,6 @@ static uid_t lookup_user(const char *optarg)
|
|||||||
return uid;
|
return uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *namespaces_list[] = {
|
|
||||||
"MOUNT", "PID", "UTSNAME", "IPC",
|
|
||||||
"USER", "NETWORK"
|
|
||||||
};
|
|
||||||
static int cloneflags_list[] = {
|
|
||||||
CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC,
|
|
||||||
CLONE_NEWUSER, CLONE_NEWNET
|
|
||||||
};
|
|
||||||
|
|
||||||
static int lxc_namespace_2_cloneflag(char *namespace)
|
|
||||||
{
|
|
||||||
int i, len;
|
|
||||||
len = sizeof(namespaces_list)/sizeof(namespaces_list[0]);
|
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
if (!strcmp(namespaces_list[i], namespace))
|
|
||||||
return cloneflags_list[i];
|
|
||||||
|
|
||||||
ERROR("invalid namespace name %s", namespace);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lxc_fill_namespace_flags(char *flaglist, int *flags)
|
|
||||||
{
|
|
||||||
char *token, *saveptr = NULL;
|
|
||||||
int aflag;
|
|
||||||
|
|
||||||
if (!flaglist) {
|
|
||||||
ERROR("need at least one namespace to unshare");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
token = strtok_r(flaglist, "|", &saveptr);
|
|
||||||
while (token) {
|
|
||||||
|
|
||||||
aflag = lxc_namespace_2_cloneflag(token);
|
|
||||||
if (aflag < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
*flags |= aflag;
|
|
||||||
|
|
||||||
token = strtok_r(NULL, "|", &saveptr);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct start_arg {
|
struct start_arg {
|
||||||
char ***args;
|
char ***args;
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <lxc/lxc.h>
|
#include <lxc/lxc.h>
|
||||||
@ -46,12 +48,14 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
|||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 's': args->states = optarg; break;
|
case 's': args->states = optarg; break;
|
||||||
|
case 't': args->timeout = atol(optarg); break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct option my_longopts[] = {
|
static const struct option my_longopts[] = {
|
||||||
{"state", required_argument, 0, 's'},
|
{"state", required_argument, 0, 's'},
|
||||||
|
{"timeout", required_argument, 0, 't'},
|
||||||
LXC_COMMON_OPTIONS
|
LXC_COMMON_OPTIONS
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -66,37 +70,16 @@ Options :\n\
|
|||||||
-n, --name=NAME NAME for name of the container\n\
|
-n, --name=NAME NAME for name of the container\n\
|
||||||
-s, --state=STATE ORed states to wait for\n\
|
-s, --state=STATE ORed states to wait for\n\
|
||||||
STOPPED, STARTING, RUNNING, STOPPING,\n\
|
STOPPED, STARTING, RUNNING, STOPPING,\n\
|
||||||
ABORTING, FREEZING, FROZEN\n",
|
ABORTING, FREEZING, FROZEN\n\
|
||||||
|
-t, --timeout=TMO Seconds to wait for state changes\n",
|
||||||
.options = my_longopts,
|
.options = my_longopts,
|
||||||
.parser = my_parser,
|
.parser = my_parser,
|
||||||
.checker = my_checker,
|
.checker = my_checker,
|
||||||
|
.timeout = -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int fillwaitedstates(char *strstates, int *states)
|
|
||||||
{
|
|
||||||
char *token, *saveptr = NULL;
|
|
||||||
int state;
|
|
||||||
|
|
||||||
token = strtok_r(strstates, "|", &saveptr);
|
|
||||||
while (token) {
|
|
||||||
|
|
||||||
state = lxc_str2state(token);
|
|
||||||
if (state < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
states[state] = 1;
|
|
||||||
|
|
||||||
token = strtok_r(NULL, "|", &saveptr);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct lxc_msg msg;
|
|
||||||
int s[MAX_STATE] = { }, fd;
|
|
||||||
int state, ret;
|
|
||||||
|
|
||||||
if (lxc_arguments_parse(&my_args, argc, argv))
|
if (lxc_arguments_parse(&my_args, argc, argv))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@ -104,53 +87,5 @@ int main(int argc, char *argv[])
|
|||||||
my_args.progname, my_args.quiet))
|
my_args.progname, my_args.quiet))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (fillwaitedstates(my_args.states, s))
|
return lxc_wait(strdup(my_args.name), my_args.states, my_args.timeout);
|
||||||
return -1;
|
|
||||||
|
|
||||||
fd = lxc_monitor_open();
|
|
||||||
if (fd < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* if container present,
|
|
||||||
* then check if already in requested state
|
|
||||||
*/
|
|
||||||
ret = -1;
|
|
||||||
state = lxc_getstate(my_args.name);
|
|
||||||
if (state < 0) {
|
|
||||||
goto out_close;
|
|
||||||
} else if ((state >= 0) && (s[state])) {
|
|
||||||
ret = 0;
|
|
||||||
goto out_close;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (lxc_monitor_read(fd, &msg) < 0)
|
|
||||||
goto out_close;
|
|
||||||
|
|
||||||
if (strcmp(my_args.name, msg.name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
switch (msg.type) {
|
|
||||||
case lxc_msg_state:
|
|
||||||
if (msg.value < 0 || msg.value >= MAX_STATE) {
|
|
||||||
ERROR("Receive an invalid state number '%d'",
|
|
||||||
msg.value);
|
|
||||||
goto out_close;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s[msg.value]) {
|
|
||||||
ret = 0;
|
|
||||||
goto out_close;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* just ignore garbage */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out_close:
|
|
||||||
lxc_monitor_close(fd);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
1001
src/lxc/lxccontainer.c
Normal file
1001
src/lxc/lxccontainer.c
Normal file
File diff suppressed because it is too large
Load Diff
79
src/lxc/lxccontainer.h
Normal file
79
src/lxc/lxccontainer.h
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#include "lxclock.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct lxc_container {
|
||||||
|
// private fields
|
||||||
|
char *name;
|
||||||
|
char *configfile;
|
||||||
|
sem_t *slock;
|
||||||
|
sem_t *privlock;
|
||||||
|
int numthreads; /* protected by privlock. */
|
||||||
|
struct lxc_conf *lxc_conf; // maybe we'll just want the whole lxc_handler?
|
||||||
|
|
||||||
|
// public fields
|
||||||
|
char *error_string;
|
||||||
|
int error_num;
|
||||||
|
int daemonize;
|
||||||
|
|
||||||
|
bool (*is_defined)(struct lxc_container *c); // did /var/lib/lxc/$name/config exist
|
||||||
|
const char *(*state)(struct lxc_container *c);
|
||||||
|
bool (*is_running)(struct lxc_container *c); // true so long as defined and not stopped
|
||||||
|
bool (*freeze)(struct lxc_container *c);
|
||||||
|
bool (*unfreeze)(struct lxc_container *c);
|
||||||
|
pid_t (*init_pid)(struct lxc_container *c);
|
||||||
|
bool (*load_config)(struct lxc_container *c, const char *alt_file);
|
||||||
|
/* The '...' is the command line. If provided, it must be ended with a NULL */
|
||||||
|
bool (*start)(struct lxc_container *c, int useinit, char * const argv[]);
|
||||||
|
bool (*startl)(struct lxc_container *c, int useinit, ...);
|
||||||
|
bool (*stop)(struct lxc_container *c);
|
||||||
|
void (*want_daemonize)(struct lxc_container *c);
|
||||||
|
// Return current config file name. The result is strdup()d, so free the result.
|
||||||
|
char *(*config_file_name)(struct lxc_container *c);
|
||||||
|
// for wait, timeout == -1 means wait forever, timeout == 0 means don't wait.
|
||||||
|
// otherwise timeout is seconds to wait.
|
||||||
|
bool (*wait)(struct lxc_container *c, const char *state, int timeout);
|
||||||
|
bool (*set_config_item)(struct lxc_container *c, const char *key, const char *value);
|
||||||
|
bool (*destroy)(struct lxc_container *c);
|
||||||
|
bool (*save_config)(struct lxc_container *c, const char *alt_file);
|
||||||
|
bool (*create)(struct lxc_container *c, char *t, char *const argv[]);
|
||||||
|
bool (*createl)(struct lxc_container *c, char *t, ...);
|
||||||
|
/* send SIGPWR. if timeout is not 0 or -1, do a hard stop after timeout seconds */
|
||||||
|
bool (*shutdown)(struct lxc_container *c, int timeout);
|
||||||
|
/* clear all network or capability items in the in-memory configuration */
|
||||||
|
bool (*clear_config_item)(struct lxc_container *c, const char *key);
|
||||||
|
/* print a config item to a in-memory string allocated by the caller. Return
|
||||||
|
* the length which was our would be printed. */
|
||||||
|
int (*get_config_item)(struct lxc_container *c, const char *key, char *retv, int inlen);
|
||||||
|
int (*get_keys)(struct lxc_container *c, const char *key, char *retv, int inlen);
|
||||||
|
/*
|
||||||
|
* get_cgroup_item returns the number of bytes read, or an error (<0).
|
||||||
|
* If retv NULL or inlen 0 is passed in, then the length of the cgroup
|
||||||
|
* file will be returned. * Otherwise it will return the # of bytes read.
|
||||||
|
* If inlen is less than the number of bytes available, then the returned
|
||||||
|
* value will be inlen, not the full available size of the file.
|
||||||
|
*/
|
||||||
|
int (*get_cgroup_item)(struct lxc_container *c, const char *subsys, char *retv, int inlen);
|
||||||
|
bool (*set_cgroup_item)(struct lxc_container *c, const char *subsys, const char *value);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
bool (*commit_cgroups)(struct lxc_container *c);
|
||||||
|
bool (*reread_cgroups)(struct lxc_container *c);
|
||||||
|
// question with clone: how do we handle non-standard config file in orig?
|
||||||
|
struct lxc_container (*clone)(struct container *c);
|
||||||
|
int (*ns_attach)(struct lxc_container *c, int ns_mask);
|
||||||
|
// we'll need some plumbing to support lxc-console
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lxc_container *lxc_container_new(const char *name);
|
||||||
|
int lxc_container_get(struct lxc_container *c);
|
||||||
|
int lxc_container_put(struct lxc_container *c);
|
||||||
|
int lxc_get_wait_states(const char **states);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
char ** lxc_get_valid_keys();
|
||||||
|
char ** lxc_get_valid_values(char *key);
|
||||||
|
#endif
|
105
src/lxc/lxclock.c
Normal file
105
src/lxc/lxclock.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/* liblxcapi
|
||||||
|
*
|
||||||
|
* Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
|
||||||
|
* Copyright © 2012 Canonical Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lxclock.h"
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#define OFLAG (O_CREAT | O_RDWR)
|
||||||
|
#define SEMMODE 0660
|
||||||
|
#define SEMVALUE 1
|
||||||
|
#define SEMVALUE_LOCKED 0
|
||||||
|
#define LXCLOCK_PREFIX "/lxcapi."
|
||||||
|
|
||||||
|
|
||||||
|
static char *lxclock_name(const char *container)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int len = strlen(container) + strlen(LXCLOCK_PREFIX) + 1;
|
||||||
|
char *dest = malloc(len);
|
||||||
|
if (!dest)
|
||||||
|
return NULL;
|
||||||
|
ret = snprintf(dest, len, "%s%s", LXCLOCK_PREFIX, container);
|
||||||
|
if (ret < 0 || ret >= len) {
|
||||||
|
free(dest);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lxcfree_name(char *name)
|
||||||
|
{
|
||||||
|
if (name)
|
||||||
|
free(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static sem_t *lxc_new_unnamed_sem(void)
|
||||||
|
{
|
||||||
|
sem_t *s;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
s = malloc(sizeof(*s));
|
||||||
|
if (!s)
|
||||||
|
return NULL;
|
||||||
|
ret = sem_init(s, 0, 1);
|
||||||
|
if (ret)
|
||||||
|
return NULL;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
sem_t *lxc_newlock(const char *name)
|
||||||
|
{
|
||||||
|
char *lname;
|
||||||
|
sem_t *lock;
|
||||||
|
|
||||||
|
if (!name)
|
||||||
|
return lxc_new_unnamed_sem();
|
||||||
|
|
||||||
|
lname = lxclock_name(name);
|
||||||
|
if (!lname)
|
||||||
|
return NULL;
|
||||||
|
lock = sem_open(lname, OFLAG, SEMMODE, SEMVALUE);
|
||||||
|
lxcfree_name(lname);
|
||||||
|
if (lock == SEM_FAILED)
|
||||||
|
return NULL;
|
||||||
|
return lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lxclock(sem_t *sem, int timeout)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!timeout) {
|
||||||
|
ret = sem_wait(sem);
|
||||||
|
} else {
|
||||||
|
struct timespec ts;
|
||||||
|
if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
|
||||||
|
return -2;
|
||||||
|
ts.tv_sec += timeout;
|
||||||
|
ret = sem_timedwait(sem, &ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lxcunlock(sem_t *sem)
|
||||||
|
{
|
||||||
|
if (!sem)
|
||||||
|
return -2;
|
||||||
|
return sem_post(sem);
|
||||||
|
}
|
61
src/lxc/lxclock.h
Normal file
61
src/lxc/lxclock.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/* liblxcapi
|
||||||
|
*
|
||||||
|
* Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
|
||||||
|
* Copyright © 2012 Canonical Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h> /* For O_* constants */
|
||||||
|
#include <sys/stat.h> /* For mode constants */
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* lxc_newlock:
|
||||||
|
* if name is not given, create an unnamed semaphore. We use these
|
||||||
|
* to protect against racing threads.
|
||||||
|
* Note that an unnamed sem was malloced by us and needs to be freed.
|
||||||
|
*
|
||||||
|
* If name is given, it is prepended with '/lxcapi.', and used as the
|
||||||
|
* name for a system-wide (well, ipcns-wide) semaphore. We use that
|
||||||
|
* to protect the containers as represented on disk.
|
||||||
|
* A named sem should not be freed.
|
||||||
|
*
|
||||||
|
* XXX TODO
|
||||||
|
* We should probably introduce a lxclock_close() which detecs the type
|
||||||
|
* of lock and calls sem_close() or sem_destroy()+free() not as appropriate.
|
||||||
|
* For now, it is up to the caller to do so.
|
||||||
|
*
|
||||||
|
* sem is initialized to value of 1
|
||||||
|
*
|
||||||
|
* return NULL on failure, else a sem_t * which can be passed to
|
||||||
|
* lxclock() and lxcunlock().
|
||||||
|
*/
|
||||||
|
extern sem_t *lxc_newlock(const char *name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* lxclock: take an existing lock. If timeout is 0, wait
|
||||||
|
* indefinately. Otherwise use given timeout.
|
||||||
|
* return 0 if we got the lock, -2 on failure to set timeout, or -1
|
||||||
|
* otherwise in which case errno will be set by sem_wait()).
|
||||||
|
*/
|
||||||
|
extern int lxclock(sem_t *sem, int timeout);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* lxcunlock: unlock given sem. Return 0 on success. Otherwise returns
|
||||||
|
* -1 and sem_post will leave errno set.
|
||||||
|
*/
|
||||||
|
extern int lxcunlock(sem_t *lock);
|
49
src/lxc/lxcseccomp.h
Normal file
49
src/lxc/lxcseccomp.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* lxc: linux Container library
|
||||||
|
*
|
||||||
|
* (C) Copyright Canonical, Inc. 2012
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Serge Hallyn <serge.hallyn@canonical.com>
|
||||||
|
*
|
||||||
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _lxc_seccomp_h
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_SECCOMP
|
||||||
|
int lxc_seccomp_load(struct lxc_conf *conf);
|
||||||
|
int lxc_read_seccomp_config(struct lxc_conf *conf);
|
||||||
|
void lxc_seccomp_free(struct lxc_conf *conf);
|
||||||
|
#else
|
||||||
|
static inline int lxc_seccomp_load(struct lxc_conf *conf) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int lxc_read_seccomp_config(struct lxc_conf *conf) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void lxc_seccomp_free(struct lxc_conf *conf) {
|
||||||
|
if (conf->seccomp) {
|
||||||
|
free(conf->seccomp);
|
||||||
|
conf->seccomp = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@ -98,11 +98,28 @@ int lxc_monitor_open(void)
|
|||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lxc_monitor_read(int fd, struct lxc_msg *msg)
|
/* timeout of 0 means return immediately; -1 means wait forever */
|
||||||
|
int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout)
|
||||||
{
|
{
|
||||||
struct sockaddr_un from;
|
struct sockaddr_un from;
|
||||||
socklen_t len = sizeof(from);
|
socklen_t len = sizeof(from);
|
||||||
int ret;
|
int ret;
|
||||||
|
fd_set rfds;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
if (timeout != -1) {
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(fd, &rfds);
|
||||||
|
|
||||||
|
tv.tv_sec = timeout;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
ret = select(fd+1, &rfds, NULL, NULL, &tv);
|
||||||
|
if (ret == -1)
|
||||||
|
return -1;
|
||||||
|
else if (!ret)
|
||||||
|
return -2; // timed out
|
||||||
|
}
|
||||||
|
|
||||||
ret = recvfrom(fd, msg, sizeof(*msg), 0,
|
ret = recvfrom(fd, msg, sizeof(*msg), 0,
|
||||||
(struct sockaddr *)&from, &len);
|
(struct sockaddr *)&from, &len);
|
||||||
@ -114,6 +131,11 @@ int lxc_monitor_read(int fd, struct lxc_msg *msg)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lxc_monitor_read(int fd, struct lxc_msg *msg)
|
||||||
|
{
|
||||||
|
return lxc_monitor_read_timeout(fd, msg, -1);
|
||||||
|
}
|
||||||
|
|
||||||
int lxc_monitor_close(int fd)
|
int lxc_monitor_close(int fd)
|
||||||
{
|
{
|
||||||
return close(fd);
|
return close(fd);
|
||||||
|
@ -69,3 +69,48 @@ pid_t lxc_clone(int (*fn)(void *), void *arg, int flags)
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *namespaces_list[] = {
|
||||||
|
"MOUNT", "PID", "UTSNAME", "IPC",
|
||||||
|
"USER", "NETWORK"
|
||||||
|
};
|
||||||
|
static int cloneflags_list[] = {
|
||||||
|
CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC,
|
||||||
|
CLONE_NEWUSER, CLONE_NEWNET
|
||||||
|
};
|
||||||
|
|
||||||
|
int lxc_namespace_2_cloneflag(char *namespace)
|
||||||
|
{
|
||||||
|
int i, len;
|
||||||
|
len = sizeof(namespaces_list)/sizeof(namespaces_list[0]);
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
if (!strcmp(namespaces_list[i], namespace))
|
||||||
|
return cloneflags_list[i];
|
||||||
|
|
||||||
|
ERROR("invalid namespace name %s", namespace);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lxc_fill_namespace_flags(char *flaglist, int *flags)
|
||||||
|
{
|
||||||
|
char *token, *saveptr = NULL;
|
||||||
|
int aflag;
|
||||||
|
|
||||||
|
if (!flaglist) {
|
||||||
|
ERROR("need at least one namespace to unshare");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = strtok_r(flaglist, "|", &saveptr);
|
||||||
|
while (token) {
|
||||||
|
|
||||||
|
aflag = lxc_namespace_2_cloneflag(token);
|
||||||
|
if (aflag < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*flags |= aflag;
|
||||||
|
|
||||||
|
token = strtok_r(NULL, "|", &saveptr);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -54,4 +54,7 @@ int clone(int (*fn)(void *), void *child_stack,
|
|||||||
|
|
||||||
extern pid_t lxc_clone(int (*fn)(void *), void *arg, int flags);
|
extern pid_t lxc_clone(int (*fn)(void *), void *arg, int flags);
|
||||||
|
|
||||||
|
extern int lxc_namespace_2_cloneflag(char *namespace);
|
||||||
|
extern int lxc_fill_namespace_flags(char *flaglist, int *flags);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
|
|
||||||
#include "nl.h"
|
#include "nl.h"
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
|
#include "conf.h"
|
||||||
|
|
||||||
#ifndef IFLA_LINKMODE
|
#ifndef IFLA_LINKMODE
|
||||||
# define IFLA_LINKMODE 17
|
# define IFLA_LINKMODE 17
|
||||||
@ -1004,3 +1005,18 @@ int lxc_bridge_attach(const char *bridge, const char *ifname)
|
|||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char* lxc_network_types[LXC_NET_MAXCONFTYPE + 1] = {
|
||||||
|
[LXC_NET_VETH] = "veth",
|
||||||
|
[LXC_NET_MACVLAN] = "macvlan",
|
||||||
|
[LXC_NET_VLAN] = "vlan",
|
||||||
|
[LXC_NET_PHYS] = "phys",
|
||||||
|
[LXC_NET_EMPTY] = "empty",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *lxc_net_type_to_str(int type)
|
||||||
|
{
|
||||||
|
if (type < 0 || type > LXC_NET_MAXCONFTYPE)
|
||||||
|
return NULL;
|
||||||
|
return lxc_network_types[type];
|
||||||
|
}
|
||||||
|
@ -122,4 +122,5 @@ extern int lxc_neigh_proxy_on(const char *name, int family);
|
|||||||
*/
|
*/
|
||||||
extern int lxc_neigh_proxy_off(const char *name, int family);
|
extern int lxc_neigh_proxy_off(const char *name, int family);
|
||||||
|
|
||||||
|
extern const char *lxc_net_type_to_str(int type);
|
||||||
#endif
|
#endif
|
||||||
|
155
src/lxc/seccomp.c
Normal file
155
src/lxc/seccomp.c
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* lxc: linux Container library
|
||||||
|
*
|
||||||
|
* (C) Copyright Canonical, Inc. 2012
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Serge Hallyn <serge.hallyn@canonical.com>
|
||||||
|
*
|
||||||
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <seccomp.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <seccomp.h>
|
||||||
|
#include "config.h"
|
||||||
|
#include "lxcseccomp.h"
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
lxc_log_define(lxc_seccomp, lxc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The first line of the config file has a policy language version
|
||||||
|
* the second line has some directives
|
||||||
|
* then comes policy subject to the directives
|
||||||
|
* right now version must be '1'
|
||||||
|
* the directives must include 'whitelist' (only type of policy currently
|
||||||
|
* supported) and can include 'debug' (though debug is not yet supported).
|
||||||
|
*/
|
||||||
|
static int parse_config(FILE *f, struct lxc_conf *conf)
|
||||||
|
{
|
||||||
|
char line[1024];
|
||||||
|
int ret, version;
|
||||||
|
|
||||||
|
ret = fscanf(f, "%d\n", &version);
|
||||||
|
if (ret != 1 || version != 1) {
|
||||||
|
ERROR("invalid version");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!fgets(line, 1024, f)) {
|
||||||
|
ERROR("invalid config file");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!strstr(line, "whitelist")) {
|
||||||
|
ERROR("only whitelist policy is supported");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (strstr(line, "debug")) {
|
||||||
|
ERROR("debug not yet implemented");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* now read in the whitelist entries one per line */
|
||||||
|
while (fgets(line, 1024, f)) {
|
||||||
|
int nr;
|
||||||
|
ret = sscanf(line, "%d", &nr);
|
||||||
|
if (ret != 1)
|
||||||
|
return -1;
|
||||||
|
ret = seccomp_rule_add(
|
||||||
|
#if HAVE_SCMP_FILTER_CTX
|
||||||
|
conf->seccomp_ctx,
|
||||||
|
#endif
|
||||||
|
SCMP_ACT_ALLOW, nr, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
ERROR("failed loading allow rule for %d\n", nr);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lxc_read_seccomp_config(struct lxc_conf *conf)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!conf->seccomp)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#if HAVE_SCMP_FILTER_CTX
|
||||||
|
/* XXX for debug, pass in SCMP_ACT_TRAP */
|
||||||
|
conf->seccomp_ctx = seccomp_init(SCMP_ACT_ERRNO(31));
|
||||||
|
ret = !conf->seccomp_ctx;
|
||||||
|
#else
|
||||||
|
ret = seccomp_init(SCMP_ACT_ERRNO(31)) < 0;
|
||||||
|
#endif
|
||||||
|
if (ret) {
|
||||||
|
ERROR("failed initializing seccomp");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* turn of no-new-privs. We don't want it in lxc, and it breaks
|
||||||
|
* with apparmor */
|
||||||
|
if (seccomp_attr_set(
|
||||||
|
#if HAVE_SCMP_FILTER_CTX
|
||||||
|
conf->seccomp_ctx,
|
||||||
|
#endif
|
||||||
|
SCMP_FLTATR_CTL_NNP, 0)) {
|
||||||
|
ERROR("failed to turn off n-new-privs\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
f = fopen(conf->seccomp, "r");
|
||||||
|
if (!f) {
|
||||||
|
SYSERROR("failed to open seccomp policy file %s\n", conf->seccomp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ret = parse_config(f, conf);
|
||||||
|
fclose(f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lxc_seccomp_load(struct lxc_conf *conf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
if (!conf->seccomp)
|
||||||
|
return 0;
|
||||||
|
ret = seccomp_load(
|
||||||
|
#if HAVE_SCMP_FILTER_CTX
|
||||||
|
conf->seccomp_ctx
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
if (ret < 0) {
|
||||||
|
ERROR("Error loading the seccomp policy");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lxc_seccomp_free(struct lxc_conf *conf) {
|
||||||
|
if (conf->seccomp) {
|
||||||
|
free(conf->seccomp);
|
||||||
|
conf->seccomp = NULL;
|
||||||
|
}
|
||||||
|
#if HAVE_SCMP_FILTER_CTX
|
||||||
|
if (conf->seccomp_ctx) {
|
||||||
|
seccomp_release(conf->seccomp_ctx);
|
||||||
|
conf->seccomp_ctx = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
@ -127,6 +127,7 @@ int signalfd(int fd, const sigset_t *mask, int flags)
|
|||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
#include "namespace.h"
|
#include "namespace.h"
|
||||||
#include "apparmor.h"
|
#include "apparmor.h"
|
||||||
|
#include "lxcseccomp.h"
|
||||||
|
|
||||||
lxc_log_define(lxc_start, lxc);
|
lxc_log_define(lxc_start, lxc);
|
||||||
|
|
||||||
@ -278,6 +279,29 @@ int lxc_pid_callback(int fd, struct lxc_request *request,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lxc_clone_flags_callback(int fd, struct lxc_request *request,
|
||||||
|
struct lxc_handler *handler)
|
||||||
|
{
|
||||||
|
struct lxc_answer answer;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
answer.pid = 0;
|
||||||
|
answer.ret = handler->clone_flags;
|
||||||
|
|
||||||
|
ret = send(fd, &answer, sizeof(answer), 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
WARN("failed to send answer to the peer");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != sizeof(answer)) {
|
||||||
|
ERROR("partial answer sent");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state)
|
int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state)
|
||||||
{
|
{
|
||||||
handler->state = state;
|
handler->state = state;
|
||||||
@ -353,6 +377,11 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf)
|
|||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lxc_read_seccomp_config(conf) != 0) {
|
||||||
|
ERROR("failed loading seccomp policy");
|
||||||
|
goto out_free_name;
|
||||||
|
}
|
||||||
|
|
||||||
/* Begin the set the state to STARTING*/
|
/* Begin the set the state to STARTING*/
|
||||||
if (lxc_set_state(name, handler, STARTING)) {
|
if (lxc_set_state(name, handler, STARTING)) {
|
||||||
ERROR("failed to set state '%s'", lxc_state2str(STARTING));
|
ERROR("failed to set state '%s'", lxc_state2str(STARTING));
|
||||||
@ -530,6 +559,9 @@ static int do_start(void *data)
|
|||||||
if (apparmor_load(handler) < 0)
|
if (apparmor_load(handler) < 0)
|
||||||
goto out_warn_father;
|
goto out_warn_father;
|
||||||
|
|
||||||
|
if (lxc_seccomp_load(handler->conf) != 0)
|
||||||
|
goto out_warn_father;
|
||||||
|
|
||||||
if (run_lxc_hooks(handler->name, "start", handler->conf)) {
|
if (run_lxc_hooks(handler->name, "start", handler->conf)) {
|
||||||
ERROR("failed to run start hooks for container '%s'.", handler->name);
|
ERROR("failed to run start hooks for container '%s'.", handler->name);
|
||||||
goto out_warn_father;
|
goto out_warn_father;
|
||||||
@ -547,9 +579,39 @@ out_warn_father:
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int save_phys_nics(struct lxc_conf *conf)
|
||||||
|
{
|
||||||
|
struct lxc_list *iterator;
|
||||||
|
|
||||||
|
lxc_list_for_each(iterator, &conf->network) {
|
||||||
|
struct lxc_netdev *netdev = iterator->elem;
|
||||||
|
|
||||||
|
if (netdev->type != LXC_NET_PHYS)
|
||||||
|
continue;
|
||||||
|
conf->saved_nics = realloc(conf->saved_nics,
|
||||||
|
(conf->num_savednics+1)*sizeof(struct saved_nic));
|
||||||
|
if (!conf->saved_nics) {
|
||||||
|
SYSERROR("failed to allocate memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
conf->saved_nics[conf->num_savednics].ifindex = netdev->ifindex;
|
||||||
|
conf->saved_nics[conf->num_savednics].orig_name = strdup(netdev->link);
|
||||||
|
if (!conf->saved_nics[conf->num_savednics].orig_name) {
|
||||||
|
SYSERROR("failed to allocate memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
INFO("stored saved_nic #%d idx %d name %s\n", conf->num_savednics,
|
||||||
|
conf->saved_nics[conf->num_savednics].ifindex,
|
||||||
|
conf->saved_nics[conf->num_savednics].orig_name);
|
||||||
|
conf->num_savednics++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int lxc_spawn(struct lxc_handler *handler)
|
int lxc_spawn(struct lxc_handler *handler)
|
||||||
{
|
{
|
||||||
int clone_flags;
|
|
||||||
int failed_before_rename = 0;
|
int failed_before_rename = 0;
|
||||||
const char *name = handler->name;
|
const char *name = handler->name;
|
||||||
int pinfd;
|
int pinfd;
|
||||||
@ -557,10 +619,10 @@ int lxc_spawn(struct lxc_handler *handler)
|
|||||||
if (lxc_sync_init(handler))
|
if (lxc_sync_init(handler))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
clone_flags = CLONE_NEWUTS|CLONE_NEWPID|CLONE_NEWIPC|CLONE_NEWNS;
|
handler->clone_flags = CLONE_NEWUTS|CLONE_NEWPID|CLONE_NEWIPC|CLONE_NEWNS;
|
||||||
if (!lxc_list_empty(&handler->conf->network)) {
|
if (!lxc_list_empty(&handler->conf->network)) {
|
||||||
|
|
||||||
clone_flags |= CLONE_NEWNET;
|
handler->clone_flags |= CLONE_NEWNET;
|
||||||
|
|
||||||
/* Find gateway addresses from the link device, which is
|
/* Find gateway addresses from the link device, which is
|
||||||
* no longer accessible inside the container. Do this
|
* no longer accessible inside the container. Do this
|
||||||
@ -582,6 +644,11 @@ int lxc_spawn(struct lxc_handler *handler)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (save_phys_nics(handler->conf)) {
|
||||||
|
ERROR("failed to save physical nic info");
|
||||||
|
goto out_abort;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* if the rootfs is not a blockdev, prevent the container from
|
* if the rootfs is not a blockdev, prevent the container from
|
||||||
* marking it readonly.
|
* marking it readonly.
|
||||||
@ -594,7 +661,7 @@ int lxc_spawn(struct lxc_handler *handler)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Create a process in a new set of namespaces */
|
/* Create a process in a new set of namespaces */
|
||||||
handler->pid = lxc_clone(do_start, handler, clone_flags);
|
handler->pid = lxc_clone(do_start, handler, handler->clone_flags);
|
||||||
if (handler->pid < 0) {
|
if (handler->pid < 0) {
|
||||||
SYSERROR("failed to fork into a new namespace");
|
SYSERROR("failed to fork into a new namespace");
|
||||||
goto out_delete_net;
|
goto out_delete_net;
|
||||||
@ -612,7 +679,7 @@ int lxc_spawn(struct lxc_handler *handler)
|
|||||||
goto out_delete_net;
|
goto out_delete_net;
|
||||||
|
|
||||||
/* Create the network configuration */
|
/* Create the network configuration */
|
||||||
if (clone_flags & CLONE_NEWNET) {
|
if (handler->clone_flags & CLONE_NEWNET) {
|
||||||
if (lxc_assign_network(&handler->conf->network, handler->pid)) {
|
if (lxc_assign_network(&handler->conf->network, handler->pid)) {
|
||||||
ERROR("failed to create the configured network");
|
ERROR("failed to create the configured network");
|
||||||
goto out_delete_net;
|
goto out_delete_net;
|
||||||
@ -642,8 +709,8 @@ int lxc_spawn(struct lxc_handler *handler)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_delete_net:
|
out_delete_net:
|
||||||
if (clone_flags & CLONE_NEWNET)
|
if (handler->clone_flags & CLONE_NEWNET)
|
||||||
lxc_delete_network(&handler->conf->network);
|
lxc_delete_network(handler);
|
||||||
out_abort:
|
out_abort:
|
||||||
lxc_abort(name, handler);
|
lxc_abort(name, handler);
|
||||||
lxc_sync_fini(handler);
|
lxc_sync_fini(handler);
|
||||||
@ -675,7 +742,7 @@ int __lxc_start(const char *name, struct lxc_conf *conf,
|
|||||||
err = lxc_spawn(handler);
|
err = lxc_spawn(handler);
|
||||||
if (err) {
|
if (err) {
|
||||||
ERROR("failed to spawn '%s'", name);
|
ERROR("failed to spawn '%s'", name);
|
||||||
goto out_fini;
|
goto out_fini_nonet;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = lxc_poll(name, handler);
|
err = lxc_poll(name, handler);
|
||||||
@ -708,8 +775,13 @@ int __lxc_start(const char *name, struct lxc_conf *conf,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lxc_rename_phys_nics_on_shutdown(handler->conf);
|
||||||
|
|
||||||
err = lxc_error_set_and_log(handler->pid, status);
|
err = lxc_error_set_and_log(handler->pid, status);
|
||||||
out_fini:
|
out_fini:
|
||||||
|
lxc_delete_network(handler);
|
||||||
|
|
||||||
|
out_fini_nonet:
|
||||||
lxc_cgroup_destroy(name);
|
lxc_cgroup_destroy(name);
|
||||||
lxc_fini(name, handler);
|
lxc_fini(name, handler);
|
||||||
return err;
|
return err;
|
||||||
|
@ -39,6 +39,7 @@ struct lxc_handler {
|
|||||||
pid_t pid;
|
pid_t pid;
|
||||||
char *name;
|
char *name;
|
||||||
lxc_state_t state;
|
lxc_state_t state;
|
||||||
|
int clone_flags;
|
||||||
int sigfd;
|
int sigfd;
|
||||||
sigset_t oldmask;
|
sigset_t oldmask;
|
||||||
struct lxc_conf *conf;
|
struct lxc_conf *conf;
|
||||||
|
115
src/lxc/state.c
115
src/lxc/state.c
@ -21,6 +21,7 @@
|
|||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@ -31,9 +32,11 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
|
|
||||||
|
#include <lxc/lxc.h>
|
||||||
#include <lxc/log.h>
|
#include <lxc/log.h>
|
||||||
#include <lxc/start.h>
|
#include <lxc/start.h>
|
||||||
#include <lxc/cgroup.h>
|
#include <lxc/cgroup.h>
|
||||||
|
#include <lxc/monitor.h>
|
||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
@ -162,3 +165,115 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int fillwaitedstates(const char *strstates, int *states)
|
||||||
|
{
|
||||||
|
char *token, *saveptr = NULL;
|
||||||
|
char *strstates_dup = strdup(strstates);
|
||||||
|
int state;
|
||||||
|
|
||||||
|
if (!strstates_dup)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
token = strtok_r(strstates_dup, "|", &saveptr);
|
||||||
|
while (token) {
|
||||||
|
|
||||||
|
state = lxc_str2state(token);
|
||||||
|
if (state < 0) {
|
||||||
|
free(strstates_dup);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
states[state] = 1;
|
||||||
|
|
||||||
|
token = strtok_r(NULL, "|", &saveptr);
|
||||||
|
}
|
||||||
|
free(strstates_dup);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int lxc_wait(const char *lxcname, const char *states, int timeout)
|
||||||
|
{
|
||||||
|
struct lxc_msg msg;
|
||||||
|
int state, ret;
|
||||||
|
int s[MAX_STATE] = { }, fd;
|
||||||
|
|
||||||
|
if (fillwaitedstates(states, s))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
fd = lxc_monitor_open();
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if container present,
|
||||||
|
* then check if already in requested state
|
||||||
|
*/
|
||||||
|
ret = -1;
|
||||||
|
state = lxc_getstate(lxcname);
|
||||||
|
if (state < 0) {
|
||||||
|
goto out_close;
|
||||||
|
} else if ((state >= 0) && (s[state])) {
|
||||||
|
ret = 0;
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
int elapsed_time, curtime = 0;
|
||||||
|
struct timeval tv;
|
||||||
|
int stop = 0;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
if (timeout != -1) {
|
||||||
|
retval = gettimeofday(&tv, NULL);
|
||||||
|
if (retval)
|
||||||
|
goto out_close;
|
||||||
|
curtime = tv.tv_sec;
|
||||||
|
}
|
||||||
|
if (lxc_monitor_read_timeout(fd, &msg, timeout) < 0)
|
||||||
|
goto out_close;
|
||||||
|
|
||||||
|
if (timeout != -1) {
|
||||||
|
retval = gettimeofday(&tv, NULL);
|
||||||
|
if (retval)
|
||||||
|
goto out_close;
|
||||||
|
elapsed_time = tv.tv_sec - curtime;
|
||||||
|
if (timeout - elapsed_time <= 0)
|
||||||
|
stop = 1;
|
||||||
|
timeout -= elapsed_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(lxcname, msg.name)) {
|
||||||
|
if (stop) {
|
||||||
|
ret = -2;
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (msg.type) {
|
||||||
|
case lxc_msg_state:
|
||||||
|
if (msg.value < 0 || msg.value >= MAX_STATE) {
|
||||||
|
ERROR("Receive an invalid state number '%d'",
|
||||||
|
msg.value);
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s[msg.value]) {
|
||||||
|
ret = 0;
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (stop) {
|
||||||
|
ret = -2;
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
/* just ignore garbage */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out_close:
|
||||||
|
lxc_monitor_close(fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -33,5 +33,6 @@ extern lxc_state_t lxc_getstate(const char *name);
|
|||||||
|
|
||||||
extern lxc_state_t lxc_str2state(const char *state);
|
extern lxc_state_t lxc_str2state(const char *state);
|
||||||
extern const char *lxc_state2str(lxc_state_t state);
|
extern const char *lxc_state2str(lxc_state_t state);
|
||||||
|
extern int lxc_wait(const char *lxcname, const char *states, int timeout);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
22
src/python-lxc/Makefile.am
Normal file
22
src/python-lxc/Makefile.am
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
if ENABLE_PYTHON
|
||||||
|
|
||||||
|
if HAVE_DEBIAN
|
||||||
|
DISTSETUPOPTS=--install-layout=deb
|
||||||
|
else
|
||||||
|
DISTSETUPOPTS=
|
||||||
|
endif
|
||||||
|
|
||||||
|
all:
|
||||||
|
CFLAGS="$(CFLAGS) -I ../../src -L../../src/lxc/" $(PYTHON) setup.py build
|
||||||
|
|
||||||
|
install:
|
||||||
|
if [ "$(DESTDIR)" = "" ]; then \
|
||||||
|
$(PYTHON) setup.py install --prefix=$(prefix) --no-compile $(DISTSETUPOPTS); \
|
||||||
|
else \
|
||||||
|
$(PYTHON) setup.py install --root=$(DESTDIR) --prefix=$(prefix) --no-compile $(DISTSETUPOPTS); \
|
||||||
|
fi
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build
|
||||||
|
|
||||||
|
endif
|
158
src/python-lxc/examples/api_test.py.in
Normal file
158
src/python-lxc/examples/api_test.py.in
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
#
|
||||||
|
# api_test.py: Test/demo of the python3-lxc API
|
||||||
|
#
|
||||||
|
# (C) Copyright Canonical Ltd. 2012
|
||||||
|
#
|
||||||
|
# Authors:
|
||||||
|
# Stéphane Graber <stgraber@ubuntu.com>
|
||||||
|
#
|
||||||
|
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
|
||||||
|
import warnings
|
||||||
|
warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable")
|
||||||
|
|
||||||
|
import lxc
|
||||||
|
import uuid
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Some constants
|
||||||
|
LXC_PATH_LIB = "@LXCPATH@"
|
||||||
|
LXC_TEMPLATE = "ubuntu"
|
||||||
|
|
||||||
|
# Let's pick a random name, avoiding clashes
|
||||||
|
CONTAINER_NAME = str(uuid.uuid1())
|
||||||
|
CLONE_NAME = str(uuid.uuid1())
|
||||||
|
|
||||||
|
## Instantiate the container instance
|
||||||
|
print("Getting instance for '%s'" % CONTAINER_NAME)
|
||||||
|
container = lxc.Container(CONTAINER_NAME)
|
||||||
|
|
||||||
|
# A few basic checks of the current state
|
||||||
|
assert(container.config_file_name == "%s/%s/config" %
|
||||||
|
(LXC_PATH_LIB, CONTAINER_NAME))
|
||||||
|
assert(not container.defined)
|
||||||
|
assert(container.init_pid == -1)
|
||||||
|
assert(container.name == CONTAINER_NAME)
|
||||||
|
assert(not container.running)
|
||||||
|
assert(container.state == "STOPPED")
|
||||||
|
|
||||||
|
## Create a rootfs
|
||||||
|
print("Creating rootfs using '%s'" % LXC_TEMPLATE)
|
||||||
|
container.create(LXC_TEMPLATE)
|
||||||
|
|
||||||
|
assert(container.defined)
|
||||||
|
assert(container.name == CONTAINER_NAME
|
||||||
|
== container.get_config_item("lxc.utsname"))
|
||||||
|
assert(container.name in lxc.list_containers())
|
||||||
|
|
||||||
|
## Test the config
|
||||||
|
print("Testing the configuration")
|
||||||
|
capdrop = container.get_config_item("lxc.cap.drop")
|
||||||
|
container.clear_config_item("lxc.cap.drop")
|
||||||
|
container.set_config_item("lxc.cap.drop", capdrop[:-1])
|
||||||
|
container.append_config_item("lxc.cap.drop", capdrop[-1])
|
||||||
|
container.save_config()
|
||||||
|
|
||||||
|
# A few basic checks of the current state
|
||||||
|
assert(isinstance(capdrop, list))
|
||||||
|
assert(capdrop == container.get_config_item("lxc.cap.drop"))
|
||||||
|
|
||||||
|
## Test the networking
|
||||||
|
print("Testing the networking")
|
||||||
|
|
||||||
|
# A few basic checks of the current state
|
||||||
|
assert("name" in container.get_keys("lxc.network.0"))
|
||||||
|
assert(len(container.network) == 1)
|
||||||
|
assert(container.network[0].hwaddr.startswith("00:16:3e"))
|
||||||
|
|
||||||
|
## Starting the container
|
||||||
|
print("Starting the container")
|
||||||
|
container.start()
|
||||||
|
container.wait("RUNNING", 3)
|
||||||
|
|
||||||
|
# A few basic checks of the current state
|
||||||
|
assert(container.init_pid > 1)
|
||||||
|
assert(container.running)
|
||||||
|
assert(container.state == "RUNNING")
|
||||||
|
|
||||||
|
## Checking IP address
|
||||||
|
print("Getting the IP addresses")
|
||||||
|
ips = container.get_ips(timeout=10)
|
||||||
|
container.attach("NETWORK|UTSNAME", "/sbin/ifconfig", "eth0")
|
||||||
|
|
||||||
|
# A few basic checks of the current state
|
||||||
|
assert(len(ips) > 0)
|
||||||
|
|
||||||
|
## Testing cgroups a bit
|
||||||
|
print("Testing cgroup API")
|
||||||
|
max_mem = container.get_cgroup_item("memory.max_usage_in_bytes")
|
||||||
|
current_limit = container.get_cgroup_item("memory.limit_in_bytes")
|
||||||
|
assert(container.set_cgroup_item("memory.limit_in_bytes", max_mem))
|
||||||
|
assert(container.get_cgroup_item("memory.limit_in_bytes") != current_limit)
|
||||||
|
|
||||||
|
## Freezing the container
|
||||||
|
print("Freezing the container")
|
||||||
|
container.freeze()
|
||||||
|
container.wait("FROZEN", 3)
|
||||||
|
|
||||||
|
# A few basic checks of the current state
|
||||||
|
assert(container.init_pid > 1)
|
||||||
|
assert(container.running)
|
||||||
|
assert(container.state == "FROZEN")
|
||||||
|
|
||||||
|
## Unfreezing the container
|
||||||
|
print("Unfreezing the container")
|
||||||
|
container.unfreeze()
|
||||||
|
container.wait("RUNNING", 3)
|
||||||
|
|
||||||
|
# A few basic checks of the current state
|
||||||
|
assert(container.init_pid > 1)
|
||||||
|
assert(container.running)
|
||||||
|
assert(container.state == "RUNNING")
|
||||||
|
|
||||||
|
if len(sys.argv) > 1 and sys.argv[1] == "--with-console":
|
||||||
|
## Attaching to tty1
|
||||||
|
print("Attaching to tty1")
|
||||||
|
container.console(tty=1)
|
||||||
|
|
||||||
|
## Shutting down the container
|
||||||
|
print("Shutting down the container")
|
||||||
|
container.shutdown(3)
|
||||||
|
|
||||||
|
if container.running:
|
||||||
|
print("Stopping the container")
|
||||||
|
container.stop()
|
||||||
|
container.wait("STOPPED", 3)
|
||||||
|
|
||||||
|
# A few basic checks of the current state
|
||||||
|
assert(container.init_pid == -1)
|
||||||
|
assert(not container.running)
|
||||||
|
assert(container.state == "STOPPED")
|
||||||
|
|
||||||
|
## Cloning the container
|
||||||
|
print("Cloning the container")
|
||||||
|
clone = lxc.Container(CLONE_NAME)
|
||||||
|
clone.clone(container)
|
||||||
|
clone.start()
|
||||||
|
clone.stop()
|
||||||
|
clone.destroy()
|
||||||
|
|
||||||
|
## Destroy the container
|
||||||
|
print("Destroying the container")
|
||||||
|
container.destroy()
|
||||||
|
|
||||||
|
assert(not container.defined)
|
620
src/python-lxc/lxc.c
Normal file
620
src/python-lxc/lxc.c
Normal file
@ -0,0 +1,620 @@
|
|||||||
|
/*
|
||||||
|
* python-lxc: Python bindings for LXC
|
||||||
|
*
|
||||||
|
* (C) Copyright Canonical Ltd. 2012
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stéphane Graber <stgraber@ubuntu.com>
|
||||||
|
*
|
||||||
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Python.h>
|
||||||
|
#include "structmember.h"
|
||||||
|
#include <lxc/lxccontainer.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
struct lxc_container *container;
|
||||||
|
} Container;
|
||||||
|
|
||||||
|
char**
|
||||||
|
convert_tuple_to_char_pointer_array(PyObject *argv) {
|
||||||
|
int argc = PyTuple_Size(argv);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
char **result = (char**) malloc(sizeof(char*)*argc + 1);
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
PyObject *pyobj = PyTuple_GetItem(argv, i);
|
||||||
|
|
||||||
|
char *str = NULL;
|
||||||
|
PyObject *pystr;
|
||||||
|
if (!PyUnicode_Check(pyobj)) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Expected a string");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pystr = PyUnicode_AsUTF8String(pyobj);
|
||||||
|
str = PyBytes_AsString(pystr);
|
||||||
|
memcpy((char *) &result[i], (char *) &str, sizeof(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
result[argc] = NULL;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
Container_dealloc(Container* self)
|
||||||
|
{
|
||||||
|
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
Container *self;
|
||||||
|
|
||||||
|
self = (Container *)type->tp_alloc(type, 0);
|
||||||
|
|
||||||
|
return (PyObject *)self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
Container_init(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
static char *kwlist[] = {"name", NULL};
|
||||||
|
char *name = NULL;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist,
|
||||||
|
&name))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
self->container = lxc_container_new(name);
|
||||||
|
if (!self->container) {
|
||||||
|
fprintf(stderr, "%d: error creating lxc_container %s\n", __LINE__, name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container properties
|
||||||
|
static PyObject *
|
||||||
|
Container_config_file_name(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
return PyUnicode_FromString(self->container->config_file_name(self->container));
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_defined(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
if (self->container->is_defined(self->container)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_init_pid(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("i", self->container->init_pid(self->container));
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_name(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
return PyUnicode_FromString(self->container->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_running(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
if (self->container->is_running(self->container)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_state(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
return PyUnicode_FromString(self->container->state(self->container));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container Functions
|
||||||
|
static PyObject *
|
||||||
|
Container_clear_config_item(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
static char *kwlist[] = {"key", NULL};
|
||||||
|
char *key = NULL;
|
||||||
|
|
||||||
|
if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist,
|
||||||
|
&key))
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
|
||||||
|
if (self->container->clear_config_item(self->container, key)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_create(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
char* template_name = NULL;
|
||||||
|
char** create_args = {NULL};
|
||||||
|
PyObject *vargs = NULL;
|
||||||
|
static char *kwlist[] = {"template", "args", NULL};
|
||||||
|
|
||||||
|
if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist,
|
||||||
|
&template_name, &vargs))
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
|
||||||
|
if (vargs && PyTuple_Check(vargs)) {
|
||||||
|
create_args = convert_tuple_to_char_pointer_array(vargs);
|
||||||
|
if (!create_args) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->container->create(self->container, template_name, create_args)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_destroy(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
if (self->container->destroy(self->container)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_freeze(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
if (self->container->freeze(self->container)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_get_cgroup_item(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
static char *kwlist[] = {"key", NULL};
|
||||||
|
char* key = NULL;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist,
|
||||||
|
&key))
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
|
||||||
|
len = self->container->get_cgroup_item(self->container, key, NULL, 0);
|
||||||
|
|
||||||
|
if (len <= 0) {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* value = (char*) malloc(sizeof(char)*len + 1);
|
||||||
|
if (self->container->get_cgroup_item(self->container, key, value, len + 1) != len) {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PyUnicode_FromString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_get_config_item(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
static char *kwlist[] = {"key", NULL};
|
||||||
|
char* key = NULL;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist,
|
||||||
|
&key))
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
|
||||||
|
len = self->container->get_config_item(self->container, key, NULL, 0);
|
||||||
|
|
||||||
|
if (len <= 0) {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* value = (char*) malloc(sizeof(char)*len + 1);
|
||||||
|
if (self->container->get_config_item(self->container, key, value, len + 1) != len) {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PyUnicode_FromString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_get_keys(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
static char *kwlist[] = {"key", NULL};
|
||||||
|
char* key = NULL;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist,
|
||||||
|
&key))
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
|
||||||
|
len = self->container->get_keys(self->container, key, NULL, 0);
|
||||||
|
|
||||||
|
if (len <= 0) {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* value = (char*) malloc(sizeof(char)*len + 1);
|
||||||
|
if (self->container->get_keys(self->container, key, value, len + 1) != len) {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PyUnicode_FromString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_load_config(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
static char *kwlist[] = {"path", NULL};
|
||||||
|
char* path = NULL;
|
||||||
|
|
||||||
|
if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist,
|
||||||
|
&path))
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
|
||||||
|
if (self->container->load_config(self->container, path)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_save_config(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
static char *kwlist[] = {"path", NULL};
|
||||||
|
char* path = NULL;
|
||||||
|
|
||||||
|
if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist,
|
||||||
|
&path))
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
|
||||||
|
if (self->container->save_config(self->container, path)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_set_cgroup_item(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
static char *kwlist[] = {"key", "value", NULL};
|
||||||
|
char *key = NULL;
|
||||||
|
char *value = NULL;
|
||||||
|
|
||||||
|
if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss|", kwlist,
|
||||||
|
&key, &value))
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
|
||||||
|
if (self->container->set_cgroup_item(self->container, key, value)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_set_config_item(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
static char *kwlist[] = {"key", "value", NULL};
|
||||||
|
char *key = NULL;
|
||||||
|
char *value = NULL;
|
||||||
|
|
||||||
|
if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss|", kwlist,
|
||||||
|
&key, &value))
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
|
||||||
|
if (self->container->set_config_item(self->container, key, value)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_shutdown(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
static char *kwlist[] = {"timeout", NULL};
|
||||||
|
int timeout = -1;
|
||||||
|
|
||||||
|
if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist,
|
||||||
|
&timeout))
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
|
||||||
|
if (self->container->shutdown(self->container, timeout)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_start(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
char** init_args = {NULL};
|
||||||
|
PyObject *useinit = NULL, *vargs = NULL;
|
||||||
|
int init_useinit = 0;
|
||||||
|
static char *kwlist[] = {"useinit", "cmd", NULL};
|
||||||
|
|
||||||
|
if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist,
|
||||||
|
&useinit, &vargs))
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
|
||||||
|
if (useinit && useinit == Py_True) {
|
||||||
|
init_useinit = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vargs && PyTuple_Check(vargs)) {
|
||||||
|
init_args = convert_tuple_to_char_pointer_array(vargs);
|
||||||
|
if (!init_args) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self->container->want_daemonize(self->container);
|
||||||
|
|
||||||
|
if (self->container->start(self->container, init_useinit, init_args)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_stop(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
if (self->container->stop(self->container)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_unfreeze(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
if (self->container->unfreeze(self->container)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Container_wait(Container *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
static char *kwlist[] = {"state", "timeout", NULL};
|
||||||
|
char *state = NULL;
|
||||||
|
int timeout = -1;
|
||||||
|
|
||||||
|
if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist,
|
||||||
|
&state, &timeout))
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
|
||||||
|
if (self->container->wait(self->container, state, timeout)) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyGetSetDef Container_getseters[] = {
|
||||||
|
{"config_file_name",
|
||||||
|
(getter)Container_config_file_name, 0,
|
||||||
|
"Path to the container configuration",
|
||||||
|
NULL},
|
||||||
|
{"defined",
|
||||||
|
(getter)Container_defined, 0,
|
||||||
|
"Boolean indicating whether the container configuration exists",
|
||||||
|
NULL},
|
||||||
|
{"init_pid",
|
||||||
|
(getter)Container_init_pid, 0,
|
||||||
|
"PID of the container's init process in the host's PID namespace",
|
||||||
|
NULL},
|
||||||
|
{"name",
|
||||||
|
(getter)Container_name, 0,
|
||||||
|
"Container name",
|
||||||
|
NULL},
|
||||||
|
{"running",
|
||||||
|
(getter)Container_running, 0,
|
||||||
|
"Boolean indicating whether the container is running or not",
|
||||||
|
NULL},
|
||||||
|
{"state",
|
||||||
|
(getter)Container_state, 0,
|
||||||
|
"Container state",
|
||||||
|
NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyMethodDef Container_methods[] = {
|
||||||
|
{"clear_config_item", (PyCFunction)Container_clear_config_item, METH_VARARGS | METH_KEYWORDS,
|
||||||
|
"clear_config_item(key) -> boolean\n"
|
||||||
|
"\n"
|
||||||
|
"Clear the current value of a config key."
|
||||||
|
},
|
||||||
|
{"create", (PyCFunction)Container_create, METH_VARARGS | METH_KEYWORDS,
|
||||||
|
"create(template, args = (,)) -> boolean\n"
|
||||||
|
"\n"
|
||||||
|
"Create a new rootfs for the container, using the given template "
|
||||||
|
"and passing some optional arguments to it."
|
||||||
|
},
|
||||||
|
{"destroy", (PyCFunction)Container_destroy, METH_NOARGS,
|
||||||
|
"destroy() -> boolean\n"
|
||||||
|
"\n"
|
||||||
|
"Destroys the container."
|
||||||
|
},
|
||||||
|
{"freeze", (PyCFunction)Container_freeze, METH_NOARGS,
|
||||||
|
"freeze() -> boolean\n"
|
||||||
|
"\n"
|
||||||
|
"Freezes the container and returns its return code."
|
||||||
|
},
|
||||||
|
{"get_cgroup_item", (PyCFunction)Container_get_cgroup_item, METH_VARARGS | METH_KEYWORDS,
|
||||||
|
"get_cgroup_item(key) -> string\n"
|
||||||
|
"\n"
|
||||||
|
"Get the current value of a cgroup entry."
|
||||||
|
},
|
||||||
|
{"get_config_item", (PyCFunction)Container_get_config_item, METH_VARARGS | METH_KEYWORDS,
|
||||||
|
"get_config_item(key) -> string\n"
|
||||||
|
"\n"
|
||||||
|
"Get the current value of a config key."
|
||||||
|
},
|
||||||
|
{"get_keys", (PyCFunction)Container_get_keys, METH_VARARGS | METH_KEYWORDS,
|
||||||
|
"get_keys(key) -> string\n"
|
||||||
|
"\n"
|
||||||
|
"Get a list of valid sub-keys for a key."
|
||||||
|
},
|
||||||
|
{"load_config", (PyCFunction)Container_load_config, METH_VARARGS | METH_KEYWORDS,
|
||||||
|
"load_config(path = DEFAULT) -> boolean\n"
|
||||||
|
"\n"
|
||||||
|
"Read the container configuration from its default "
|
||||||
|
"location or from an alternative location if provided."
|
||||||
|
},
|
||||||
|
{"save_config", (PyCFunction)Container_save_config, METH_VARARGS | METH_KEYWORDS,
|
||||||
|
"save_config(path = DEFAULT) -> boolean\n"
|
||||||
|
"\n"
|
||||||
|
"Save the container configuration to its default "
|
||||||
|
"location or to an alternative location if provided."
|
||||||
|
},
|
||||||
|
{"set_cgroup_item", (PyCFunction)Container_set_cgroup_item, METH_VARARGS | METH_KEYWORDS,
|
||||||
|
"set_cgroup_item(key, value) -> boolean\n"
|
||||||
|
"\n"
|
||||||
|
"Set a cgroup entry to the provided value."
|
||||||
|
},
|
||||||
|
{"set_config_item", (PyCFunction)Container_set_config_item, METH_VARARGS | METH_KEYWORDS,
|
||||||
|
"set_config_item(key, value) -> boolean\n"
|
||||||
|
"\n"
|
||||||
|
"Set a config key to the provided value."
|
||||||
|
},
|
||||||
|
{"shutdown", (PyCFunction)Container_shutdown, METH_VARARGS | METH_KEYWORDS,
|
||||||
|
"shutdown(timeout = -1) -> boolean\n"
|
||||||
|
"\n"
|
||||||
|
"Sends SIGPWR to the container and wait for it to shutdown "
|
||||||
|
"unless timeout is set to a positive value, in which case "
|
||||||
|
"the container will be killed when the timeout is reached."
|
||||||
|
},
|
||||||
|
{"start", (PyCFunction)Container_start, METH_VARARGS | METH_KEYWORDS,
|
||||||
|
"start(useinit = False, cmd = (,)) -> boolean\n"
|
||||||
|
"\n"
|
||||||
|
"Start the container, optionally using lxc-init and "
|
||||||
|
"an alternate init command, then returns its return code."
|
||||||
|
},
|
||||||
|
{"stop", (PyCFunction)Container_stop, METH_NOARGS,
|
||||||
|
"stop() -> boolean\n"
|
||||||
|
"\n"
|
||||||
|
"Stop the container and returns its return code."
|
||||||
|
},
|
||||||
|
{"unfreeze", (PyCFunction)Container_unfreeze, METH_NOARGS,
|
||||||
|
"unfreeze() -> boolean\n"
|
||||||
|
"\n"
|
||||||
|
"Unfreezes the container and returns its return code."
|
||||||
|
},
|
||||||
|
{"wait", (PyCFunction)Container_wait, METH_VARARGS | METH_KEYWORDS,
|
||||||
|
"wait(state, timeout = -1) -> boolean\n"
|
||||||
|
"\n"
|
||||||
|
"Wait for the container to reach a given state or timeout."
|
||||||
|
},
|
||||||
|
{NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyTypeObject _lxc_ContainerType = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"lxc.Container", /* tp_name */
|
||||||
|
sizeof(Container), /* tp_basicsize */
|
||||||
|
0, /* tp_itemsize */
|
||||||
|
(destructor)Container_dealloc, /* tp_dealloc */
|
||||||
|
0, /* tp_print */
|
||||||
|
0, /* tp_getattr */
|
||||||
|
0, /* tp_setattr */
|
||||||
|
0, /* tp_reserved */
|
||||||
|
0, /* tp_repr */
|
||||||
|
0, /* tp_as_number */
|
||||||
|
0, /* tp_as_sequence */
|
||||||
|
0, /* tp_as_mapping */
|
||||||
|
0, /* tp_hash */
|
||||||
|
0, /* tp_call */
|
||||||
|
0, /* tp_str */
|
||||||
|
0, /* tp_getattro */
|
||||||
|
0, /* tp_setattro */
|
||||||
|
0, /* tp_as_buffer */
|
||||||
|
Py_TPFLAGS_DEFAULT |
|
||||||
|
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||||
|
"Container objects", /* tp_doc */
|
||||||
|
0, /* tp_traverse */
|
||||||
|
0, /* tp_clear */
|
||||||
|
0, /* tp_richcompare */
|
||||||
|
0, /* tp_weaklistoffset */
|
||||||
|
0, /* tp_iter */
|
||||||
|
0, /* tp_iternext */
|
||||||
|
Container_methods, /* tp_methods */
|
||||||
|
0, /* tp_members */
|
||||||
|
Container_getseters, /* tp_getset */
|
||||||
|
0, /* tp_base */
|
||||||
|
0, /* tp_dict */
|
||||||
|
0, /* tp_descr_get */
|
||||||
|
0, /* tp_descr_set */
|
||||||
|
0, /* tp_dictoffset */
|
||||||
|
(initproc)Container_init, /* tp_init */
|
||||||
|
0, /* tp_alloc */
|
||||||
|
Container_new, /* tp_new */
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyModuleDef _lxcmodule = {
|
||||||
|
PyModuleDef_HEAD_INIT,
|
||||||
|
"_lxc",
|
||||||
|
"Binding for liblxc in python",
|
||||||
|
-1,
|
||||||
|
NULL, NULL, NULL, NULL, NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
PyMODINIT_FUNC
|
||||||
|
PyInit__lxc(void)
|
||||||
|
{
|
||||||
|
PyObject* m;
|
||||||
|
|
||||||
|
if (PyType_Ready(&_lxc_ContainerType) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
m = PyModule_Create(&_lxcmodule);
|
||||||
|
if (m == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
Py_INCREF(&_lxc_ContainerType);
|
||||||
|
PyModule_AddObject(m, "Container", (PyObject *)&_lxc_ContainerType);
|
||||||
|
return m;
|
||||||
|
}
|
468
src/python-lxc/lxc/__init__.py.in
Normal file
468
src/python-lxc/lxc/__init__.py.in
Normal file
@ -0,0 +1,468 @@
|
|||||||
|
#
|
||||||
|
# python-lxc: Python bindings for LXC
|
||||||
|
#
|
||||||
|
# (C) Copyright Canonical Ltd. 2012
|
||||||
|
#
|
||||||
|
# Authors:
|
||||||
|
# Stéphane Graber <stgraber@ubuntu.com>
|
||||||
|
#
|
||||||
|
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
|
||||||
|
import _lxc
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import stat
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
warnings.warn("The python-lxc API isn't yet stable "
|
||||||
|
"and may change at any point in the future.", Warning, 2)
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerNetwork():
|
||||||
|
props = {}
|
||||||
|
|
||||||
|
def __init__(self, container, index):
|
||||||
|
self.container = container
|
||||||
|
self.index = index
|
||||||
|
|
||||||
|
for key in self.container.get_keys("lxc.network.%s" % self.index):
|
||||||
|
if "." in key:
|
||||||
|
self.props[key.replace(".", "_")] = key
|
||||||
|
else:
|
||||||
|
self.props[key] = key
|
||||||
|
|
||||||
|
if not self.props:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __delattr__(self, key):
|
||||||
|
if key in ["container", "index", "props"]:
|
||||||
|
return object.__delattr__(self, key)
|
||||||
|
|
||||||
|
if key not in self.props:
|
||||||
|
raise AttributeError("'%s' network has no attribute '%s'" % (
|
||||||
|
self.__get_network_item("type"), key))
|
||||||
|
|
||||||
|
return self.__clear_network_item(self.props[key])
|
||||||
|
|
||||||
|
def __dir__(self):
|
||||||
|
return sorted(self.props.keys())
|
||||||
|
|
||||||
|
def __getattr__(self, key):
|
||||||
|
if key in ["container", "index", "props"]:
|
||||||
|
return object.__getattribute__(self, key)
|
||||||
|
|
||||||
|
if key not in self.props:
|
||||||
|
raise AttributeError("'%s' network has no attribute '%s'" % (
|
||||||
|
self.__get_network_item("type"), key))
|
||||||
|
|
||||||
|
return self.__get_network_item(self.props[key])
|
||||||
|
|
||||||
|
def __hasattr__(self, key):
|
||||||
|
if key in ["container", "index", "props"]:
|
||||||
|
return object.__hasattr__(self, key)
|
||||||
|
|
||||||
|
if key not in self.props:
|
||||||
|
raise AttributeError("'%s' network has no attribute '%s'" % (
|
||||||
|
self.__get_network_item("type"), key))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "'%s' network at index '%s'" % (
|
||||||
|
self.__get_network_item("type"), self.index)
|
||||||
|
|
||||||
|
def __setattr__(self, key, value):
|
||||||
|
if key in ["container", "index", "props"]:
|
||||||
|
return object.__setattr__(self, key, value)
|
||||||
|
|
||||||
|
if key not in self.props:
|
||||||
|
raise AttributeError("'%s' network has no attribute '%s'" % (
|
||||||
|
self.__get_network_item("type"), key))
|
||||||
|
|
||||||
|
return self.__set_network_item(self.props[key], value)
|
||||||
|
|
||||||
|
def __clear_network_item(self, key):
|
||||||
|
return self.container.clear_config_item("lxc.network.%s.%s" % (
|
||||||
|
self.index, key))
|
||||||
|
|
||||||
|
def __get_network_item(self, key):
|
||||||
|
return self.container.get_config_item("lxc.network.%s.%s" % (
|
||||||
|
self.index, key))
|
||||||
|
|
||||||
|
def __set_network_item(self, key, value):
|
||||||
|
return self.container.set_config_item("lxc.network.%s.%s" % (
|
||||||
|
self.index, key), value)
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerNetworkList():
|
||||||
|
def __init__(self, container):
|
||||||
|
self.container = container
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
if index >= len(self):
|
||||||
|
raise IndexError("list index out of range")
|
||||||
|
|
||||||
|
return ContainerNetwork(self.container, index)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
values = self.container.get_config_item("lxc.network")
|
||||||
|
|
||||||
|
if values:
|
||||||
|
return len(values)
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def add(self, network_type):
|
||||||
|
index = len(self)
|
||||||
|
|
||||||
|
return self.container.set_config_item("lxc.network.%s.type" % index,
|
||||||
|
network_type)
|
||||||
|
|
||||||
|
def remove(self, index):
|
||||||
|
count = len(self)
|
||||||
|
if index >= count:
|
||||||
|
raise IndexError("list index out of range")
|
||||||
|
|
||||||
|
return self.container.clear_config_item("lxc.network.%s" % index)
|
||||||
|
|
||||||
|
|
||||||
|
class Container(_lxc.Container):
|
||||||
|
def __init__(self, name):
|
||||||
|
"""
|
||||||
|
Creates a new Container instance.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
raise Exception("Running as non-root.")
|
||||||
|
|
||||||
|
_lxc.Container.__init__(self, name)
|
||||||
|
self.network = ContainerNetworkList(self)
|
||||||
|
|
||||||
|
def add_device_node(self, path, destpath=None):
|
||||||
|
"""
|
||||||
|
Add block/char device to running container.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.running:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not destpath:
|
||||||
|
destpath = path
|
||||||
|
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Lookup the source
|
||||||
|
path_stat = os.stat(path)
|
||||||
|
mode = stat.S_IMODE(path_stat.st_mode)
|
||||||
|
|
||||||
|
# Allow the target
|
||||||
|
if stat.S_ISBLK(path_stat.st_mode):
|
||||||
|
self.set_cgroup_item("devices.allow",
|
||||||
|
"b %s:%s rwm" %
|
||||||
|
(int(path_stat.st_rdev / 256),
|
||||||
|
int(path_stat.st_rdev % 256)))
|
||||||
|
elif stat.S_ISCHR(path_stat.st_mode):
|
||||||
|
self.set_cgroup_item("devices.allow",
|
||||||
|
"c %s:%s rwm" %
|
||||||
|
(int(path_stat.st_rdev / 256),
|
||||||
|
int(path_stat.st_rdev % 256)))
|
||||||
|
|
||||||
|
# Create the target
|
||||||
|
rootfs = "/proc/%s/root/" % self.init_pid
|
||||||
|
container_path = "%s/%s" % (rootfs, destpath)
|
||||||
|
|
||||||
|
if os.path.exists(container_path):
|
||||||
|
os.remove(container_path)
|
||||||
|
|
||||||
|
os.mknod(container_path, path_stat.st_mode, path_stat.st_rdev)
|
||||||
|
os.chmod(container_path, mode)
|
||||||
|
os.chown(container_path, 0, 0)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add_device_net(self, name, destname=None):
|
||||||
|
"""
|
||||||
|
Add network device to running container.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.running:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not destname:
|
||||||
|
destname = name
|
||||||
|
|
||||||
|
if not os.path.exists("/sys/class/net/%s/" % name):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return subprocess.call(['ip', 'link', 'set',
|
||||||
|
'dev', name,
|
||||||
|
'netns', str(self.init_pid),
|
||||||
|
'name', destname]) == 0
|
||||||
|
|
||||||
|
def append_config_item(self, key, value):
|
||||||
|
"""
|
||||||
|
Append 'value' to 'key', assuming 'key' is a list.
|
||||||
|
If 'key' isn't a list, 'value' will be set as the value of 'key'.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return _lxc.Container.set_config_item(self, key, value)
|
||||||
|
|
||||||
|
def attach(self, namespace="ALL", *cmd):
|
||||||
|
"""
|
||||||
|
Attach to a running container.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.running:
|
||||||
|
return False
|
||||||
|
|
||||||
|
attach = ["lxc-attach", "-n", self.name]
|
||||||
|
if namespace != "ALL":
|
||||||
|
attach += ["-s", namespace]
|
||||||
|
|
||||||
|
if cmd:
|
||||||
|
attach += ["--"] + list(cmd)
|
||||||
|
|
||||||
|
if subprocess.call(
|
||||||
|
attach,
|
||||||
|
universal_newlines=True) != 0:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def create(self, template, args={}):
|
||||||
|
"""
|
||||||
|
Create a new rootfs for the container.
|
||||||
|
|
||||||
|
"template" must be a valid template name.
|
||||||
|
|
||||||
|
"args" (optional) is a dictionary of parameters and values to pass
|
||||||
|
to the template.
|
||||||
|
"""
|
||||||
|
|
||||||
|
template_args = []
|
||||||
|
for item in args.items():
|
||||||
|
template_args.append("--%s" % item[0])
|
||||||
|
template_args.append("%s" % item[1])
|
||||||
|
|
||||||
|
return _lxc.Container.create(self, template, tuple(template_args))
|
||||||
|
|
||||||
|
def clone(self, container):
|
||||||
|
"""
|
||||||
|
Clone an existing container into a new one.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.defined:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if isinstance(container, Container):
|
||||||
|
source = container
|
||||||
|
else:
|
||||||
|
source = Container(container)
|
||||||
|
|
||||||
|
if not source.defined:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if subprocess.call(["lxc-clone", "-o", source.name, "-n", self.name],
|
||||||
|
universal_newlines=True) != 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.load_config()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def console(self, tty="1"):
|
||||||
|
"""
|
||||||
|
Access the console of a container.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.running:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if subprocess.call(["lxc-console", "-n", self.name, "-t", "%s" % tty],
|
||||||
|
universal_newlines=True) != 0:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_cgroup_item(self, key):
|
||||||
|
"""
|
||||||
|
Returns the value for a given cgroup entry.
|
||||||
|
A list is returned when multiple values are set.
|
||||||
|
"""
|
||||||
|
value = _lxc.Container.get_cgroup_item(self, key)
|
||||||
|
|
||||||
|
if value is False:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return value.rstrip("\n")
|
||||||
|
|
||||||
|
def get_config_item(self, key):
|
||||||
|
"""
|
||||||
|
Returns the value for a given config key.
|
||||||
|
A list is returned when multiple values are set.
|
||||||
|
"""
|
||||||
|
value = _lxc.Container.get_config_item(self, key)
|
||||||
|
|
||||||
|
if value is False:
|
||||||
|
return False
|
||||||
|
elif value.endswith("\n"):
|
||||||
|
return value.rstrip("\n").split("\n")
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
|
def get_ips(self, timeout=60, interface=None, protocol=None):
|
||||||
|
"""
|
||||||
|
Returns the list of IP addresses for the container.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.defined or not self.running:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.makedirs("/run/netns")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
path = tempfile.mktemp(dir="/run/netns")
|
||||||
|
|
||||||
|
os.symlink("/proc/%s/ns/net" % self.init_pid, path)
|
||||||
|
|
||||||
|
ips = []
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
while count < timeout:
|
||||||
|
if count != 0:
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
base_cmd = ["ip", "netns", "exec", path.split("/")[-1], "ip"]
|
||||||
|
|
||||||
|
# Get IPv6
|
||||||
|
if protocol in ("ipv6", None):
|
||||||
|
ip6_cmd = base_cmd + ["-6", "addr", "show", "scope", "global"]
|
||||||
|
if interface:
|
||||||
|
ip = subprocess.Popen(ip6_cmd + ["dev", interface],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
universal_newlines=True)
|
||||||
|
else:
|
||||||
|
ip = subprocess.Popen(ip6_cmd, stdout=subprocess.PIPE,
|
||||||
|
universal_newlines=True)
|
||||||
|
|
||||||
|
ip.wait()
|
||||||
|
for line in ip.stdout.read().split("\n"):
|
||||||
|
fields = line.split()
|
||||||
|
if len(fields) > 2 and fields[0] == "inet6":
|
||||||
|
ips.append(fields[1].split('/')[0])
|
||||||
|
|
||||||
|
# Get IPv4
|
||||||
|
if protocol in ("ipv4", None):
|
||||||
|
ip4_cmd = base_cmd + ["-4", "addr", "show", "scope", "global"]
|
||||||
|
if interface:
|
||||||
|
ip = subprocess.Popen(ip4_cmd + ["dev", interface],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
universal_newlines=True)
|
||||||
|
else:
|
||||||
|
ip = subprocess.Popen(ip4_cmd, stdout=subprocess.PIPE,
|
||||||
|
universal_newlines=True)
|
||||||
|
|
||||||
|
ip.wait()
|
||||||
|
for line in ip.stdout.read().split("\n"):
|
||||||
|
fields = line.split()
|
||||||
|
if len(fields) > 2 and fields[0] == "inet":
|
||||||
|
ips.append(fields[1].split('/')[0])
|
||||||
|
|
||||||
|
if ips:
|
||||||
|
break
|
||||||
|
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
os.remove(path)
|
||||||
|
return ips
|
||||||
|
|
||||||
|
def get_keys(self, key=None):
|
||||||
|
"""
|
||||||
|
Returns a list of valid sub-keys.
|
||||||
|
"""
|
||||||
|
if key:
|
||||||
|
value = _lxc.Container.get_keys(self, key)
|
||||||
|
else:
|
||||||
|
value = _lxc.Container.get_keys(self)
|
||||||
|
|
||||||
|
if value is False:
|
||||||
|
return False
|
||||||
|
elif value.endswith("\n"):
|
||||||
|
return value.rstrip("\n").split("\n")
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
|
def set_config_item(self, key, value):
|
||||||
|
"""
|
||||||
|
Set a config key to a provided value.
|
||||||
|
The value can be a list for the keys supporting multiple values.
|
||||||
|
"""
|
||||||
|
old_value = self.get_config_item(key)
|
||||||
|
|
||||||
|
# Check if it's a list
|
||||||
|
def set_key(key, value):
|
||||||
|
self.clear_config_item(key)
|
||||||
|
if isinstance(value, list):
|
||||||
|
for entry in value:
|
||||||
|
if not _lxc.Container.set_config_item(self, key, entry):
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
_lxc.Container.set_config_item(self, key, value)
|
||||||
|
|
||||||
|
set_key(key, value)
|
||||||
|
new_value = self.get_config_item(key)
|
||||||
|
|
||||||
|
if (isinstance(value, str) and isinstance(new_value, str) and
|
||||||
|
value == new_value):
|
||||||
|
return True
|
||||||
|
elif (isinstance(value, list) and isinstance(new_value, list) and
|
||||||
|
set(value) == set(new_value)):
|
||||||
|
return True
|
||||||
|
elif (isinstance(value, str) and isinstance(new_value, list) and
|
||||||
|
set([value]) == set(new_value)):
|
||||||
|
return True
|
||||||
|
elif old_value:
|
||||||
|
set_key(key, old_value)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
self.clear_config_item(key)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def wait(self, state, timeout=-1):
|
||||||
|
"""
|
||||||
|
Wait for the container to reach a given state or timeout.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if isinstance(state, str):
|
||||||
|
state = state.upper()
|
||||||
|
|
||||||
|
return _lxc.Container.wait(self, state, timeout)
|
||||||
|
|
||||||
|
|
||||||
|
def list_containers(as_object=False):
|
||||||
|
"""
|
||||||
|
List the containers on the system.
|
||||||
|
"""
|
||||||
|
containers = []
|
||||||
|
for entry in glob.glob("@LXCPATH@/*/config"):
|
||||||
|
if as_object:
|
||||||
|
containers.append(Container(entry.split("/")[-2]))
|
||||||
|
else:
|
||||||
|
containers.append(entry.split("/")[-2])
|
||||||
|
return containers
|
10
src/python-lxc/setup.py
Normal file
10
src/python-lxc/setup.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from distutils.core import setup, Extension
|
||||||
|
|
||||||
|
module = Extension('_lxc', sources=['lxc.c'], libraries=['lxc'])
|
||||||
|
|
||||||
|
setup(name='_lxc',
|
||||||
|
version='0.1',
|
||||||
|
description='LXC',
|
||||||
|
packages=['lxc'],
|
||||||
|
package_dir={'lxc': 'lxc'},
|
||||||
|
ext_modules=[module])
|
23
src/tests/Makefile.am
Normal file
23
src/tests/Makefile.am
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
if ENABLE_TESTS
|
||||||
|
|
||||||
|
LDADD = ../lxc/liblxc.so -lpthread
|
||||||
|
lxc_test_containertests_SOURCES = containertests.c
|
||||||
|
lxc_test_locktests_SOURCES = locktests.c
|
||||||
|
lxc_test_startone_SOURCES = startone.c
|
||||||
|
lxc_test_destroytest_SOURCES = destroytest.c
|
||||||
|
lxc_test_saveconfig_SOURCES = saveconfig.c
|
||||||
|
lxc_test_createtest_SOURCES = createtest.c
|
||||||
|
lxc_test_shutdowntest_SOURCES = shutdowntest.c
|
||||||
|
lxc_test_get_item_SOURCES = get_item.c
|
||||||
|
lxc_test_getkeys_SOURCES = getkeys.c
|
||||||
|
|
||||||
|
AM_CFLAGS=-I$(top_srcdir)/src \
|
||||||
|
-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
|
||||||
|
-DLXCPATH=\"$(LXCPATH)\" \
|
||||||
|
-DLXCINITDIR=\"$(LXCINITDIR)\"
|
||||||
|
|
||||||
|
bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \
|
||||||
|
lxc-test-destroytest lxc-test-saveconfig lxc-test-createtest \
|
||||||
|
lxc-test-shutdowntest lxc-test-get_item lxc-test-getkeys
|
||||||
|
|
||||||
|
endif
|
262
src/tests/containertests.c
Normal file
262
src/tests/containertests.c
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
/* liblxcapi
|
||||||
|
*
|
||||||
|
* Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
|
||||||
|
* Copyright © 2012 Canonical Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
#include "../lxc/lxccontainer.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <lxc/state.h>
|
||||||
|
|
||||||
|
#define MYNAME "lxctest1"
|
||||||
|
|
||||||
|
static int destroy_busybox(void)
|
||||||
|
{
|
||||||
|
int status, ret;
|
||||||
|
pid_t pid = fork();
|
||||||
|
|
||||||
|
if (pid < 0) {
|
||||||
|
perror("fork");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (pid == 0) {
|
||||||
|
ret = execlp("lxc-destroy", "lxc-destroy", "-f", "-n", MYNAME, NULL);
|
||||||
|
// Should not return
|
||||||
|
perror("execl");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
again:
|
||||||
|
ret = waitpid(pid, &status, 0);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == -EINTR)
|
||||||
|
goto again;
|
||||||
|
perror("waitpid");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ret != pid)
|
||||||
|
goto again;
|
||||||
|
if (!WIFEXITED(status)) { // did not exit normally
|
||||||
|
fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int create_busybox(void)
|
||||||
|
{
|
||||||
|
int status, ret;
|
||||||
|
pid_t pid = fork();
|
||||||
|
|
||||||
|
if (pid < 0) {
|
||||||
|
perror("fork");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (pid == 0) {
|
||||||
|
ret = execlp("lxc-create", "lxc-create", "-t", "busybox", "-f", "/etc/lxc/lxc.conf", "-n", MYNAME, NULL);
|
||||||
|
// Should not return
|
||||||
|
perror("execl");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
again:
|
||||||
|
ret = waitpid(pid, &status, 0);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == -EINTR)
|
||||||
|
goto again;
|
||||||
|
perror("waitpid");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ret != pid)
|
||||||
|
goto again;
|
||||||
|
if (!WIFEXITED(status)) { // did not exit normally
|
||||||
|
fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct lxc_container *c;
|
||||||
|
int ret = 0;
|
||||||
|
const char *s;
|
||||||
|
bool b;
|
||||||
|
char *str;
|
||||||
|
|
||||||
|
ret = 1;
|
||||||
|
/* test refcounting */
|
||||||
|
c = lxc_container_new(MYNAME);
|
||||||
|
if (!c) {
|
||||||
|
fprintf(stderr, "%d: error creating lxc_container %s\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!lxc_container_get(c)) {
|
||||||
|
fprintf(stderr, "%d: error getting refcount\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
/* peek in, inappropriately, make sure refcount is a we'd like */
|
||||||
|
if (c->numthreads != 2) {
|
||||||
|
fprintf(stderr, "%d: refcount is %d, not %d\n", __LINE__, c->numthreads, 2);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (strcmp(c->name, MYNAME) != 0) {
|
||||||
|
fprintf(stderr, "%d: container has wrong name (%s not %s)\n", __LINE__, c->name, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
str = c->config_file_name(c);
|
||||||
|
#define CONFIGFNAM LXCPATH "/" MYNAME "/config"
|
||||||
|
if (!str || strcmp(str, CONFIGFNAM)) {
|
||||||
|
fprintf(stderr, "%d: got wrong config file name (%s, not %s)\n", __LINE__, str, CONFIGFNAM);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
free(str);
|
||||||
|
free(c->configfile);
|
||||||
|
c->configfile = NULL;
|
||||||
|
str = c->config_file_name(c);
|
||||||
|
if (str) {
|
||||||
|
fprintf(stderr, "%d: config file name was not NULL as it should have been\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (lxc_container_put(c) != 0) {
|
||||||
|
fprintf(stderr, "%d: c was freed on non-final put\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (c->numthreads != 1) {
|
||||||
|
fprintf(stderr, "%d: refcount is %d, not %d\n", __LINE__, c->numthreads, 1);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (lxc_container_put(c) != 1) {
|
||||||
|
fprintf(stderr, "%d: c was not freed on final put\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test a real container */
|
||||||
|
c = lxc_container_new(MYNAME);
|
||||||
|
if (!c) {
|
||||||
|
fprintf(stderr, "%d: error creating lxc_container %s\n", __LINE__, MYNAME);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->lxc_conf != NULL) {
|
||||||
|
fprintf(stderr, "%d: lxc_conf is not NULL as it should be\n", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
b = c->is_defined(c);
|
||||||
|
if (b) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = c->state(c);
|
||||||
|
if (s && strcmp(s, "STOPPED") != 0) {
|
||||||
|
// liblxc says a container is STOPPED if it doesn't exist. That's because
|
||||||
|
// the container may be an application container - it's not wrong, just
|
||||||
|
// sometimes unintuitive.
|
||||||
|
fprintf(stderr, "%d: %s thinks it is in state %s\n", __LINE__, c->name, s);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a container
|
||||||
|
// the liblxc api does not support creation - it probably will eventually,
|
||||||
|
// but not yet.
|
||||||
|
// So we just call out to lxc-create. We'll create a busybox container.
|
||||||
|
ret = create_busybox();
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "%d: failed to create a busybox container\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
b = c->is_defined(c);
|
||||||
|
if (!b) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = c->state(c);
|
||||||
|
if (!s || strcmp(s, "STOPPED")) {
|
||||||
|
fprintf(stderr, "%d: %s is in state %s, not in STOPPED.\n", __LINE__, c->name, s ? s : "undefined");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
b = c->load_config(c, NULL);
|
||||||
|
if (!b) {
|
||||||
|
fprintf(stderr, "%d: %s failed to read its config\n", __LINE__, c->name);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// test wait states
|
||||||
|
int numstates = lxc_get_wait_states(NULL);
|
||||||
|
if (numstates != MAX_STATE) {
|
||||||
|
fprintf(stderr, "%d: lxc_get_wait_states gave %d not %d\n", __LINE__, numstates, MAX_STATE);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
const char **sstr = malloc(numstates * sizeof(const char *));
|
||||||
|
numstates = lxc_get_wait_states(sstr);
|
||||||
|
int i;
|
||||||
|
for (i=0; i<numstates; i++) {
|
||||||
|
fprintf(stderr, "got state %d %s\n", i, sstr[i]);
|
||||||
|
}
|
||||||
|
free(sstr);
|
||||||
|
|
||||||
|
printf("hit return to start container");
|
||||||
|
char mychar;
|
||||||
|
ret = scanf("%c", &mychar);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* non-daemonized is tested in 'startone' */
|
||||||
|
c->want_daemonize(c);
|
||||||
|
if (!c->startl(c, 0, NULL, NULL)) {
|
||||||
|
fprintf(stderr, "%d: %s failed to start daemonized\n", __LINE__, c->name);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->wait(c, "RUNNING", -1)) {
|
||||||
|
fprintf(stderr, "%d: failed waiting for state RUNNING\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(3);
|
||||||
|
s = c->state(c);
|
||||||
|
if (!s || strcmp(s, "RUNNING")) {
|
||||||
|
fprintf(stderr, "%d: %s is in state %s, not in RUNNING.\n", __LINE__, c->name, s ? s : "undefined");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("hit return to finish");
|
||||||
|
ret = scanf("%c", &mychar);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
|
||||||
|
fprintf(stderr, "all lxc_container tests passed for %s\n", c->name);
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (c) {
|
||||||
|
c->stop(c);
|
||||||
|
destroy_busybox();
|
||||||
|
}
|
||||||
|
lxc_container_put(c);
|
||||||
|
exit(ret);
|
||||||
|
}
|
92
src/tests/createtest.c
Normal file
92
src/tests/createtest.c
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/* liblxcapi
|
||||||
|
*
|
||||||
|
* Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
|
||||||
|
* Copyright © 2012 Canonical Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
#include "../lxc/lxccontainer.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#define MYNAME "lxctest1"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct lxc_container *c;
|
||||||
|
int ret = 1;
|
||||||
|
|
||||||
|
if ((c = lxc_container_new(MYNAME)) == NULL) {
|
||||||
|
fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->is_defined(c)) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->set_config_item(c, "lxc.network.type", "veth")) {
|
||||||
|
fprintf(stderr, "%d: failed to set network type\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
c->set_config_item(c, "lxc.network.link", "lxcbr0");
|
||||||
|
c->set_config_item(c, "lxc.network.flags", "up");
|
||||||
|
if (!c->createl(c, "ubuntu", "-r", "lucid", NULL)) {
|
||||||
|
fprintf(stderr, "%d: failed to create a lucid container\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->is_defined(c)) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->load_config(c, NULL);
|
||||||
|
c->want_daemonize(c);
|
||||||
|
if (!c->startl(c, 0, NULL)) {
|
||||||
|
fprintf(stderr, "%d: failed to start %s\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%d: %s started, you have 60 seconds to test a console\n", __LINE__, MYNAME);
|
||||||
|
sleep(60); // wait a minute to let user connect to console
|
||||||
|
|
||||||
|
if (!c->stop(c)) {
|
||||||
|
fprintf(stderr, "%d: failed to stop %s\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->destroy(c)) {
|
||||||
|
fprintf(stderr, "%d: error deleting %s\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->is_defined(c)) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "all lxc_container tests passed for %s\n", c->name);
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
lxc_container_put(c);
|
||||||
|
exit(ret);
|
||||||
|
}
|
104
src/tests/destroytest.c
Normal file
104
src/tests/destroytest.c
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/* liblxcapi
|
||||||
|
*
|
||||||
|
* Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
|
||||||
|
* Copyright © 2012 Canonical Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
#include "../lxc/lxccontainer.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#define MYNAME "lxctest1"
|
||||||
|
|
||||||
|
static int create_ubuntu(void)
|
||||||
|
{
|
||||||
|
int status, ret;
|
||||||
|
pid_t pid = fork();
|
||||||
|
|
||||||
|
if (pid < 0) {
|
||||||
|
perror("fork");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (pid == 0) {
|
||||||
|
ret = execlp("lxc-create", "lxc-create", "-t", "ubuntu", "-f", "/etc/lxc/lxc.conf", "-n", MYNAME, NULL);
|
||||||
|
// Should not return
|
||||||
|
perror("execl");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
again:
|
||||||
|
ret = waitpid(pid, &status, 0);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == -EINTR)
|
||||||
|
goto again;
|
||||||
|
perror("waitpid");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ret != pid)
|
||||||
|
goto again;
|
||||||
|
if (!WIFEXITED(status)) { // did not exit normally
|
||||||
|
fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct lxc_container *c;
|
||||||
|
int ret = 1;
|
||||||
|
|
||||||
|
if ((c = lxc_container_new(MYNAME)) == NULL) {
|
||||||
|
fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->is_defined(c)) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (create_ubuntu()) {
|
||||||
|
fprintf(stderr, "%d: failed to create a ubuntu container\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->is_defined(c)) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->destroy(c)) {
|
||||||
|
fprintf(stderr, "%d: error deleting %s\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->is_defined(c)) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "all lxc_container tests passed for %s\n", c->name);
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
lxc_container_put(c);
|
||||||
|
exit(ret);
|
||||||
|
}
|
308
src/tests/get_item.c
Normal file
308
src/tests/get_item.c
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
/* liblxcapi
|
||||||
|
*
|
||||||
|
* Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
|
||||||
|
* Copyright © 2012 Canonical Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
#include "../lxc/lxccontainer.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <lxc/state.h>
|
||||||
|
|
||||||
|
#define MYNAME "lxctest1"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct lxc_container *c;
|
||||||
|
int ret;
|
||||||
|
char v1[2], v2[256], v3[2048];
|
||||||
|
|
||||||
|
if ((c = lxc_container_new("testxyz")) == NULL) {
|
||||||
|
fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->set_config_item(c, "lxc.hook.pre-start", "hi there")) {
|
||||||
|
fprintf(stderr, "%d: failed to set hook.pre-start\n", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = c->get_config_item(c, "lxc.hook.pre-start", v2, 255);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%d: get_config_item(lxc.hook.pre-start) returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "lxc.hook.pre-start returned %d %s\n", ret, v2);
|
||||||
|
|
||||||
|
ret = c->get_config_item(c, "lxc.network", v2, 255);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%d: get_config_item returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%d: get_config_item(lxc.network) returned %d %s\n", __LINE__, ret, v2);
|
||||||
|
if (!c->set_config_item(c, "lxc.tty", "4")) {
|
||||||
|
fprintf(stderr, "%d: failed to set tty\n", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = c->get_config_item(c, "lxc.tty", v2, 255);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%d: get_config_item(lxc.tty) returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "lxc.tty returned %d %s\n", ret, v2);
|
||||||
|
|
||||||
|
if (!c->set_config_item(c, "lxc.arch", "x86")) {
|
||||||
|
fprintf(stderr, "%d: failed to set arch\n", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = c->get_config_item(c, "lxc.arch", v2, 255);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%d: get_config_item(lxc.arch) returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
printf("lxc.arch returned %d %s\n", ret, v2);
|
||||||
|
|
||||||
|
#define HNAME "hostname1"
|
||||||
|
// demonstrate proper usage:
|
||||||
|
char *alloced;
|
||||||
|
if (!c->set_config_item(c, "lxc.utsname", HNAME)) {
|
||||||
|
fprintf(stderr, "%d: failed to set utsname\n", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len;
|
||||||
|
len = c->get_config_item(c, "lxc.utsname", NULL, 0); // query the size of the string
|
||||||
|
if (len < 0) {
|
||||||
|
fprintf(stderr, "%d: get_config_item(lxc.utsname) returned %d\n", __LINE__, len);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
printf("lxc.utsname returned %d\n", len);
|
||||||
|
|
||||||
|
// allocate the length of string + 1 for trailing \0
|
||||||
|
alloced = malloc(len+1);
|
||||||
|
if (!alloced) {
|
||||||
|
fprintf(stderr, "%d: failed to allocate %d bytes for utsname\n", __LINE__, len);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
// now pass in the malloc'd array, and pass in length of string + 1: again
|
||||||
|
// because we need room for the trailing \0
|
||||||
|
ret = c->get_config_item(c, "lxc.utsname", alloced, len+1);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%d: get_config_item(lxc.utsname) returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (strcmp(alloced, HNAME) != 0 || ret != len) {
|
||||||
|
fprintf(stderr, "lxc.utsname returned wrong value: %d %s not %d %s\n", ret, alloced, len, HNAME);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
printf("lxc.utsname returned %d %s\n", len, alloced);
|
||||||
|
free(alloced);
|
||||||
|
|
||||||
|
if (!c->set_config_item(c, "lxc.mount.entry", "hi there")) {
|
||||||
|
fprintf(stderr, "%d: failed to set mount.entry\n", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = c->get_config_item(c, "lxc.mount.entry", v2, 255);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%d: get_config_item(lxc.mount.entry) returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
printf("lxc.mount.entry returned %d %s\n", ret, v2);
|
||||||
|
|
||||||
|
if (!c->set_config_item(c, "lxc.aa_profile", "unconfined")) {
|
||||||
|
fprintf(stderr, "%d: failed to set aa_profile\n", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = c->get_config_item(c, "lxc.aa_profile", v2, 255);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%d: get_config_item(lxc.aa_profile) returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
printf("lxc.aa_profile returned %d %s\n", ret, v2);
|
||||||
|
|
||||||
|
lxc_container_put(c);
|
||||||
|
|
||||||
|
// new test with real container
|
||||||
|
if ((c = lxc_container_new(MYNAME)) == NULL) {
|
||||||
|
fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
c->destroy(c);
|
||||||
|
lxc_container_put(c);
|
||||||
|
|
||||||
|
if ((c = lxc_container_new(MYNAME)) == NULL) {
|
||||||
|
fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!c->createl(c, "ubuntu", "-r", "lucid", NULL)) {
|
||||||
|
fprintf(stderr, "%d: failed to create a lucid container\n", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
lxc_container_put(c);
|
||||||
|
|
||||||
|
/* XXX TODO load_config needs to clear out any old config first */
|
||||||
|
if ((c = lxc_container_new(MYNAME)) == NULL) {
|
||||||
|
fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = c->get_config_item(c, "lxc.cap.drop", NULL, 300);
|
||||||
|
if (ret < 5 || ret > 255) {
|
||||||
|
fprintf(stderr, "%d: get_config_item(lxc.cap.drop) with NULL returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = c->get_config_item(c, "lxc.cap.drop", v1, 1);
|
||||||
|
if (ret < 5 || ret > 255) {
|
||||||
|
fprintf(stderr, "%d: get_config_item(lxc.cap.drop) returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = c->get_config_item(c, "lxc.cap.drop", v2, 255);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%d: get_config_item(lxc.cap.drop) returned %d %s\n", __LINE__, ret, v2);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
printf("%d: get_config_item(lxc.cap.drop) returned %d %s\n", __LINE__, ret, v2);
|
||||||
|
ret = c->get_config_item(c, "lxc.network", v2, 255);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%d: get_config_item returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
printf("%d: get_config_item(lxc.network) returned %d %s\n", __LINE__, ret, v2);
|
||||||
|
|
||||||
|
if (!c->set_config_item(c, "lxc.network.ipv4", "10.2.3.4")) {
|
||||||
|
fprintf(stderr, "%d: failed to set ipv4\n", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = c->get_config_item(c, "lxc.network.0.ipv4", v2, 255);
|
||||||
|
if (ret <= 0) {
|
||||||
|
fprintf(stderr, "%d: lxc.network.0.ipv4 returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!c->clear_config_item(c, "lxc.network.0.ipv4")) {
|
||||||
|
fprintf(stderr, "%d: failed clearing all ipv4 entries\n", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = c->get_config_item(c, "lxc.network.0.ipv4", v2, 255);
|
||||||
|
if (ret != 0) {
|
||||||
|
fprintf(stderr, "%d: after clearing ipv4 entries get_item(lxc.network.0.ipv4 returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = c->get_config_item(c, "lxc.network.0.link", v2, 255);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%d: get_config_item returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
printf("%d: get_config_item (link) returned %d %s\n", __LINE__, ret, v2);
|
||||||
|
ret = c->get_config_item(c, "lxc.network.0.name", v2, 255);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%d: get_config_item returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
printf("%d: get_config_item (name) returned %d %s\n", __LINE__, ret, v2);
|
||||||
|
|
||||||
|
if (!c->clear_config_item(c, "lxc.network")) {
|
||||||
|
fprintf(stderr, "%d: clear_config_item failed\n", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = c->get_config_item(c, "lxc.network", v2, 255);
|
||||||
|
if (ret != 0) {
|
||||||
|
fprintf(stderr, "%d: network was not actually cleared (get_network returned %d)\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = c->get_config_item(c, "lxc.cgroup", v3, 2047);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%d: get_config_item(cgroup.devices) returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
printf("%d: get_config_item (cgroup.devices) returned %d %s\n", __LINE__, ret, v3);
|
||||||
|
|
||||||
|
ret = c->get_config_item(c, "lxc.cgroup.devices.allow", v3, 2047);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%d: get_config_item(cgroup.devices.devices.allow) returned %d\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
printf("%d: get_config_item (cgroup.devices.devices.allow) returned %d %s\n", __LINE__, ret, v3);
|
||||||
|
|
||||||
|
if (!c->clear_config_item(c, "lxc.cgroup")) {
|
||||||
|
fprintf(stderr, "%d: failed clearing lxc.cgroup", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!c->clear_config_item(c, "lxc.cap.drop")) {
|
||||||
|
fprintf(stderr, "%d: failed clearing lxc.cap.drop", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!c->clear_config_item(c, "lxc.mount.entries")) {
|
||||||
|
fprintf(stderr, "%d: failed clearing lxc.mount.entries", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!c->clear_config_item(c, "lxc.hook")) {
|
||||||
|
fprintf(stderr, "%d: failed clearing lxc.hook", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
c->destroy(c);
|
||||||
|
printf("All get_item tests passed\n");
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
lxc_container_put(c);
|
||||||
|
exit(ret);
|
||||||
|
};
|
71
src/tests/getkeys.c
Normal file
71
src/tests/getkeys.c
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/* liblxcapi
|
||||||
|
*
|
||||||
|
* Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
|
||||||
|
* Copyright © 2012 Canonical Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
#include "../lxc/lxccontainer.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <lxc/state.h>
|
||||||
|
|
||||||
|
#define MYNAME "lxctest1"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct lxc_container *c;
|
||||||
|
int len, ret;
|
||||||
|
char v3[2048];
|
||||||
|
|
||||||
|
if ((c = lxc_container_new(MYNAME)) == NULL) {
|
||||||
|
fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->set_config_item(c, "lxc.network.type", "veth");
|
||||||
|
|
||||||
|
len = c->get_keys(c, NULL, NULL, 0);
|
||||||
|
if (len < 0) {
|
||||||
|
fprintf(stderr, "%d: failed to get length of all keys (%d)\n", __LINE__, len);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = c->get_keys(c, NULL, v3, len+1);
|
||||||
|
if (ret != len) {
|
||||||
|
fprintf(stderr, "%d: failed to get keys (%d)\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
printf("get_keys returned %d\n%s", ret, v3);
|
||||||
|
|
||||||
|
ret = c->get_keys(c, "lxc.network.0", v3, 2000);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "%d: failed to get nic 0 keys(%d)\n", __LINE__, ret);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
printf("get_keys for nic 1 returned %d\n%s", ret, v3);
|
||||||
|
|
||||||
|
out:
|
||||||
|
lxc_container_put(c);
|
||||||
|
exit(ret);
|
||||||
|
}
|
239
src/tests/locktests.c
Normal file
239
src/tests/locktests.c
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
/* liblxcapi
|
||||||
|
*
|
||||||
|
* Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
|
||||||
|
* Copyright © 2012 Canonical Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
#include "../lxc/lxclock.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define mycontainername "lxctest.sem"
|
||||||
|
#define TIMEOUT_SECS 3
|
||||||
|
|
||||||
|
int timedout;
|
||||||
|
int pid_to_kill;
|
||||||
|
|
||||||
|
void timeouthandler(int sig)
|
||||||
|
{
|
||||||
|
// timeout received
|
||||||
|
timedout = 1;
|
||||||
|
kill(pid_to_kill, SIGTERM);
|
||||||
|
}
|
||||||
|
|
||||||
|
void starttimer(int secs)
|
||||||
|
{
|
||||||
|
timedout = 0;
|
||||||
|
signal(SIGALRM, timeouthandler);
|
||||||
|
alarm(secs);
|
||||||
|
}
|
||||||
|
void stoptimer(void)
|
||||||
|
{
|
||||||
|
alarm(0);
|
||||||
|
signal(SIGALRM, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_one_lock(sem_t *lock)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
starttimer(TIMEOUT_SECS);
|
||||||
|
ret = lxclock(lock, TIMEOUT_SECS*2);
|
||||||
|
stoptimer();
|
||||||
|
if (ret == 0) {
|
||||||
|
lxcunlock(lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (timedout)
|
||||||
|
fprintf(stderr, "%d: timed out waiting for lock\n", __LINE__);
|
||||||
|
else
|
||||||
|
fprintf(stderr, "%d: failed to get single lock\n", __LINE__);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get one lock. Fork a second task to try to get a second lock,
|
||||||
|
* with infinite timeout. If our alarm hits, kill the second
|
||||||
|
* task. If second task does not
|
||||||
|
*/
|
||||||
|
int test_two_locks(sem_t *lock)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = lxclock(lock, 1);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "%d: Error getting first lock\n", __LINE__);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_to_kill = fork();
|
||||||
|
if (pid_to_kill < 0) {
|
||||||
|
fprintf(stderr, "%d: Failed to fork\n", __LINE__);
|
||||||
|
lxcunlock(lock);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid_to_kill == 0) { // child
|
||||||
|
ret = lxclock(lock, TIMEOUT_SECS*2);
|
||||||
|
if (ret == 0) {
|
||||||
|
lxcunlock(lock);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%d: child, was not able to get lock\n", __LINE__);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
starttimer(TIMEOUT_SECS);
|
||||||
|
waitpid(pid_to_kill, &status, 0);
|
||||||
|
stoptimer();
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
// child exited normally - timeout didn't kill it
|
||||||
|
if (WEXITSTATUS(status) == 0)
|
||||||
|
fprintf(stderr, "%d: child was able to get the lock\n", __LINE__);
|
||||||
|
else
|
||||||
|
fprintf(stderr, "%d: child timed out too early\n", __LINE__);
|
||||||
|
lxcunlock(lock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
lxcunlock(lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get one lock. try to get second lock, but asking for timeout. If
|
||||||
|
* should return failure. If our own alarm, set at twice the lock
|
||||||
|
* request's timeout, hits, then lxclock() did not properly time out.
|
||||||
|
*/
|
||||||
|
int test_with_timeout(sem_t *lock)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ret = lxclock(lock, 0);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "%d: Error getting first lock\n", __LINE__);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
pid_to_kill = fork();
|
||||||
|
if (pid_to_kill < 0) {
|
||||||
|
fprintf(stderr, "%d: Error on fork\n", __LINE__);
|
||||||
|
lxcunlock(lock);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if (pid_to_kill == 0) {
|
||||||
|
ret = lxclock(lock, TIMEOUT_SECS);
|
||||||
|
if (ret == 0) {
|
||||||
|
lxcunlock(lock);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
starttimer(TIMEOUT_SECS * 2);
|
||||||
|
waitpid(pid_to_kill, &status, 0);
|
||||||
|
stoptimer();
|
||||||
|
if (!WIFEXITED(status)) {
|
||||||
|
fprintf(stderr, "%d: lxclock did not honor its timeout\n", __LINE__);
|
||||||
|
lxcunlock(lock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (WEXITSTATUS(status) == 0) {
|
||||||
|
fprintf(stderr, "%d: child was able to get lock, should have failed with timeout\n", __LINE__);
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
lxcunlock(lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int ret, sval, r;
|
||||||
|
sem_t *lock;
|
||||||
|
|
||||||
|
lock = lxc_newlock(NULL);
|
||||||
|
if (!lock) {
|
||||||
|
fprintf(stderr, "%d: failed to get unnamed lock\n", __LINE__);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
ret = lxclock(lock, 0);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "%d: failed to take unnamed lock (%d)\n", __LINE__, ret);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = lxcunlock(lock);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "%d: failed to put unnamed lock (%d)\n", __LINE__, ret);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
sem_destroy(lock);
|
||||||
|
free(lock);
|
||||||
|
|
||||||
|
lock = lxc_newlock(mycontainername);
|
||||||
|
if (!lock) {
|
||||||
|
fprintf(stderr, "%d: failed to get lock\n", __LINE__);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
r = sem_getvalue(lock, &sval);
|
||||||
|
if (!r) {
|
||||||
|
fprintf(stderr, "%d: sem value at start is %d\n", __LINE__, sval);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "%d: failed to get initial value\n", __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = test_one_lock(lock);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "%d: test failed\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
r = sem_getvalue(lock, &sval);
|
||||||
|
if (!r) {
|
||||||
|
fprintf(stderr, "%d: sem value is %d\n", __LINE__, sval);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "%d: failed to get sem value\n", __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = test_two_locks(lock);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "%d: test failed\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
r = sem_getvalue(lock, &sval);
|
||||||
|
if (!r) {
|
||||||
|
fprintf(stderr, "%d: sem value is %d\n", __LINE__, sval);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "%d: failed to get value\n", __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = test_with_timeout(lock);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "%d: test failed\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
r = sem_getvalue(lock, &sval);
|
||||||
|
if (!r) {
|
||||||
|
fprintf(stderr, "%d: sem value is %d\n", __LINE__, sval);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "%d: failed to get value\n", __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "all tests passed\n");
|
||||||
|
|
||||||
|
out:
|
||||||
|
exit(ret);
|
||||||
|
}
|
106
src/tests/saveconfig.c
Normal file
106
src/tests/saveconfig.c
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/* liblxcapi
|
||||||
|
*
|
||||||
|
* Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
|
||||||
|
* Copyright © 2012 Canonical Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
#include "../lxc/lxccontainer.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#define MYNAME "lxctest1"
|
||||||
|
|
||||||
|
static int create_ubuntu(void)
|
||||||
|
{
|
||||||
|
int status, ret;
|
||||||
|
pid_t pid = fork();
|
||||||
|
|
||||||
|
if (pid < 0) {
|
||||||
|
perror("fork");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (pid == 0) {
|
||||||
|
ret = execlp("lxc-create", "lxc-create", "-t", "ubuntu", "-f", "/etc/lxc/lxc.conf", "-n", MYNAME, NULL);
|
||||||
|
// Should not return
|
||||||
|
perror("execl");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
again:
|
||||||
|
ret = waitpid(pid, &status, 0);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == -EINTR)
|
||||||
|
goto again;
|
||||||
|
perror("waitpid");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ret != pid)
|
||||||
|
goto again;
|
||||||
|
if (!WIFEXITED(status)) { // did not exit normally
|
||||||
|
fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct lxc_container *c;
|
||||||
|
int ret = 1;
|
||||||
|
|
||||||
|
if ((c = lxc_container_new(MYNAME)) == NULL) {
|
||||||
|
fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->is_defined(c)) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (create_ubuntu()) {
|
||||||
|
fprintf(stderr, "%d: failed to create a ubuntu container\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->is_defined(c)) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->load_config(c, NULL);
|
||||||
|
unlink("/tmp/lxctest1");
|
||||||
|
if (!c->save_config(c, "/tmp/lxctest1")) {
|
||||||
|
fprintf(stderr, "%d: failed writing config file /tmp/lxctest1\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
rename(LXCPATH "/" MYNAME "/config", LXCPATH "/" MYNAME "/config.bak");
|
||||||
|
if (!c->save_config(c, NULL)) {
|
||||||
|
fprintf(stderr, "%d: failed writing config file\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "all lxc_container tests passed for %s\n", c->name);
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
lxc_container_put(c);
|
||||||
|
exit(ret);
|
||||||
|
}
|
93
src/tests/shutdowntest.c
Normal file
93
src/tests/shutdowntest.c
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
|
||||||
|
/* liblxcapi
|
||||||
|
*
|
||||||
|
* Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
|
||||||
|
* Copyright © 2012 Canonical Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
#include "../lxc/lxccontainer.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#define MYNAME "lxctest1"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct lxc_container *c;
|
||||||
|
int ret = 1;
|
||||||
|
|
||||||
|
if ((c = lxc_container_new(MYNAME)) == NULL) {
|
||||||
|
fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->is_defined(c)) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->set_config_item(c, "lxc.network.type", "veth")) {
|
||||||
|
fprintf(stderr, "%d: failed to set network type\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
c->set_config_item(c, "lxc.network.link", "lxcbr0");
|
||||||
|
c->set_config_item(c, "lxc.network.flags", "up");
|
||||||
|
if (!c->createl(c, "ubuntu", "-r", "lucid", NULL)) {
|
||||||
|
fprintf(stderr, "%d: failed to create a lucid container\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->is_defined(c)) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->load_config(c, NULL);
|
||||||
|
c->want_daemonize(c);
|
||||||
|
if (!c->startl(c, 0, NULL)) {
|
||||||
|
fprintf(stderr, "%d: failed to start %s\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%d: %s started, you have 60 seconds to test a console\n", __LINE__, MYNAME);
|
||||||
|
sleep(60); // wait a minute to let user connect to console
|
||||||
|
|
||||||
|
if (!c->shutdown(c, 60)) {
|
||||||
|
fprintf(stderr, "%d: failed to shut down %s\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->destroy(c)) {
|
||||||
|
fprintf(stderr, "%d: error deleting %s\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->is_defined(c)) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "all lxc_container tests passed for %s\n", c->name);
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
lxc_container_put(c);
|
||||||
|
exit(ret);
|
||||||
|
}
|
264
src/tests/startone.c
Normal file
264
src/tests/startone.c
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
/* liblxcapi
|
||||||
|
*
|
||||||
|
* Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
|
||||||
|
* Copyright © 2012 Canonical Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
#include "../lxc/lxccontainer.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#define MYNAME "lxctest1"
|
||||||
|
|
||||||
|
static int destroy_ubuntu(void)
|
||||||
|
{
|
||||||
|
int status, ret;
|
||||||
|
pid_t pid = fork();
|
||||||
|
|
||||||
|
if (pid < 0) {
|
||||||
|
perror("fork");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (pid == 0) {
|
||||||
|
ret = execlp("lxc-destroy", "lxc-destroy", "-f", "-n", MYNAME, NULL);
|
||||||
|
// Should not return
|
||||||
|
perror("execl");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
again:
|
||||||
|
ret = waitpid(pid, &status, 0);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == -EINTR)
|
||||||
|
goto again;
|
||||||
|
perror("waitpid");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ret != pid)
|
||||||
|
goto again;
|
||||||
|
if (!WIFEXITED(status)) { // did not exit normally
|
||||||
|
fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int create_ubuntu(void)
|
||||||
|
{
|
||||||
|
int status, ret;
|
||||||
|
pid_t pid = fork();
|
||||||
|
|
||||||
|
if (pid < 0) {
|
||||||
|
perror("fork");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (pid == 0) {
|
||||||
|
ret = execlp("lxc-create", "lxc-create", "-t", "ubuntu", "-f", "/etc/lxc/lxc.conf", "-n", MYNAME, NULL);
|
||||||
|
// Should not return
|
||||||
|
perror("execl");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
again:
|
||||||
|
ret = waitpid(pid, &status, 0);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == -EINTR)
|
||||||
|
goto again;
|
||||||
|
perror("waitpid");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ret != pid)
|
||||||
|
goto again;
|
||||||
|
if (!WIFEXITED(status)) { // did not exit normally
|
||||||
|
fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct lxc_container *c;
|
||||||
|
int ret = 0;
|
||||||
|
const char *s;
|
||||||
|
bool b;
|
||||||
|
char buf[201];
|
||||||
|
int len;
|
||||||
|
|
||||||
|
ret = 1;
|
||||||
|
/* test a real container */
|
||||||
|
c = lxc_container_new(MYNAME);
|
||||||
|
if (!c) {
|
||||||
|
fprintf(stderr, "%d: error creating lxc_container %s\n", __LINE__, MYNAME);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->is_defined(c)) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = create_ubuntu();
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "%d: failed to create a ubuntu container\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
b = c->is_defined(c);
|
||||||
|
if (!b) {
|
||||||
|
fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = c->get_cgroup_item(c, "cpuset.cpus", buf, 200);
|
||||||
|
if (len >= 0) {
|
||||||
|
fprintf(stderr, "%d: %s not running but had cgroup settings\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(buf, "0");
|
||||||
|
b = c->set_cgroup_item(c, "cpuset.cpus", buf);
|
||||||
|
if (b) {
|
||||||
|
fprintf(stderr, "%d: %s not running but coudl set cgroup settings\n", __LINE__, MYNAME);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = c->state(c);
|
||||||
|
if (!s || strcmp(s, "STOPPED")) {
|
||||||
|
fprintf(stderr, "%d: %s is in state %s, not in STOPPED.\n", __LINE__, c->name, s ? s : "undefined");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
b = c->load_config(c, NULL);
|
||||||
|
if (!b) {
|
||||||
|
fprintf(stderr, "%d: %s failed to read its config\n", __LINE__, c->name);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->set_config_item(c, "lxc.utsname", "bobo")) {
|
||||||
|
fprintf(stderr, "%d: failed setting lxc.utsname\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("hit return to start container");
|
||||||
|
char mychar;
|
||||||
|
ret = scanf("%c", &mychar);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!lxc_container_get(c)) {
|
||||||
|
fprintf(stderr, "%d: failed to get extra ref to container\n", __LINE__);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
fprintf(stderr, "%d: fork failed\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (pid == 0) {
|
||||||
|
b = c->startl(c, 0, NULL);
|
||||||
|
if (!b)
|
||||||
|
fprintf(stderr, "%d: %s failed to start\n", __LINE__, c->name);
|
||||||
|
lxc_container_put(c);
|
||||||
|
exit(!b);
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(3);
|
||||||
|
s = c->state(c);
|
||||||
|
if (!s || strcmp(s, "RUNNING")) {
|
||||||
|
fprintf(stderr, "%d: %s is in state %s, not in RUNNING.\n", __LINE__, c->name, s ? s : "undefined");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = c->get_cgroup_item(c, "cpuset.cpus", buf, 0);
|
||||||
|
if (len <= 0) {
|
||||||
|
fprintf(stderr, "%d: not able to get length of cpuset.cpus (ret %d)\n", __LINE__, len);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = c->get_cgroup_item(c, "cpuset.cpus", buf, 200);
|
||||||
|
if (len <= 0 || strcmp(buf, "0\n")) {
|
||||||
|
fprintf(stderr, "%d: not able to get cpuset.cpus (len %d buf %s)\n", __LINE__, len, buf);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(buf, "FROZEN");
|
||||||
|
b = c->set_cgroup_item(c, "freezer.state", buf);
|
||||||
|
if (!b) {
|
||||||
|
fprintf(stderr, "%d: not able to set freezer.state.\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(buf, "XXX");
|
||||||
|
len = c->get_cgroup_item(c, "freezer.state", buf, 200);
|
||||||
|
if (len <= 0 || (strcmp(buf, "FREEZING\n") && strcmp(buf, "FROZEN\n"))) {
|
||||||
|
fprintf(stderr, "%d: not able to get freezer.state (len %d buf %s)\n", __LINE__, len, buf);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->set_cgroup_item(c, "freezer.state", "THAWED");
|
||||||
|
|
||||||
|
printf("hit return to finish");
|
||||||
|
ret = scanf("%c", &mychar);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
c->stop(c);
|
||||||
|
|
||||||
|
/* feh - multilib has moved the lxc-init crap */
|
||||||
|
goto ok;
|
||||||
|
|
||||||
|
ret = system("mkdir -p " LXCPATH "/lxctest1/rootfs//usr/local/libexec/lxc");
|
||||||
|
if (!ret)
|
||||||
|
ret = system("mkdir -p " LXCPATH "/lxctest1/rootfs/usr/lib/lxc/");
|
||||||
|
if (!ret)
|
||||||
|
ret = system("cp src/lxc/lxc-init " LXCPATH "/lxctest1/rootfs//usr/local/libexec/lxc");
|
||||||
|
if (!ret)
|
||||||
|
ret = system("cp src/lxc/liblxc.so " LXCPATH "/lxctest1/rootfs/usr/lib/lxc");
|
||||||
|
if (!ret)
|
||||||
|
ret = system("cp src/lxc/liblxc.so " LXCPATH "/lxctest1/rootfs/usr/lib/lxc/liblxc.so.0");
|
||||||
|
if (!ret)
|
||||||
|
ret = system("cp src/lxc/liblxc.so " LXCPATH "/lxctest1/rootfs/usr/lib");
|
||||||
|
if (!ret)
|
||||||
|
ret = system("mkdir -p " LXCPATH "/lxctest1/rootfs/dev/shm");
|
||||||
|
if (!ret)
|
||||||
|
ret = system("chroot " LXCPATH "/lxctest1/rootfs apt-get install --no-install-recommends lxc");
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "%d: failed to installing lxc-init in container\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
// next write out the config file; does it match?
|
||||||
|
if (!c->startl(c, 1, "/bin/hostname", NULL)) {
|
||||||
|
fprintf(stderr, "%d: failed to lxc-execute /bin/hostname\n", __LINE__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
// auto-check result? ('bobo' is printed on stdout)
|
||||||
|
|
||||||
|
ok:
|
||||||
|
fprintf(stderr, "all lxc_container tests passed for %s\n", c->name);
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (c) {
|
||||||
|
c->stop(c);
|
||||||
|
destroy_ubuntu();
|
||||||
|
}
|
||||||
|
lxc_container_put(c);
|
||||||
|
exit(ret);
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user