Merge git://github.com/lxc/lxc

Signed-off-by: Daniel Lezcano <dlezcano@fr.ibm.com>
This commit is contained in:
Daniel Lezcano 2012-12-13 21:47:55 +01:00
commit 2e3ae157d5
128 changed files with 9532 additions and 1382 deletions

46
.gitignore vendored
View File

@ -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

View File

@ -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.)

View File

@ -500,5 +500,3 @@ necessary. Here is a sample; alter the names:
Ty Coon, President of Vice
That's all there is to it!

View File

@ -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.

View File

@ -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
View File

@ -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

View File

@ -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
View File

@ -0,0 +1,3 @@
lxc.network.type = veth
lxc.network.link = virbr0
lxc.network.flags = up

3
config/lxc.conf.ubuntu Normal file
View File

@ -0,0 +1,3 @@
lxc.network.type = veth
lxc.network.link = lxcbr0
lxc.network.flags = up

1
config/lxc.conf.unknown Normal file
View File

@ -0,0 +1 @@
lxc.network.type = empty

View File

@ -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],[[]],[[]])

View File

@ -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
View 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:
-->

View 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">
@ -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>

View 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">

View 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">

View 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">

View 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">
@ -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>

View 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">

View 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">

View 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">

View 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">

View File

@ -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

View 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">

View File

@ -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">
]>

View 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">

View File

@ -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">

View 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">
@ -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>

View 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">

View 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">

View File

@ -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>

View 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 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>

View 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 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>

View File

@ -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
View 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

View File

@ -1 +1 @@
SUBDIRS = lxc
SUBDIRS = lxc tests python-lxc

View File

@ -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

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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));

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -27,7 +27,6 @@
#include <unistd.h>
#include <stdlib.h>
#include "log.h"
#include "start.h"

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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
View 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
View 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))

View File

@ -140,4 +140,3 @@ while read line; do
printf "%-${container_field_width}s %s\n" "${container_of_pid[pid]}" "$line"
fi
done < <(ps "$@")

View File

@ -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

View File

@ -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

View 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)

View File

@ -1,3 +1,3 @@
#!/bin/bash
#!/bin/sh
echo "lxc version: @PACKAGE_VERSION@"

View File

@ -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

View File

@ -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;

View File

@ -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));
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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

File diff suppressed because it is too large Load Diff

79
src/lxc/lxccontainer.h Normal file
View 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
View 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
View 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
View 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

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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];
}

View File

@ -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
View 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
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View 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

View 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
View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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