mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-06 01:45:47 +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.pc
|
||||
|
||||
templates/lxc-debian
|
||||
templates/lxc-lucid
|
||||
templates/lxc-maverick
|
||||
templates/lxc-natty
|
||||
templates/lxc-oneiric
|
||||
templates/lxc-fedora
|
||||
templates/lxc-altlinux
|
||||
templates/lxc-sshd
|
||||
templates/lxc-busybox
|
||||
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-cgroup
|
||||
src/lxc/lxc-checkconfig
|
||||
src/lxc/lxc-checkpoint
|
||||
src/lxc/lxc-cinit
|
||||
src/lxc/lxc-cmd
|
||||
src/lxc/lxc-clone
|
||||
src/lxc/lxc-console
|
||||
src/lxc/lxc-create
|
||||
src/lxc/lxc-destroy
|
||||
src/lxc/lxc-enter
|
||||
src/lxc/lxc-exec
|
||||
src/lxc/lxc-execute
|
||||
src/lxc/lxc-freeze
|
||||
src/lxc/lxc-info
|
||||
src/lxc/lxc-init
|
||||
src/lxc/lxc-kill
|
||||
src/lxc/lxc-ls
|
||||
src/lxc/lxc-monitor
|
||||
src/lxc/lxc-netstat
|
||||
src/lxc/lxc-setcap
|
||||
src/lxc/lxc-ps
|
||||
src/lxc/lxc-restart
|
||||
src/lxc/lxc-setcap
|
||||
src/lxc/lxc-setuid
|
||||
src/lxc/lxc-shutdown
|
||||
src/lxc/lxc-start
|
||||
src/lxc/lxc-start-ephemeral
|
||||
src/lxc/lxc-stop
|
||||
src/lxc/lxc-unfreeze
|
||||
src/lxc/lxc-unshare
|
||||
src/lxc/lxc-version
|
||||
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/config.guess
|
||||
|
@ -83,5 +83,4 @@ then you just add a line saying
|
||||
|
||||
Signed-off-by: Random J Developer <random@developer.org>
|
||||
|
||||
using your real name (sorry, no pseudonyms or anonymous
|
||||
contributions.)
|
||||
using your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||
|
2
COPYING
2
COPYING
@ -500,5 +500,3 @@ necessary. Here is a sample; alter the names:
|
||||
Ty Coon, President of Vice
|
||||
|
||||
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
|
||||
==================
|
||||
|
||||
Briefly, the shell commands `./configure; make; make install' should
|
||||
configure, build, and install this package. The following
|
||||
Briefly, the shell commands `./autogen.sh; ./configure; make; make install'
|
||||
should configure, build, and install this package. The following
|
||||
more-detailed instructions are generic; see the `README' file for
|
||||
instructions specific to this package.
|
||||
|
||||
|
@ -2,13 +2,17 @@
|
||||
|
||||
ACLOCAL_AMFLAGS = -I config
|
||||
|
||||
SUBDIRS = src templates doc
|
||||
SUBDIRS = config src templates doc
|
||||
DIST_SUBDIRS = config src templates doc
|
||||
EXTRA_DIST = autogen.sh lxc.spec CONTRIBUTING MAINTAINERS ChangeLog
|
||||
|
||||
pcdatadir = $(libdir)/pkgconfig
|
||||
pcdata_DATA = lxc.pc
|
||||
|
||||
install-data-local:
|
||||
$(MKDIR_P) $(DESTDIR)$(LXCPATH)
|
||||
$(MKDIR_P) $(DESTDIR)$(localstatedir)/cache/lxc
|
||||
|
||||
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
|
||||
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
|
||||
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.
|
||||
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
|
||||
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.
|
||||
|
||||
Getting help:
|
||||
@ -48,7 +53,33 @@ Portability:
|
||||
|
||||
lxc is developed and tested on Linux since kernel mainline version 2.6.27
|
||||
(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
|
||||
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
|
||||
|
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_AUX_DIR([config])
|
||||
AM_CONFIG_HEADER([src/config.h])
|
||||
AM_INIT_AUTOMAKE([-Wno-portability])
|
||||
AM_INIT_AUTOMAKE([-Wall -Werror -Wno-portability])
|
||||
AC_CANONICAL_HOST
|
||||
AM_PROG_CC_C_O
|
||||
AC_GNU_SOURCE
|
||||
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_HELP_STRING([--disable-rpath], [do not set rpath in executables])],
|
||||
[], [enable_rpath=yes])
|
||||
|
||||
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_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])
|
||||
|
||||
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_ERROR([docbook2man required by man request, but not found])
|
||||
AC_MSG_CHECKING(for docbook2x-man)
|
||||
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
|
||||
|
||||
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],
|
||||
[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_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_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"])
|
||||
|
||||
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(LIBDIR, $libdir)
|
||||
AS_AC_EXPAND(BINDIR, $bindir)
|
||||
@ -69,12 +165,13 @@ AC_ARG_WITH([rootfs-path],
|
||||
[lxc rootfs mount point]
|
||||
)], [], [with_rootfs_path=['${libdir}/lxc/rootfs']])
|
||||
|
||||
AS_AC_EXPAND(LXC_CONFFILE, $conffile)
|
||||
AS_AC_EXPAND(LXC_GENERATE_DATE, "$(date)")
|
||||
AS_AC_EXPAND(LXCPATH, "${with_config_path}")
|
||||
AS_AC_EXPAND(LXCROOTFSMOUNT, "${with_rootfs_path}")
|
||||
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],
|
||||
[],
|
||||
@ -101,9 +198,10 @@ AC_CHECK_DECLS([PR_CAPBSET_DROP], [], [], [#include <sys/prctl.h>])
|
||||
AC_CHECK_HEADERS([sys/signalfd.h])
|
||||
|
||||
AC_PROG_GCC_TRADITIONAL
|
||||
AC_PROG_SED
|
||||
|
||||
if test "x$GCC" = "xyes"; then
|
||||
CFLAGS="$CFLAGS -Wall"
|
||||
CFLAGS="$CFLAGS -Wall -Werror"
|
||||
fi
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
@ -134,6 +232,7 @@ AC_CONFIG_FILES([
|
||||
doc/lxc.sgml
|
||||
doc/common_options.sgml
|
||||
doc/see_also.sgml
|
||||
doc/legacy/lxc-ls.sgml
|
||||
|
||||
doc/rootfs/Makefile
|
||||
|
||||
@ -154,6 +253,7 @@ AC_CONFIG_FILES([
|
||||
templates/lxc-opensuse
|
||||
templates/lxc-busybox
|
||||
templates/lxc-fedora
|
||||
templates/lxc-oracle
|
||||
templates/lxc-altlinux
|
||||
templates/lxc-sshd
|
||||
templates/lxc-archlinux
|
||||
@ -161,7 +261,6 @@ AC_CONFIG_FILES([
|
||||
src/Makefile
|
||||
src/lxc/Makefile
|
||||
src/lxc/lxc-ps
|
||||
src/lxc/lxc-ls
|
||||
src/lxc/lxc-netstat
|
||||
src/lxc/lxc-checkconfig
|
||||
src/lxc/lxc-setcap
|
||||
@ -170,7 +269,15 @@ AC_CONFIG_FILES([
|
||||
src/lxc/lxc-create
|
||||
src/lxc/lxc-clone
|
||||
src/lxc/lxc-shutdown
|
||||
src/lxc/lxc-start-ephemeral
|
||||
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],[[]],[[]])
|
||||
|
@ -19,7 +19,6 @@ man_MANS = \
|
||||
lxc-unfreeze.1 \
|
||||
lxc-monitor.1 \
|
||||
lxc-wait.1 \
|
||||
lxc-ls.1 \
|
||||
lxc-ps.1 \
|
||||
lxc-cgroup.1 \
|
||||
lxc-kill.1 \
|
||||
@ -29,15 +28,23 @@ man_MANS = \
|
||||
\
|
||||
lxc.7
|
||||
|
||||
if ENABLE_PYTHON
|
||||
man_MANS += lxc-ls.1
|
||||
else
|
||||
man_MANS += legacy/lxc-ls.1
|
||||
endif
|
||||
|
||||
%.1 : %.sgml
|
||||
docbook2man -w all $<
|
||||
$(db2xman) $<
|
||||
test "$(shell basename $@)" != "$@" && mv $(shell basename $@) $@ || true
|
||||
|
||||
%.5 : %.sgml
|
||||
docbook2man -w all $<
|
||||
$(db2xman) $<
|
||||
test "$(shell basename $@)" != "$@" && mv $(shell basename $@) $@ || true
|
||||
|
||||
%.7 : %.sgml
|
||||
docbook2man -w all $<
|
||||
$(db2xman) $<
|
||||
test "$(shell basename $@)" != "$@" && mv $(shell basename $@) $@ || true
|
||||
|
||||
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 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="opt">-a <replaceable>arch</replaceable></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>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
@ -125,6 +127,52 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
</listitem>
|
||||
</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>
|
||||
|
||||
</refsect1>
|
||||
@ -147,19 +195,86 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
</para>
|
||||
<para>
|
||||
To deactivate the network link eth1 of a running container that
|
||||
does not have the NET_ADMIN capability, use the <option>-e</option>
|
||||
option to use increased capabilities:
|
||||
does not have the NET_ADMIN capability, use either the
|
||||
<option>-e</option> option to use increased capabilities,
|
||||
assuming the <command>ip</command> tool is installed:
|
||||
<programlisting>
|
||||
lxc-attach -n container -e -- /sbin/ip link delete eth1
|
||||
</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>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Security</title>
|
||||
<para>
|
||||
The <option>-e</option> should be used with care, as it may break
|
||||
the isolation of the containers if used improperly.
|
||||
The <option>-e</option> and <option>-s</option> options should
|
||||
be used with care, as it may break the isolation of the containers
|
||||
if used improperly.
|
||||
</para>
|
||||
</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 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 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 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 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.
|
||||
Refer to the examples in <filename>@LXCTEMPLATEDIR@</filename>
|
||||
for details of the expected script structure.
|
||||
Alternatively, the full path to an executable template script
|
||||
can also be passed as a parameter.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -123,9 +125,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
</term>
|
||||
<listitem>
|
||||
<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
|
||||
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
|
||||
automatically if the <filename>@LXCPATH@</filename> filesystem is found to
|
||||
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.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<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 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 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 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 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">
|
||||
]>
|
||||
@ -49,8 +49,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>lxc-ls</command>
|
||||
<arg choice="opt">-1</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>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -65,6 +71,16 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
<refsect1>
|
||||
<title>Options</title>
|
||||
<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>
|
||||
<term>
|
||||
@ -72,48 +88,79 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
List active containers.
|
||||
List only active containers (same as --frozen --running).
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<option><optional>ls options</optional></option>
|
||||
<option><optional>--frozen</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.
|
||||
List only frozen containers.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>lxc-ls --active -1</term>
|
||||
<term>
|
||||
<option><optional>--running</optional></option>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
list active containers and display the list in one column.
|
||||
List only running containers.
|
||||
</para>
|
||||
</listitem>
|
||||
</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>
|
||||
</refsect1>
|
||||
|
||||
@ -127,15 +174,33 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
</citerefentry>,
|
||||
</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>
|
||||
<title>Author</title>
|
||||
<para>Daniel Lezcano <email>daniel.lezcano@free.fr</email></para>
|
||||
<para>Stéphane Graber <email>stgraber@ubuntu.com</email></para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
|
||||
<!-- 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 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">
|
||||
]>
|
||||
|
@ -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 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 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 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">-c <replaceable>console_file</replaceable></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">-C</arg>
|
||||
<arg choice="opt">command</arg>
|
||||
@ -107,6 +108,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
</listitem>
|
||||
</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>
|
||||
<term>
|
||||
<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 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 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 seealso SYSTEM "@builddir@/see_also.sgml">
|
||||
@ -79,6 +79,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
</listitem>
|
||||
</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>
|
||||
|
||||
</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">
|
||||
]>
|
||||
@ -374,6 +374,26 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
</para>
|
||||
</listitem>
|
||||
</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>
|
||||
</refsect2>
|
||||
|
||||
@ -481,6 +501,31 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
</variablelist>
|
||||
</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>
|
||||
<title>Mount points</title>
|
||||
<para>
|
||||
@ -640,6 +685,84 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
</variablelist>
|
||||
</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>
|
||||
|
@ -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">
|
||||
]>
|
||||
@ -280,7 +280,7 @@ rootfs
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
|
||||
<![CDATA[
|
||||
---------
|
||||
| STOPPED |<---------------
|
||||
--------- |
|
||||
@ -305,7 +305,7 @@ rootfs
|
||||
---------- |
|
||||
| |
|
||||
---------------------
|
||||
|
||||
]]>
|
||||
</programlisting>
|
||||
</refsect2>
|
||||
|
||||
@ -570,7 +570,7 @@ rootfs
|
||||
to the background.
|
||||
|
||||
<programlisting>
|
||||
|
||||
<![CDATA[
|
||||
# launch lxc-wait in background
|
||||
lxc-wait -n foo -s STOPPED &
|
||||
LXC_WAIT_PID=$!
|
||||
@ -583,7 +583,7 @@ rootfs
|
||||
# is STOPPED
|
||||
wait $LXC_WAIT_PID
|
||||
echo "'foo' is finished"
|
||||
|
||||
]]>
|
||||
</programlisting>
|
||||
</para>
|
||||
</refsect2>
|
||||
|
@ -29,8 +29,8 @@ Summary: %{name} : Linux Container
|
||||
Group: Applications/System
|
||||
License: LGPL
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
||||
Requires: libcap
|
||||
BuildRequires: libcap libcap-devel docbook-utils
|
||||
Requires: libcap openssl rsync
|
||||
BuildRequires: libcap libcap-devel docbook2X
|
||||
|
||||
%description
|
||||
|
||||
@ -91,11 +91,13 @@ rm -rf %{buildroot}
|
||||
%{_mandir}/*
|
||||
%{_datadir}/doc/*
|
||||
%{_datadir}/lxc/*
|
||||
%{_sysconfdir}/lxc/*
|
||||
|
||||
%files libs
|
||||
%defattr(-,root,root)
|
||||
%{_libdir}/*.so.*
|
||||
%{_libdir}/%{name}
|
||||
%{_localstatedir}/*
|
||||
%attr(4555,root,root) %{_libexecdir}/%{name}/lxc-init
|
||||
|
||||
%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 \
|
||||
log.h \
|
||||
state.h \
|
||||
attach.h
|
||||
attach.h \
|
||||
lxccontainer.h \
|
||||
lxclock.h
|
||||
|
||||
sodir=$(libdir)
|
||||
# use PROGRAMS to avoid complains from automake
|
||||
@ -50,33 +52,41 @@ liblxc_so_SOURCES = \
|
||||
genl.c genl.h \
|
||||
\
|
||||
caps.c caps.h \
|
||||
lxcseccomp.h \
|
||||
mainloop.c mainloop.h \
|
||||
af_unix.c af_unix.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 \
|
||||
-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
|
||||
-DLXCPATH=\"$(LXCPATH)\" \
|
||||
-DLXCINITDIR=\"$(LXCINITDIR)\"
|
||||
-DLXCINITDIR=\"$(LXCINITDIR)\" \
|
||||
-DLXCTEMPLATEDIR=\"$(LXCTEMPLATEDIR)\"
|
||||
|
||||
if ENABLE_APPARMOR
|
||||
AM_CFLAGS += -DHAVE_APPARMOR
|
||||
endif
|
||||
|
||||
if ENABLE_SECCOMP
|
||||
AM_CFLAGS += -DHAVE_SECCOMP
|
||||
liblxc_so_SOURCES += seccomp.c
|
||||
endif
|
||||
|
||||
liblxc_so_CFLAGS = -fPIC -DPIC $(AM_CFLAGS)
|
||||
|
||||
liblxc_so_LDFLAGS = \
|
||||
-shared \
|
||||
-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 = \
|
||||
lxc-ps \
|
||||
lxc-netstat \
|
||||
lxc-ls \
|
||||
lxc-checkconfig \
|
||||
lxc-setcap \
|
||||
lxc-setuid \
|
||||
@ -86,6 +96,14 @@ bin_SCRIPTS = \
|
||||
lxc-shutdown \
|
||||
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 = \
|
||||
lxc-attach \
|
||||
lxc-unshare \
|
||||
@ -110,7 +128,7 @@ AM_LDFLAGS = -Wl,-E
|
||||
if ENABLE_RPATH
|
||||
AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir)
|
||||
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_cgroup_SOURCES = lxc_cgroup.c
|
||||
|
@ -45,6 +45,7 @@ struct lxc_arguments {
|
||||
int daemonize;
|
||||
const char *rcfile;
|
||||
const char *console;
|
||||
const char *pidfile;
|
||||
|
||||
/* for lxc-checkpoint/restart */
|
||||
const char *statefile;
|
||||
@ -57,6 +58,7 @@ struct lxc_arguments {
|
||||
|
||||
/* for lxc-wait */
|
||||
char *states;
|
||||
long timeout;
|
||||
|
||||
/* close fds from parent? */
|
||||
int close_all_fds;
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
#if !HAVE_DECL_PR_CAPBSET_DROP
|
||||
@ -121,13 +122,22 @@ out_error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int lxc_attach_to_ns(pid_t pid)
|
||||
int lxc_attach_to_ns(pid_t pid, int which)
|
||||
{
|
||||
char path[MAXPATHLEN];
|
||||
char *ns[] = { "pid", "mnt", "net", "ipc", "uts" };
|
||||
const int size = sizeof(ns) / sizeof(char *);
|
||||
/* according to <http://article.gmane.org/gmane.linux.kernel.containers.lxc.devel/1429>,
|
||||
* 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 i;
|
||||
int i, j, saved_errno;
|
||||
|
||||
|
||||
snprintf(path, MAXPATHLEN, "/proc/%d/ns", pid);
|
||||
if (access(path, X_OK)) {
|
||||
@ -136,16 +146,39 @@ int lxc_attach_to_ns(pid_t pid)
|
||||
}
|
||||
|
||||
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]);
|
||||
fd[i] = open(path, O_RDONLY);
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
return -1;
|
||||
}
|
||||
@ -156,6 +189,49 @@ int lxc_attach_to_ns(pid_t pid)
|
||||
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 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 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);
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
static int lxc_one_cgroup_attach(const char *name,
|
||||
struct mntent *mntent, pid_t pid)
|
||||
static int lxc_one_cgroup_finish_attach(int fd, 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 *cgmnt = mntent->mnt_dir;
|
||||
int flags, ret = 0;
|
||||
int flags;
|
||||
int rc;
|
||||
|
||||
flags = get_cgroup_flags(mntent);
|
||||
@ -274,31 +299,83 @@ static int lxc_one_cgroup_attach(const char *name,
|
||||
return -1;
|
||||
}
|
||||
|
||||
f = fopen(tasks, "w");
|
||||
if (!f) {
|
||||
fd = open(tasks, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
SYSERROR("failed to open '%s'", tasks);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fprintf(f, "%d", pid) <= 0) {
|
||||
SYSERROR("failed to write pid '%d' to '%s'", pid, tasks);
|
||||
ret = -1;
|
||||
return fd;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* for each mounted cgroup, attach a pid to the cgroup for the container
|
||||
*/
|
||||
int lxc_cgroup_attach(const char *name, pid_t pid)
|
||||
int lxc_cgroup_finish_attach(void *data, pid_t pid)
|
||||
{
|
||||
int *fds = data;
|
||||
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;
|
||||
FILE *file = NULL;
|
||||
int err = -1;
|
||||
int found = 0;
|
||||
int *fds;
|
||||
int i;
|
||||
static const int MAXFDS = 256;
|
||||
|
||||
file = setmntent(MTAB, "r");
|
||||
if (!file) {
|
||||
@ -306,7 +383,29 @@ int lxc_cgroup_attach(const char *name, pid_t pid)
|
||||
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))) {
|
||||
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);
|
||||
|
||||
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'",
|
||||
++found, mntent->mnt_dir, mntent->mnt_opts);
|
||||
|
||||
err = lxc_one_cgroup_attach(name, mntent, pid);
|
||||
if (err)
|
||||
fds[i] = lxc_one_cgroup_prepare_attach(name, mntent);
|
||||
if (fds[i] < 0) {
|
||||
err = fds[i];
|
||||
lxc_cgroup_dispose_attach(fds);
|
||||
goto out;
|
||||
}
|
||||
i++;
|
||||
};
|
||||
|
||||
if (!found)
|
||||
ERROR("No cgroup mounted on the system");
|
||||
|
||||
*data = fds;
|
||||
|
||||
out:
|
||||
endmntent(file);
|
||||
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
|
||||
* 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 (access(cgparent, F_OK)) {
|
||||
ret = mkdir(cgparent, 0755);
|
||||
if (ret == -1 && errno == EEXIST) {
|
||||
if (ret == -1 && errno != EEXIST) {
|
||||
SYSERROR("failed to create '%s' directory", cgparent);
|
||||
return -1;
|
||||
}
|
||||
@ -668,6 +789,13 @@ out:
|
||||
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,
|
||||
char *value, size_t len)
|
||||
{
|
||||
@ -692,7 +820,18 @@ int lxc_cgroup_get(const char *name, const char *filename,
|
||||
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);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
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_nrtasks(const char *name);
|
||||
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);
|
||||
#endif
|
||||
|
@ -154,11 +154,32 @@ pid_t get_init_pid(const char *name)
|
||||
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 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_state_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,
|
||||
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_STATE] = lxc_state_callback,
|
||||
[LXC_COMMAND_PID] = lxc_pid_callback,
|
||||
[LXC_COMMAND_CLONE_FLAGS] = lxc_clone_flags_callback,
|
||||
};
|
||||
|
||||
if (request->type < 0 || request->type >= LXC_COMMAND_MAX)
|
||||
@ -305,5 +327,6 @@ extern int lxc_command_mainloop_add(const char *name,
|
||||
close(fd);
|
||||
}
|
||||
|
||||
handler->conf->maincmd_fd = fd;
|
||||
return ret;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ enum {
|
||||
LXC_COMMAND_STOP,
|
||||
LXC_COMMAND_STATE,
|
||||
LXC_COMMAND_PID,
|
||||
LXC_COMMAND_CLONE_FLAGS,
|
||||
LXC_COMMAND_MAX,
|
||||
};
|
||||
|
||||
@ -48,6 +49,7 @@ struct lxc_command {
|
||||
};
|
||||
|
||||
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,
|
||||
int *stopped);
|
||||
|
580
src/lxc/conf.c
580
src/lxc/conf.c
@ -66,6 +66,8 @@
|
||||
#include <apparmor.h>
|
||||
#endif
|
||||
|
||||
#include "lxcseccomp.h"
|
||||
|
||||
lxc_log_define(lxc_conf, lxc);
|
||||
|
||||
#define MAXHWLEN 18
|
||||
@ -109,6 +111,9 @@ lxc_log_define(lxc_conf, lxc);
|
||||
#define PR_CAPBSET_DROP 24
|
||||
#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);
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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[] = {
|
||||
{ "defaults", 0, 0 },
|
||||
{ "ro", 0, MS_RDONLY },
|
||||
@ -214,12 +233,41 @@ static struct caps_opt caps_opt[] = {
|
||||
#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,
|
||||
const char *script, ...)
|
||||
{
|
||||
int ret;
|
||||
FILE *f;
|
||||
char *buffer, *p, *output;
|
||||
char *buffer, *p;
|
||||
size_t size = 0;
|
||||
va_list ap;
|
||||
|
||||
@ -266,29 +314,7 @@ static int run_script(const char *name, const char *section,
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
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;
|
||||
return run_buffer(buffer);
|
||||
}
|
||||
|
||||
static int find_fstype_cb(char* buffer, void *data)
|
||||
@ -636,6 +662,15 @@ static int setup_tty(const struct lxc_rootfs *rootfs,
|
||||
return -1;
|
||||
}
|
||||
} 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)) {
|
||||
WARN("failed to mount '%s'->'%s'",
|
||||
pty_info->name, path);
|
||||
@ -708,7 +743,7 @@ static int umount_oldrootfs(const char *oldrootfs)
|
||||
{
|
||||
char path[MAXPATHLEN];
|
||||
void *cbparm[2];
|
||||
struct lxc_list mountlist, *iterator;
|
||||
struct lxc_list mountlist, *iterator, *next;
|
||||
int ok, still_mounted, last_still_mounted;
|
||||
int rc;
|
||||
|
||||
@ -748,7 +783,7 @@ static int umount_oldrootfs(const char *oldrootfs)
|
||||
last_still_mounted = still_mounted;
|
||||
still_mounted = 0;
|
||||
|
||||
lxc_list_for_each(iterator, &mountlist) {
|
||||
lxc_list_for_each_safe(iterator, &mountlist, next) {
|
||||
|
||||
/* umount normally */
|
||||
if (!umount(iterator->elem)) {
|
||||
@ -843,6 +878,114 @@ static int setup_rootfs_pivot_root(const char *rootfs, const char *pivotdir)
|
||||
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)
|
||||
{
|
||||
if (!rootfs->path)
|
||||
@ -1061,6 +1204,8 @@ static int setup_kmsg(const struct lxc_rootfs *rootfs,
|
||||
char kpath[MAXPATHLEN];
|
||||
int ret;
|
||||
|
||||
if (!rootfs->path)
|
||||
return 0;
|
||||
ret = snprintf(kpath, sizeof(kpath), "%s/dev/kmsg", rootfs->mount);
|
||||
if (ret < 0 || ret >= sizeof(kpath))
|
||||
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
|
||||
* use /var/lib/lxc/CN/rootfs as the target prefix */
|
||||
r = snprintf(path, MAXPATHLEN, "/var/lib/lxc/%s/rootfs", lxc_name);
|
||||
* use $LXCPATH/CN/rootfs as the target prefix */
|
||||
r = snprintf(path, MAXPATHLEN, LXCPATH "/%s/rootfs", lxc_name);
|
||||
if (r < 0 || r >= MAXPATHLEN)
|
||||
goto skipvarlib;
|
||||
|
||||
@ -1647,7 +1792,7 @@ static int setup_netdev(struct lxc_netdev *netdev)
|
||||
ifname, strerror(-err));
|
||||
if (netdev->ipv6_gateway_auto) {
|
||||
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);
|
||||
}
|
||||
return -1;
|
||||
@ -1680,6 +1825,21 @@ static int setup_network(struct lxc_list *network)
|
||||
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)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
@ -1715,6 +1875,8 @@ static int setup_private_host_hw_addr(char *veth1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *default_rootfs_mount = LXCROOTFSMOUNT;
|
||||
|
||||
struct lxc_conf *lxc_conf_init(void)
|
||||
{
|
||||
struct lxc_conf *new;
|
||||
@ -1733,7 +1895,8 @@ struct lxc_conf *lxc_conf_init(void)
|
||||
new->console.master = -1;
|
||||
new->console.slave = -1;
|
||||
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->network);
|
||||
lxc_list_init(&new->mount_list);
|
||||
@ -1765,6 +1928,8 @@ static int instanciate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
|
||||
return -1;
|
||||
}
|
||||
veth1 = mktemp(veth1buf);
|
||||
/* store away for deconf */
|
||||
memcpy(netdev->priv.veth_attr.veth1, veth1, IFNAMSIZ);
|
||||
}
|
||||
|
||||
snprintf(veth2buf, sizeof(veth2buf), "vethXXXXXX");
|
||||
@ -1841,6 +2006,25 @@ out_delete:
|
||||
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)
|
||||
{
|
||||
char peerbuf[IFNAMSIZ], *peer;
|
||||
@ -1889,6 +2073,20 @@ static int instanciate_macvlan(struct lxc_handler *handler, struct lxc_netdev *n
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!netdev->link) {
|
||||
@ -1950,6 +2153,19 @@ static int instanciate_phys(struct lxc_handler *handler, struct lxc_netdev *netd
|
||||
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)
|
||||
{
|
||||
netdev->ifindex = 0;
|
||||
@ -1963,6 +2179,19 @@ static int instanciate_empty(struct lxc_handler *handler, struct lxc_netdev *net
|
||||
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)
|
||||
{
|
||||
struct lxc_list *network = &handler->conf->network;
|
||||
@ -1989,28 +2218,32 @@ int lxc_create_network(struct lxc_handler *handler)
|
||||
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_netdev *netdev;
|
||||
|
||||
lxc_list_for_each(iterator, network) {
|
||||
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))
|
||||
WARN("failed to rename to the initial name the " \
|
||||
"netdev '%s'", netdev->link);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (netdev_deconf[netdev->type](handler, netdev)) {
|
||||
WARN("failed to destroy netdev");
|
||||
}
|
||||
|
||||
/* Recent kernel remove the virtual interfaces when the network
|
||||
* namespace is destroyed but in case we did not moved the
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@ -2166,11 +2399,23 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
|
||||
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)) {
|
||||
ERROR("failed to setup rootfs for '%s'", name);
|
||||
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)) {
|
||||
ERROR("failed to setup the mounts for '%s'", name);
|
||||
return -1;
|
||||
@ -2186,6 +2431,13 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
|
||||
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)) {
|
||||
ERROR("failed to setup the cgroups for '%s'", name);
|
||||
return -1;
|
||||
@ -2196,10 +2448,8 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setup_tty(&lxc_conf->rootfs, &lxc_conf->tty_info, lxc_conf->ttydir)) {
|
||||
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)
|
||||
which = LXCHOOK_PRESTART;
|
||||
else if (strcmp(hook, "pre-mount") == 0)
|
||||
which = LXCHOOK_PREMOUNT;
|
||||
else if (strcmp(hook, "mount") == 0)
|
||||
which = LXCHOOK_MOUNT;
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/param.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
@ -31,6 +32,10 @@
|
||||
|
||||
#include <lxc/start.h> /* for lxc_handler */
|
||||
|
||||
#if HAVE_SCMP_FILTER_CTX
|
||||
typedef void * scmp_filter_ctx;
|
||||
#endif
|
||||
|
||||
enum {
|
||||
LXC_NET_EMPTY,
|
||||
LXC_NET_VETH,
|
||||
@ -76,6 +81,7 @@ struct lxc_route6 {
|
||||
|
||||
struct ifla_veth {
|
||||
char *pair; /* pair name */
|
||||
char veth1[IFNAMSIZ]; /* needed for deconf */
|
||||
};
|
||||
|
||||
struct ifla_vlan {
|
||||
@ -103,6 +109,7 @@ union netdev_p {
|
||||
* @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
|
||||
* @upscript : a script filename to be executed during interface configuration
|
||||
* @downscript : a script filename to be executed during interface destruction
|
||||
*/
|
||||
struct lxc_netdev {
|
||||
int type;
|
||||
@ -120,6 +127,7 @@ struct lxc_netdev {
|
||||
struct in6_addr *ipv6_gateway;
|
||||
bool ipv6_gateway_auto;
|
||||
char *upscript;
|
||||
char *downscript;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -203,8 +211,15 @@ struct lxc_rootfs {
|
||||
#endif
|
||||
*/
|
||||
enum lxchooks {
|
||||
LXCHOOK_PRESTART, LXCHOOK_MOUNT, LXCHOOK_START,
|
||||
LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_START,
|
||||
LXCHOOK_POSTSTOP, NUM_LXC_HOOKS};
|
||||
extern char *lxchook_names[NUM_LXC_HOOKS];
|
||||
|
||||
struct saved_nic {
|
||||
int ifindex;
|
||||
char *orig_name;
|
||||
};
|
||||
|
||||
struct lxc_conf {
|
||||
char *fstab;
|
||||
int tty;
|
||||
@ -215,6 +230,8 @@ struct lxc_conf {
|
||||
struct utsname *utsname;
|
||||
struct lxc_list cgroup;
|
||||
struct lxc_list network;
|
||||
struct saved_nic *saved_nics;
|
||||
int num_savednics;
|
||||
struct lxc_list mount_list;
|
||||
struct lxc_list caps;
|
||||
struct lxc_tty_info tty_info;
|
||||
@ -226,9 +243,18 @@ struct lxc_conf {
|
||||
#if HAVE_APPARMOR
|
||||
char *aa_profile;
|
||||
#endif
|
||||
char *logfile;
|
||||
int loglevel;
|
||||
|
||||
#if HAVE_APPARMOR /* || HAVE_SELINUX || HAVE_SMACK */
|
||||
int lsm_umount_proc;
|
||||
#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);
|
||||
@ -237,20 +263,30 @@ int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf);
|
||||
* Initialize the lxc configuration structure
|
||||
*/
|
||||
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 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_find_gateway_addresses(struct lxc_handler *handler);
|
||||
|
||||
extern int lxc_create_tty(const char *name, struct lxc_conf *conf);
|
||||
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
|
||||
*/
|
||||
|
||||
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
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -27,6 +27,15 @@
|
||||
struct lxc_conf;
|
||||
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_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 */
|
||||
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
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#include "log.h"
|
||||
#include "start.h"
|
||||
|
||||
|
@ -75,7 +75,7 @@ directory=$(readlink -f "$lxc_path")
|
||||
for i in "$@"; do
|
||||
case $i in
|
||||
--help)
|
||||
help; exit 1;;
|
||||
help; exit;;
|
||||
--active)
|
||||
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)
|
||||
fi
|
||||
|
||||
if [ -z "$containers" ]; then
|
||||
echo "$(basename $0): no containers found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$directory"
|
||||
ls -d $@ -- $containers
|
@ -14,6 +14,11 @@ struct lxc_list {
|
||||
__iterator != __list; \
|
||||
__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)
|
||||
{
|
||||
list->elem = NULL;
|
||||
|
@ -41,6 +41,7 @@
|
||||
|
||||
int lxc_log_fd = -1;
|
||||
static char log_prefix[LXC_LOG_PREFIX_SIZE] = "lxc";
|
||||
int lxc_loglevel_specified = 0;
|
||||
|
||||
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;
|
||||
|
||||
if (lxc_log_fd != -1)
|
||||
return 0;
|
||||
|
||||
if (priority) {
|
||||
lxc_loglevel_specified = 1;
|
||||
lxc_priority = lxc_log_priority_to_int(priority);
|
||||
|
||||
if (lxc_priority == LXC_LOG_PRIORITY_NOTSET) {
|
||||
@ -185,3 +190,39 @@ extern int lxc_log_init(const char *file, const char *priority,
|
||||
|
||||
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
|
||||
|
||||
/* predefined priorities. */
|
||||
enum {
|
||||
enum lxc_loglevel {
|
||||
LXC_LOG_PRIORITY_TRACE,
|
||||
LXC_LOG_PRIORITY_DEBUG,
|
||||
LXC_LOG_PRIORITY_INFO,
|
||||
@ -291,4 +291,6 @@ extern int lxc_log_init(const char *file, const char *priority,
|
||||
const char *prefix, int quiet);
|
||||
|
||||
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
|
||||
|
@ -1,13 +1,13 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
# Allow environment variables to override grep and config
|
||||
: ${CONFIG:=/proc/config.gz}
|
||||
: ${GREP:=zgrep}
|
||||
|
||||
SETCOLOR_SUCCESS="echo -en \\033[1;32m"
|
||||
SETCOLOR_FAILURE="echo -en \\033[1;31m"
|
||||
SETCOLOR_WARNING="echo -en \\033[1;33m"
|
||||
SETCOLOR_NORMAL="echo -en \\033[0;39m"
|
||||
SETCOLOR_SUCCESS="printf \\e[1;32m"
|
||||
SETCOLOR_FAILURE="printf \\e[1;31m"
|
||||
SETCOLOR_WARNING="printf \\e[1;33m"
|
||||
SETCOLOR_NORMAL="printf \\e[0;39m"
|
||||
|
||||
is_set() {
|
||||
$GREP -q "$1=[y|m]" $CONFIG
|
||||
@ -21,12 +21,12 @@ is_enabled() {
|
||||
RES=$?
|
||||
|
||||
if [ $RES -eq 0 ]; then
|
||||
$SETCOLOR_SUCCESS && echo -e "enabled" && $SETCOLOR_NORMAL
|
||||
$SETCOLOR_SUCCESS && echo "enabled" && $SETCOLOR_NORMAL
|
||||
else
|
||||
if [ ! -z "$mandatory" -a "$mandatory" = yes ]; then
|
||||
$SETCOLOR_FAILURE && echo -e "required" && $SETCOLOR_NORMAL
|
||||
$SETCOLOR_FAILURE && echo "required" && $SETCOLOR_NORMAL
|
||||
else
|
||||
$SETCOLOR_WARNING && echo -e "missing" && $SETCOLOR_NORMAL
|
||||
$SETCOLOR_WARNING && echo "missing" && $SETCOLOR_NORMAL
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@ -68,40 +68,45 @@ print_cgroups() {
|
||||
}
|
||||
|
||||
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 | \
|
||||
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 | \
|
||||
sed -r 's/.* 2.6.([0-9]{2}).*/\1/')
|
||||
else
|
||||
KVER_MINOR=$($GREP '^# Linux' $CONFIG | \
|
||||
sed -r 's/.* [0-9]\.([0-9]{1,3})\.[0-9]{1,3}.*/\1/')
|
||||
fi
|
||||
echo -n "File capabilities: " &&
|
||||
( [[ ${KVER_MAJOR} == 2 && ${KVER_MINOR} < 33 ]] &&
|
||||
is_enabled CONFIG_SECURITY_FILE_CAPABILITIES ) ||
|
||||
( [[ ( ${KVER_MAJOR} == 2 && ${KVER_MINOR} > 32 ) ||
|
||||
${KVER_MAJOR} > 2 ]] && $SETCOLOR_SUCCESS &&
|
||||
echo -e "enabled" && $SETCOLOR_NORMAL )
|
||||
|
||||
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 "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 "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
|
||||
mount /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new $rootfs || { echo "$(basename $0): failed to mount new rootfs" >&2; false; }
|
||||
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
|
||||
rmdir ${rootfs}_snapshot
|
||||
lvremove -f $lxc_vg/${lxc_lv_prefix}${lxc_new}_snapshot
|
||||
@ -252,7 +252,7 @@ else
|
||||
frozen=1
|
||||
fi
|
||||
mkdir -p $rootfs/
|
||||
rsync -ax $oldroot/ $rootfs/
|
||||
rsync -Hax $oldroot/ $rootfs/
|
||||
echo "lxc.rootfs = $rootfs" >> $lxc_path/$lxc_new/config
|
||||
if [ $container_running = "True" ]; then
|
||||
lxc-unfreeze -n $lxc_orig
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# lxc: linux Container library
|
||||
@ -26,6 +26,7 @@ usage() {
|
||||
echo >&2
|
||||
echo "where FS_OPTIONS is one of:" >&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 " [--fssize FS_SIZE]" >&2
|
||||
echo " -B btrfs" >&2
|
||||
@ -43,25 +44,35 @@ help() {
|
||||
echo " -B BACKING_STORE alter the container backing store (default: none)" >&2
|
||||
echo " --lvname LV_NAME specify the LVM logical volume 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 " --fstype FS_TYPE specify the filesystem type (default: ext4)" >&2
|
||||
echo " --fssize FS_SIZE specify the filesystem size (default: 500M)" >&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 " $(basename $0) -t ubuntu -h" >&2
|
||||
exit 0
|
||||
fi
|
||||
type ${templatedir}/lxc-$lxc_template 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
if [ -x ${templatedir}/lxc-$lxc_template ]; then
|
||||
echo >&2
|
||||
echo "Template-specific options (TEMPLATE_OPTIONS):" >&2
|
||||
${templatedir}/lxc-$lxc_template -h
|
||||
fi
|
||||
}
|
||||
|
||||
shortoptions='hn:f:t:B:'
|
||||
longoptions='help,name:,config:,template:,backingstore:,fstype:,lvname:,vgname:,fssize:'
|
||||
usage_err() {
|
||||
[ -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@
|
||||
bindir=@BINDIR@
|
||||
templatedir=@LXCTEMPLATEDIR@
|
||||
@ -69,64 +80,70 @@ backingstore=_unset
|
||||
fstype=ext4
|
||||
fssize=500M
|
||||
vgname=lxc
|
||||
custom_rootfs=""
|
||||
|
||||
getopt=$(getopt -o $shortoptions --longoptions $longoptions -- "$@")
|
||||
if [ $? != 0 ]; then
|
||||
usage
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
eval set -- "$getopt"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
while [ $# -gt 0 ]; do
|
||||
opt="$1"
|
||||
shift
|
||||
case "$opt" in
|
||||
-h|--help)
|
||||
help
|
||||
exit 1
|
||||
;;
|
||||
-n|--name)
|
||||
shift
|
||||
optarg_check $opt "$1"
|
||||
lxc_name=$1
|
||||
shift
|
||||
;;
|
||||
-f|--config)
|
||||
shift
|
||||
optarg_check $opt "$1"
|
||||
lxc_config=$1
|
||||
shift
|
||||
;;
|
||||
-t|--template)
|
||||
shift
|
||||
optarg_check $opt "$1"
|
||||
lxc_template=$1
|
||||
shift
|
||||
;;
|
||||
-B|--backingstore)
|
||||
shift
|
||||
optarg_check $opt "$1"
|
||||
backingstore=$1
|
||||
shift
|
||||
;;
|
||||
--lvname)
|
||||
--dir)
|
||||
optarg_check $opt "$1"
|
||||
custom_rootfs=$1
|
||||
shift
|
||||
;;
|
||||
--lvname)
|
||||
optarg_check $opt "$1"
|
||||
lvname=$1
|
||||
shift
|
||||
;;
|
||||
--vgname)
|
||||
shift
|
||||
optarg_check $opt "$1"
|
||||
vgname=$1
|
||||
shift
|
||||
;;
|
||||
--fstype)
|
||||
shift
|
||||
optarg_check $opt "$1"
|
||||
fstype=$1
|
||||
shift
|
||||
;;
|
||||
--fssize)
|
||||
shift
|
||||
optarg_check $opt "$1"
|
||||
fssize=$1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break;;
|
||||
-?)
|
||||
usage_err "unknown option '$opt'"
|
||||
;;
|
||||
-*)
|
||||
# split opts -abc into -a -b -c
|
||||
set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
@ -137,12 +154,11 @@ done
|
||||
# If -h or --help was passed into the container, we'll want to cleanup
|
||||
# afterward
|
||||
wantedhelp=0
|
||||
for var in "$@"
|
||||
do
|
||||
if [ "$var" = "-h" -o "$var" = "--help" ]; then
|
||||
for var in "$@"; do
|
||||
if [ "$var" = "-h" ] || [ "$var" = "--help" ]; then
|
||||
help
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
@ -171,9 +187,14 @@ if [ "$(id -u)" != "0" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$custom_rootfs" ] && [ "$backingstore" != "dir" ]; then
|
||||
echo "--dir is only valid with -B dir"
|
||||
fi
|
||||
|
||||
case "$backingstore" in
|
||||
lvm|none|btrfs|_unset) :;;
|
||||
*) echo "$(basename $0): '$backingstore' is not known (try 'none', 'lvm', 'btrfs')" >&2
|
||||
dir|lvm|none|btrfs|_unset) :;;
|
||||
*)
|
||||
echo "$(basename $0): '$backingstore' is not known (try 'none', 'dir', 'lvm', 'btrfs')" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
@ -186,9 +207,9 @@ fi
|
||||
|
||||
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 which btrfs >/dev/null 2>&1 &&
|
||||
if which btrfs >/dev/null 2>&1 && \
|
||||
btrfs filesystem df "$lxc_path/" >/dev/null 2>&1; then
|
||||
backingstore="btrfs"
|
||||
else
|
||||
@ -200,12 +221,13 @@ if [ "$backingstore" = "_unset" -o "$backingstore" = "btrfs" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $backingstore = "lvm" ]; then
|
||||
if [ "$backingstore" = "lvm" ]; then
|
||||
which vgscan > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "$(basename $0): vgscan not found (is lvm2 installed?)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
grep -q "\<$fstype\>" /proc/filesystems
|
||||
if [ $? -ne 0 ]; then
|
||||
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
|
||||
exit 1
|
||||
fi
|
||||
|
||||
elif [ "$backingstore" = "btrfs" ]; then
|
||||
mkdir "$lxc_path/$lxc_name"
|
||||
if ! out=$(btrfs subvolume create "$rootfs" 2>&1); then
|
||||
@ -234,10 +257,13 @@ elif [ "$backingstore" = "btrfs" ]; then
|
||||
fi
|
||||
|
||||
cleanup() {
|
||||
if [ $backingstore = "lvm" ]; then
|
||||
if [ "$backingstore" = "lvm" ]; then
|
||||
umount $rootfs
|
||||
lvremove -f $rootdev
|
||||
elif [ "$backingstore" = "btrfs" ]; then
|
||||
btrfs subvolume delete "$rootfs"
|
||||
fi
|
||||
|
||||
${bindir}/lxc-destroy -n $lxc_name
|
||||
echo "$(basename $0): aborted" >&2
|
||||
exit 1
|
||||
@ -248,18 +274,54 @@ trap cleanup HUP INT TERM
|
||||
mkdir -p $lxc_path/$lxc_name
|
||||
|
||||
if [ -z "$lxc_config" ]; then
|
||||
touch $lxc_path/$lxc_name/config
|
||||
else
|
||||
if [ ! -r "$lxc_config" ]; then
|
||||
lxc_config="@SYSCONFDIR@/lxc/lxc.conf"
|
||||
echo
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
# Create the fs as needed
|
||||
if [ $backingstore = "lvm" ]; then
|
||||
if [ "$backingstore" = "lvm" ]; then
|
||||
[ -d "$rootfs" ] || mkdir $rootfs
|
||||
lvcreate -L $fssize -n $lvname $vgname || exit 1
|
||||
udevadm settle
|
||||
@ -267,22 +329,8 @@ if [ $backingstore = "lvm" ]; then
|
||||
mount -t $fstype $rootdev $rootfs
|
||||
fi
|
||||
|
||||
if [ ! -z $lxc_template ]; then
|
||||
|
||||
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 [ ! -z "$lxc_template" ]; then
|
||||
$template_path --path=$lxc_path/$lxc_name --name=$lxc_name $*
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "$(basename $0): failed to execute template '$lxc_template'" >&2
|
||||
cleanup
|
||||
@ -291,7 +339,7 @@ if [ ! -z $lxc_template ]; then
|
||||
echo "'$lxc_template' template installed"
|
||||
fi
|
||||
|
||||
if [ $backingstore = "lvm" ]; then
|
||||
if [ "$backingstore" = "lvm" ]; then
|
||||
echo "Unmounting LVM"
|
||||
umount $rootfs
|
||||
|
||||
|
@ -69,7 +69,8 @@ while true; do
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break;;
|
||||
break
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
@ -110,7 +111,7 @@ fi
|
||||
# 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/^[^/]*/\//'`
|
||||
if [ -n "$rootdev" ]; then
|
||||
if [ -b "$rootdev" ]; then
|
||||
if [ -b "$rootdev" -o -h "$rootdev" ]; then
|
||||
lvdisplay $rootdev > /dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "removing backing store: $rootdev"
|
||||
@ -126,5 +127,6 @@ if [ -n "$rootdev" ]; then
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# recursively remove the container to remove old container configuration
|
||||
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"
|
||||
fi
|
||||
done < <(ps "$@")
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# lxc: linux Container library
|
||||
@ -81,31 +81,39 @@ lxc_dropcaps()
|
||||
chmod 0755 @LXCPATH@
|
||||
}
|
||||
|
||||
shortoptions='hd'
|
||||
longoptions='help'
|
||||
|
||||
getopt=$(getopt -o $shortoptions --longoptions $longoptions -- "$@")
|
||||
if [ $? != 0 ]; then
|
||||
usage_err() {
|
||||
[ -n "$1" ] && echo "$1" >&2
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
eval set -- "$getopt"
|
||||
optarg_check() {
|
||||
if [ -z "$2" ]; then
|
||||
usage_err "option '$1' requires an argument"
|
||||
fi
|
||||
}
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
while [ $# -gt 0 ]; do
|
||||
opt="$1"
|
||||
shift
|
||||
case "$opt" in
|
||||
-d)
|
||||
LXC_DROP_CAPS="yes"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
help
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-?)
|
||||
usage_err "unknown option '$opt'"
|
||||
;;
|
||||
-*)
|
||||
# split opts -abc into -a -b -c
|
||||
set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# lxc: linux Container library
|
||||
@ -78,31 +78,39 @@ lxc_dropuid()
|
||||
chmod 0755 @LXCPATH@
|
||||
}
|
||||
|
||||
shortoptions='hd'
|
||||
longoptions='help'
|
||||
|
||||
getopt=$(getopt -o $shortoptions --longoptions $longoptions -- "$@")
|
||||
if [ $? != 0 ]; then
|
||||
usage_err() {
|
||||
[ -n "$1" ] && echo "$1" >&2
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
eval set -- "$getopt"
|
||||
optarg_check() {
|
||||
if [ -z "$2" ]; then
|
||||
usage_err "option '$1' requires an argument"
|
||||
fi
|
||||
}
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
while [ $# -gt 0 ]; do
|
||||
opt="$1"
|
||||
shift
|
||||
case "$opt" in
|
||||
-d)
|
||||
LXC_DROP_CAPS="yes"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
help
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-?)
|
||||
usage_err "unknown option '$opt'"
|
||||
;;
|
||||
-*)
|
||||
# split opts -abc into -a -b -c
|
||||
set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
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@"
|
||||
|
@ -84,6 +84,7 @@ extern int lxc_monitor_open(void);
|
||||
* data was readen, < 0 otherwise
|
||||
*/
|
||||
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
|
||||
@ -178,6 +179,30 @@ extern int lxc_restart(const char *, int, struct lxc_conf *, int);
|
||||
*/
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
@ -40,22 +40,30 @@
|
||||
#include "start.h"
|
||||
#include "sync.h"
|
||||
#include "log.h"
|
||||
#include "namespace.h"
|
||||
|
||||
lxc_log_define(lxc_attach_ui, lxc);
|
||||
|
||||
static const struct option my_longopts[] = {
|
||||
{"elevated-privileges", no_argument, 0, 'e'},
|
||||
{"arch", required_argument, 0, 'a'},
|
||||
{"namespaces", required_argument, 0, 's'},
|
||||
{"remount-sys-proc", no_argument, 0, 'R'},
|
||||
LXC_COMMON_OPTIONS
|
||||
};
|
||||
|
||||
static int elevated_privileges = 0;
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (c) {
|
||||
case 'e': elevated_privileges = 1; break;
|
||||
case 'R': remount_sys_proc = 1; break;
|
||||
case 'a':
|
||||
new_personality = lxc_config_parse_arch(arg);
|
||||
if (new_personality < 0) {
|
||||
@ -63,6 +71,14 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
||||
return -1;
|
||||
}
|
||||
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;
|
||||
@ -83,7 +99,18 @@ Options :\n\
|
||||
WARNING: This may leak privleges into the container.\n\
|
||||
Use with care.\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,
|
||||
.parser = my_parser,
|
||||
.checker = NULL,
|
||||
@ -96,6 +123,7 @@ int main(int argc, char *argv[])
|
||||
struct passwd *passwd;
|
||||
struct lxc_proc_context_info *init_ctx;
|
||||
struct lxc_handler *handler;
|
||||
void *cgroup_data = NULL;
|
||||
uid_t uid;
|
||||
char *curdir;
|
||||
|
||||
@ -124,6 +152,48 @@ int main(int argc, char *argv[])
|
||||
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 */
|
||||
handler = calloc(1, sizeof(*handler));
|
||||
|
||||
@ -150,8 +220,22 @@ int main(int argc, char *argv[])
|
||||
if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE))
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* tell the child we are done initializing */
|
||||
if (lxc_sync_wake_child(handler, LXC_SYNC_POST_CONFIGURE))
|
||||
@ -175,19 +259,19 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (!pid) {
|
||||
lxc_sync_fini_parent(handler);
|
||||
lxc_cgroup_dispose_attach(cgroup_data);
|
||||
|
||||
curdir = get_current_dir_name();
|
||||
|
||||
ret = lxc_attach_to_ns(init_pid);
|
||||
/* A description of the purpose of this functionality is
|
||||
* provided in the lxc-attach(1) manual page. We have to
|
||||
* 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) {
|
||||
ERROR("failed to enter the namespace");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (curdir && chdir(curdir))
|
||||
WARN("could not change directory to '%s'", curdir);
|
||||
|
||||
free(curdir);
|
||||
}
|
||||
|
||||
if (new_personality < 0)
|
||||
new_personality = init_ctx->personality;
|
||||
|
@ -34,12 +34,14 @@
|
||||
|
||||
static bool state;
|
||||
static bool pid;
|
||||
static char *test_state = NULL;
|
||||
|
||||
static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
||||
{
|
||||
switch (c) {
|
||||
case 's': state = true; break;
|
||||
case 'p': pid = true; break;
|
||||
case 't': test_state = arg; break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -47,6 +49,7 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
||||
static const struct option my_longopts[] = {
|
||||
{"state", no_argument, 0, 's'},
|
||||
{"pid", no_argument, 0, 'p'},
|
||||
{"state-is", required_argument, 0, 't'},
|
||||
LXC_COMMON_OPTIONS,
|
||||
};
|
||||
|
||||
@ -60,7 +63,9 @@ lxc-info display some information about a container with the identifier NAME\n\
|
||||
Options :\n\
|
||||
-n, --name=NAME NAME for name 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,
|
||||
.parser = my_parser,
|
||||
.checker = NULL,
|
||||
@ -81,10 +86,12 @@ int main(int argc, char *argv[])
|
||||
if (!state && !pid)
|
||||
state = pid = true;
|
||||
|
||||
if (state) {
|
||||
if (state || test_state) {
|
||||
ret = lxc_getstate(my_args.name);
|
||||
if (ret < 0)
|
||||
return 1;
|
||||
if (test_state)
|
||||
return strcmp(lxc_state2str(ret), test_state) != 0;
|
||||
|
||||
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 'C': args->close_all_fds = 1; break;
|
||||
case 's': return lxc_config_define_add(&defines, arg);
|
||||
case 'p': args->pidfile = arg; break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -72,6 +73,7 @@ static const struct option my_longopts[] = {
|
||||
{"define", required_argument, 0, 's'},
|
||||
{"console", required_argument, 0, 'c'},
|
||||
{"close-all-fds", no_argument, 0, 'C'},
|
||||
{"pidfile", required_argument, 0, 'p'},
|
||||
LXC_COMMON_OPTIONS
|
||||
};
|
||||
|
||||
@ -85,6 +87,7 @@ lxc-start start COMMAND in specified container NAME\n\
|
||||
Options :\n\
|
||||
-n, --name=NAME NAME for name of 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\
|
||||
-c, --console=FILE Set the file output for the container console\n\
|
||||
-C, --close-all-fds If any fds are inherited, close them\n\
|
||||
@ -95,6 +98,7 @@ Options :\n\
|
||||
.parser = my_parser,
|
||||
.checker = NULL,
|
||||
.daemonize = 0,
|
||||
.pidfile = NULL,
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@ -107,6 +111,7 @@ int main(int argc, char *argv[])
|
||||
"/sbin/init",
|
||||
'\0',
|
||||
};
|
||||
FILE *pid_fp = NULL;
|
||||
|
||||
lxc_list_init(&defines);
|
||||
|
||||
@ -199,6 +204,15 @@ int main(int argc, char *argv[])
|
||||
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) {
|
||||
/* do an early check for needed privs, since otherwise the
|
||||
* 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)
|
||||
conf->close_all_fds = 1;
|
||||
|
||||
@ -230,6 +252,9 @@ int main(int argc, char *argv[])
|
||||
err = -1;
|
||||
}
|
||||
|
||||
if (my_args.pidfile)
|
||||
unlink(my_args.pidfile);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -84,51 +84,6 @@ static uid_t lookup_user(const char *optarg)
|
||||
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 {
|
||||
char ***args;
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include <string.h>
|
||||
#include <libgen.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <lxc/lxc.h>
|
||||
@ -46,12 +48,14 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
||||
{
|
||||
switch (c) {
|
||||
case 's': args->states = optarg; break;
|
||||
case 't': args->timeout = atol(optarg); break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct option my_longopts[] = {
|
||||
{"state", required_argument, 0, 's'},
|
||||
{"timeout", required_argument, 0, 't'},
|
||||
LXC_COMMON_OPTIONS
|
||||
};
|
||||
|
||||
@ -66,37 +70,16 @@ Options :\n\
|
||||
-n, --name=NAME NAME for name of the container\n\
|
||||
-s, --state=STATE ORed states to wait for\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,
|
||||
.parser = my_parser,
|
||||
.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[])
|
||||
{
|
||||
struct lxc_msg msg;
|
||||
int s[MAX_STATE] = { }, fd;
|
||||
int state, ret;
|
||||
|
||||
if (lxc_arguments_parse(&my_args, argc, argv))
|
||||
return -1;
|
||||
|
||||
@ -104,53 +87,5 @@ int main(int argc, char *argv[])
|
||||
my_args.progname, my_args.quiet))
|
||||
return -1;
|
||||
|
||||
if (fillwaitedstates(my_args.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(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;
|
||||
return lxc_wait(strdup(my_args.name), my_args.states, my_args.timeout);
|
||||
}
|
||||
|
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;
|
||||
}
|
||||
|
||||
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;
|
||||
socklen_t len = sizeof(from);
|
||||
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,
|
||||
(struct sockaddr *)&from, &len);
|
||||
@ -114,6 +131,11 @@ int lxc_monitor_read(int fd, struct lxc_msg *msg)
|
||||
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)
|
||||
{
|
||||
return close(fd);
|
||||
|
@ -69,3 +69,48 @@ pid_t lxc_clone(int (*fn)(void *), void *arg, int flags)
|
||||
|
||||
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 int lxc_namespace_2_cloneflag(char *namespace);
|
||||
extern int lxc_fill_namespace_flags(char *flaglist, int *flags);
|
||||
|
||||
#endif
|
||||
|
@ -47,6 +47,7 @@
|
||||
|
||||
#include "nl.h"
|
||||
#include "network.h"
|
||||
#include "conf.h"
|
||||
|
||||
#ifndef IFLA_LINKMODE
|
||||
# define IFLA_LINKMODE 17
|
||||
@ -1004,3 +1005,18 @@ int lxc_bridge_attach(const char *bridge, const char *ifname)
|
||||
|
||||
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 const char *lxc_net_type_to_str(int type);
|
||||
#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 "namespace.h"
|
||||
#include "apparmor.h"
|
||||
#include "lxcseccomp.h"
|
||||
|
||||
lxc_log_define(lxc_start, lxc);
|
||||
|
||||
@ -278,6 +279,29 @@ int lxc_pid_callback(int fd, struct lxc_request *request,
|
||||
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)
|
||||
{
|
||||
handler->state = state;
|
||||
@ -353,6 +377,11 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf)
|
||||
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*/
|
||||
if (lxc_set_state(name, handler, 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)
|
||||
goto out_warn_father;
|
||||
|
||||
if (lxc_seccomp_load(handler->conf) != 0)
|
||||
goto out_warn_father;
|
||||
|
||||
if (run_lxc_hooks(handler->name, "start", handler->conf)) {
|
||||
ERROR("failed to run start hooks for container '%s'.", handler->name);
|
||||
goto out_warn_father;
|
||||
@ -547,9 +579,39 @@ out_warn_father:
|
||||
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 clone_flags;
|
||||
int failed_before_rename = 0;
|
||||
const char *name = handler->name;
|
||||
int pinfd;
|
||||
@ -557,10 +619,10 @@ int lxc_spawn(struct lxc_handler *handler)
|
||||
if (lxc_sync_init(handler))
|
||||
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)) {
|
||||
|
||||
clone_flags |= CLONE_NEWNET;
|
||||
handler->clone_flags |= CLONE_NEWNET;
|
||||
|
||||
/* Find gateway addresses from the link device, which is
|
||||
* 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
|
||||
* marking it readonly.
|
||||
@ -594,7 +661,7 @@ int lxc_spawn(struct lxc_handler *handler)
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
SYSERROR("failed to fork into a new namespace");
|
||||
goto out_delete_net;
|
||||
@ -612,7 +679,7 @@ int lxc_spawn(struct lxc_handler *handler)
|
||||
goto out_delete_net;
|
||||
|
||||
/* Create the network configuration */
|
||||
if (clone_flags & CLONE_NEWNET) {
|
||||
if (handler->clone_flags & CLONE_NEWNET) {
|
||||
if (lxc_assign_network(&handler->conf->network, handler->pid)) {
|
||||
ERROR("failed to create the configured network");
|
||||
goto out_delete_net;
|
||||
@ -642,8 +709,8 @@ int lxc_spawn(struct lxc_handler *handler)
|
||||
return 0;
|
||||
|
||||
out_delete_net:
|
||||
if (clone_flags & CLONE_NEWNET)
|
||||
lxc_delete_network(&handler->conf->network);
|
||||
if (handler->clone_flags & CLONE_NEWNET)
|
||||
lxc_delete_network(handler);
|
||||
out_abort:
|
||||
lxc_abort(name, handler);
|
||||
lxc_sync_fini(handler);
|
||||
@ -675,7 +742,7 @@ int __lxc_start(const char *name, struct lxc_conf *conf,
|
||||
err = lxc_spawn(handler);
|
||||
if (err) {
|
||||
ERROR("failed to spawn '%s'", name);
|
||||
goto out_fini;
|
||||
goto out_fini_nonet;
|
||||
}
|
||||
|
||||
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);
|
||||
out_fini:
|
||||
lxc_delete_network(handler);
|
||||
|
||||
out_fini_nonet:
|
||||
lxc_cgroup_destroy(name);
|
||||
lxc_fini(name, handler);
|
||||
return err;
|
||||
|
@ -39,6 +39,7 @@ struct lxc_handler {
|
||||
pid_t pid;
|
||||
char *name;
|
||||
lxc_state_t state;
|
||||
int clone_flags;
|
||||
int sigfd;
|
||||
sigset_t oldmask;
|
||||
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
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
@ -31,9 +32,11 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
|
||||
#include <lxc/lxc.h>
|
||||
#include <lxc/log.h>
|
||||
#include <lxc/start.h>
|
||||
#include <lxc/cgroup.h>
|
||||
#include <lxc/monitor.h>
|
||||
#include "commands.h"
|
||||
#include "config.h"
|
||||
|
||||
@ -162,3 +165,115 @@ out:
|
||||
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 const char *lxc_state2str(lxc_state_t state);
|
||||
extern int lxc_wait(const char *lxcname, const char *states, int timeout);
|
||||
|
||||
#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